stormcloud-video-player 0.7.28 → 0.7.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/ui/OverlayRenderer.cjs","../../src/ui/OverlayRenderer.tsx","../../src/utils/overlays.ts"],"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","__getOwnPropNames","getOwnPropertyDescriptor","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","OverlayRenderer_exports","OverlayRenderer","module","exports","import_react","require","OVERLAY_API_BASE","timeStringToSeconds","timeStr","parts","split","length","hours","parseInt","minutes","secStr","dotIdx","indexOf","seconds","substring","msFrag","ms","padEnd","parseFloat","isFinite","num","Math","max","isOverlayActive","overlay","currentTime","visible","startSec","start_time","durationSec","duration","resolveImageUrl","imageUrl","apiBaseUrl","startsWith","URL","url","origin","import_jsx_runtime","computeVideoDimensions","video","videoWidth","videoHeight","nativeHeight","displayWidth","offsetWidth","displayHeight","offsetHeight","displayAspect","renderWidth","offsetX","offsetY","videoAspect","renderHeight","nativeWidth","ImageOverlay","image_url","jsx","src","objectFit","userSelect","TextOverlay","text","content","style","width","height","display","alignItems","justifyContent","color","fontSize","fontFamily","fontWeight","textAlign","padding","boxSizing","wordBreak","textShadow","pointerEvents","lineHeight","children","parseRSSXml","xmlText","maxItems","parser","DOMParser","doc","parseFromString","querySelector","Error","Array","querySelectorAll","map","item","title","textContent","replace","trim","description","pubDate","author","category","filter","i","slice","fetchRSSItems","rssUrl","encoded","resp","data","encodeURIComponent","fetch","ok","json","contents","status","ScrollerOverlay","cfg","scroller_config","uid","useId","useState","rssItems","setRssItems","rssLoading","setRssLoading","rssError","setRssError","rss_url","max_items","autoRefresh","auto_refresh","updateInterval","update_interval","useEffect","use_custom_text","custom_text","cancelled","then","items","catch","finally","interval","setInterval","clearInterval","sep","separator_char","segments","show_title","push","show_description","show_timestamp","Date","toLocaleDateString","show_author","show_category","join","scrollSpeed","scroll_speed","direction","font_size","font_family","font_weight","textColor","text_color","bgColor","background_color","bgOpacity","background_opacity","borderRadius","border_radius","itemSpacing","item_spacing","label","labelLine2","label_line2","labelColor","label_color","labelTextColor","label_text_color","accentColor","accent_color","showAccentLine","show_accent_line","isHorizontal","isReverse","fullText","animId","id","keyframes","jsxs","Fragment","flexDirection","overflow","backgroundColor","hexToRgb","background","flexShrink","flex","minHeight","minWidth","gap","letterSpacing","textTransform","whiteSpace"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBACIA,OAAWC,KAAAA,EAAOC,MAAM;gBACxBC,QAAYF,IAAAA,GAAOG,cAAc;YACjCC,GAAAA,KAAAA,CAAAA,QAAmBJ,IACnBK,GAD0BC,eACNN,OAAOO,EADuB,iBACJ;QAC9CC,GAAAA,YAAeR,KAAAA,EAAOS,GAAAA,WAAc;QACpCC,OAAAA;mBAAAA,EAAeV,OAAOW,KAAAA,IAAS,CAACC,cAAc;;IAClD,EAAIC,CAAAA;QAAAA;QAAAA,CAAW;QAAA,MAACC,QAAQC;QAAAA;QAAAA,gBAAAA,0BAAAA,IAAAA,eAAAA;QAAAA,gBAAAA,0BAAAA,IAAAA,WAAAA;KAAAA;MACtB,EAAK,IAAIC,WAAAA,gBAAAA,0BAAAA,IAAAA,CAAQD,IACfb,SAAAA,CAAUY,wCAAAA,KAAQE,MAAM;UAAEC,KAAKF,GAAG,CAACC,KAAK;qDAAEE,EAAAA,SAAY,MAAA,MAAA,gBAAA,0BAAA,IAAA,WAAA,GAAA;QAAK,WAAA;YAAA,IAAA,WAAA;SAAA;IAC/D,OAAA,IAAA,SAAA,MAAA,GAAA,GAAA;QACIC,WAAAA,GAAc,MAAA,GAAA,CAAA,SAAA,UAACC,IAAIC,MAAMC,QAAQC;YACnC,EAAIF,EAAAA,IAAQ,CAAA,GAAA,EAAA,EAAOA,qCAAP,SAAOA,KAAG,MAAM,YAAY,OAAOA,SAAS,YAAY;6DAC7D,EAAA,UAAA,MAAA,SAAA,KAAA,EAAA,GAAA,EAAA,MAAA,IAAA,CAAA,KAAA,KAAA,CAAA;;;wBAAA,IAAIG,MAAJ;0BACH,IAAI,CAACd,IAAAA,KAAAA,IAAae,CAAAA,GAAI,CAACL,GAAAA,CAAII,CAAAA,OAAQA,QAAQF,GAAAA,KACzCpB,UAAUkB,IAAII,KAAK;qCAAEP,KAAK,SAALA,MAAWI,IAAI,CAACG,IAAI;;oEAAEN,MAAAA,KAAAA,EAAY,CAAEK,CAAAA,CAAAA,MAAOnB,EAAAA,MAAAA,IAAAA,CAAAA,GAAiBiB,EAAeE,EAAKL,KAAdM,IAAG,CAAA,AAAqB,IAAfD,EAAAA;gEAAgB,YAAA,KAAA,KAAA,QAAA,EAAA,MAAA,IAAA,CAAA,IAAA,OAAA,KAAA,QAAA,EAAA;;gBAFpH,QAAK,YAAWlB,kBAAkBgB,0BAA7B,SAAA,6BAAA,QAAA,yBAAA;;gBAAA,GAAA;YAAA;SAAA;cAAA,CAAA,UAAA;;;;;;;;iBAAA,GAAA,OAAA;SAAA,kBAAA;sBAAA;;;;;;;oBAAA,WAAA,gBAAA,0BAAA,IAAA,YAAA,yCAAA;4EAAA,QAAA,yCAAA;;;;MAGP,EAAA,YAAA,CAAA,gBAAA,0BAAA,IAAA,UAAA,KAAA;MACA,EAAA,GAAOD,OAAAA,CAAAA,gBAAAA,0BAAAA,IAAAA,gBAAAA,KAAAA;IACT,IAAA,YAAA,CAAA,gBAAA,0BAAA,IAAA,kBAAA,MAAA,KAAA,IAAA,IAAA,kBAAA,GAAA,MAAA;IACA,EAAIM,EAAAA,MAAU,kBAAA,gBAAA,0BAAA,IAAA,GAACC,KAAKC,KAAAA,yCAAAA,GAAYd;WAAYA,SAASa,WAAAA,gBAAAA,0BAAAA,IAAO,OAAO5B,KAAAA,yCAASS,aAAamB,QAAQ,CAAC,GAAGR,YACnG,sEAAsE;MACtE,EAAA,iBAAA,gBAAA,0BAAA,IAAA,KAAA,yCAAA,uCAAiE;MACjE,EAAA,sBAAA,gBAAA,0BAAA,IAAA,WAAA,yCAAA,iCAAsE;MACtE,EAAA,uBAAA,gBAAA,0BAAA,IAAA,WAAA,2CAAA,gCAAqE;MACrES,EAAAA,UAAc,CAACD,gBAAAA,CAAO,CAACA,cAARA,0BAAAA,IAAQA,CAAIE,UAAU,GAAG3B,EAAAA,2CAAAA,IAAUY,QAAQ,WAAW;QAAEgB,OAAOH,iBAAAA,gBAAAA,0BAAAA,IAAAA,YAAAA,2CAAAA;QAAKT,YAAY,KAAA,CAAA,gBAAA,0BAAA,IAAA,gBAAA,MAAA;MAAK,EAAA,CAAKJ,QACzGa,MAAAA,cAAAA,UAAAA,cAAAA;;IAEF,EAAII,EAAAA,WAAe,SAAA,IAAA,CAAA,KAACJ,OAAD,GAACA,EAAAA;WAAQR,WAAAA,CAAYjB,IAAAA,GAAAA,CAAAA,EAAU,CAAC,GAAG,MAAA,MAAA,EAAc,CAAA,IAAA;QAAE4B,OAAO,EAAA,aAAA,OAAA,QAAA,EAAA,EAAA,KAAA,OAAA;MAAK,EAAIH,YAAAA,eAAAA,qBAAAA,QAAAA,wMAEtF,cC7BAK,OD6BA,IAA6B,IAAA,gBC7B7B,OAAAA,YAAAA,QAAA,CAAA,mEAAA,0EAAA;MAAAC,KAAA,AAAAA,YAAA,CAAA,GAAA,CAAA,GAAA,CAAAA,kBAAAA,IAAAA,EAAAA,mBAAAA,QAAAA,EAAAA;QAAAA,UAAAA;uBAAAA,EAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,GAAAA,EAAAA,SAAAA;gBAAAA,UAAAA;YAAAA;yDAAA,OAAAC,CAAAC,OAAA,GAAAJ,aAAAC;gBAAAI,OAAAA,IAAgFV,QAAAW,QAAA,UAAA;oBDqChF,OAAA,OAAwB;oBErClBC,QAAAA,KAAmB;oBAiFTC,SAAAA,UAAoBC,OAAA;oBAC7BA,QAAS,OAAO;oBAEfC,MAAQD,IAAAA,IAAQE,KAAA,CAAM;oBAExBD,IAAME,MAAA,IAAU,GAAG,YAAA,IAAA,GAAA,OAAA,cAAA,QAAA,KAAA;sBACEF,SACEA,MAAAA,GACVA,KAAAA,OAAAA,SAAAA,UAAAA,MAAAA,OAAAA,WAAAA;oBAFf,YAAA,EAAMG,QAAQC,UAASJ,UAAAA,KAAA,CAAM,EAAC,cAAPA,qBAAAA,UAAY,KAAK,OAAO;oBAC/C,EAAMK,QAAAA,CAAUD,SAAAA,UAASJ,WAAAA,KAAA,CAAM,EAAC,cAAPA,sBAAAA,WAAY,KAAK,OAAO;oBACjD,YAAA,EAAMM,UAASN,WAAAA,KAAA,CAAM,EAAC,cAAPA,sBAAAA,WAAY;oBAC3B,EAAMO,KAAAA,IAASD,OAAOE,OAAA,CAAQ;oBAC9B,EAAMC,UACJL,GAAAA,MAASG,UAAU,IAAID,OAAOI,SAAA,CAAU,GAAGH,UAAUD,QAAQ,OAAO;oBACtE,EAAMK,SAASJ,CAAAA,SAAU,IAAID,OAAOI,SAAA,CAAUH,SAAS,KAAK;gBAC5D,IAAMK,KAAKD,SAASP,SAASO,OAAOE,MAAA,CAAO,GAAG,KAAKH,SAAA,CAAU,GAAG,IAAI,OAAO,IAAI;gBAC/E,OAAOP,GAAAA,KAAQ,OAAOE,UAAU,KAAKI,UAAUG,KAAK;oBACtD,kBAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;wBAAA,OAAA;4BAAA,QAAA;4BAAA,YAAA;4BAAA,YAAA;4BAAA,OAAA;wBAAA;oBAAA;oBAEIZ,IAAME,MAAA,GAAA,EAAW,CAAA,CAAA,CAAG,EAAA,mBAAA,IAAA,EAAA,OAAA;wBAAA,OAAA;4BAAA,SAAA;4BAAA,MAAA;4BAAA,UAAA;4BAAA,WAAA;wBAAA;wBAAA,UAAA;4BACGF,SAAAA,AACVA,CAAAA,YAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,IAAAA,EADTK,OACAC,EADUF,OACDJ,GADUA,QACVA,GADUA,EACV,CAAM,EADI,AACH,CADS,EAAC,WACjBA,GADUA,mBACVA,GADUA,QACE,GADU,KAAK,OAAO;gCAE3CO,MAASD,CAAAA,OAAOE,OAAA,CAAQ;oCACxBC,KACJL,OAAAA,EAASG,WAAU,IAAID,QAAOI,SAAA,CAAU,GAAGH,WAAUD,SAAQ,OAAO;oCAChEK,IAASJ,GAAAA,QAAU,IAAID,QAAOI,SAAA,CAAUH,UAAS,KAAK;oCACjDI,SAAAA,CAASP,SAASO,QAAOE,MAAA,CAAO,GAAG,KAAKH,SAAA,CAAU,GAAG,IAAI,OAAO,IAAI;oCACxEL,QAAU,CAAA,IAAKI,WAAUG,MAAK;oCACvC,eAAA;oCAEYE,OAAWf,KAAAA;oCAChBgB,EAASC,OAAOC,KAAKC,EAAAA,CAAA,CAAI,GAAGF,OAAO;oCAC5C,YAAA;oCAEgBG,OACdC,GAAAA,IAAA,EACAC,WAAA;oCAEaC,MAAA,EAAS,GAAA,IAAO;oCACvBC,CAAWzB,IAAAA,gBAAoBsB,QAAQI,UAAU;gCACjDC,MAAc3B,oBAAoBsB,QAAQM,QAAQ;gCACpDD,OAAe,GAAG,OAAO;oCACtBJ,QAAeE,KAAAA,GAAAA,CAAAA,GAAYF,cAAcE,KAAAA,GAAAA,EAClD,CAD6DE,OAmB7CE,KACdC,QAAA;wCACAC,OAAAA,yDAAqBhC;4CAEN,EAAO,UAAA;4CACTiC,GAAA,CAAW,MAAA,QAAcF,SAASE,UAAA,CAAW,aAAa;4CAC9DF,eAAAA;4CACT,YAAA;4CACaE,GAAA,CAAW,MAAM,KAAA;4CACxB,YAAA;wCACU,IAAIC,IAAIF;wCACGD,OAAbI,GAAAA,CAAIC,MAAM,EAAW,OAARL;oCACzB,KAAQ;oCAER,cAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EACF,QACwBA,CAAdC,YAAU,KAAY,OAARD;wCAC1B,OAAA;4CFtEA,KAA6B,OAAA;4CCfzBM,CAAAtC,QAAA,CAAA;4CA/CKuC,QACPC,KAAA,EAAA;4CAEoBA,IAAMC,QAAAA,EAAA;4CACLD,KAAME,IAAAA,OAAA;4CACP,CAACC,WAAAA,GAAc,OAAO;wCAEpCC,CAAeJ,MAAMK,WAAA;wCACrBC,EAAgBN,MAAMO,EAAAA,UAAA;oCACvBH,KAAgB,CAACE,eAAe,OAAO;iCAGtCE,OAAgBJ,eAAeE;4BAEjCG;4BAEAC,SAAAA,aAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,GAAAA,EAAAA,OAAAA;gCAAAA,OAAAA;oCAAAA,OAAAA;oCAAAA,YAAAA;oCAAAA,YAAAA;gCAAAA;4BAAAA;4BACAC,aAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,GAAAA,EAEAC,OACFH,CADgBD,OACFJ,QADiB;gCAE/BS,OAAeT,eAAeQ;oCACpB,MAAA;oCACCN,CAAAA,SAAAA,OAAgBO,YAAA,IAAgB;oCACtC,UAAA;oCACLA,KAAeP,IAAAA;oCACfG,IAAcH,QAAAA,QAAgBM;gCAC9BF,EAAA,AAAWN,CAAAA,eAAeK,WAAA,IAAe;gCACzCE,EAAU,QAAA,eAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EACZ,OAEO;oCACLG,CAAAA,MAAAA;wCACAX,SAAAA;wCACcM,YAAAA;wCACdH,CAAeO,UAAAA,GAAAA,OAAAA,QAAAA,KAAAA,OAAAA,aAAAA;wCACfH,YAAAA;oCACAC;oCACQF,UAAcK;wCAAAA;wCAAAA;qCAAAA,CAAAA,GAAAA,CAAAA,SAAAA;+CAAAA,aAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,GAAAA,EAAAA,QAAAA;4CAAAA,OAAAA;gDAAAA,cAAAA,GAAAA,OAAAA,aAAAA;4CAAAA;4CAAAA,UAAAA,SAAAA,GAAAA,CAAAA,SAAAA,KAAAA;uDAAAA,aAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,IAAAA,EAAAA,aAAAA,OAAAA,CAAAA,QAAAA,EAAAA;oDAAAA,UAAAA;wDACdD,IAAAA,KAAeV,aAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,GAAAA,EAAAA,QAAAA;4DAAAA,OAAAA;gEAAAA,SAAAA;gEAAAA,QAAAA;4DAAAA;4DAAAA,UAAAA;wDAAAA;wDACzB,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,QAAA;4DAAA,OAAA;gEAAA,YAAA;4DAAA;4DAAA,UAAA;wDAAA;qDACF;gDAAA,GAAA;;wCAAA,GAAA;;gCAESY,IAAa,CAAA,AAAE/B,IAAAA,AAAQ,SAARA,GAAAA,CAAAA,GAAAA,mBAAAA,GAAAA,EACVO,OACF,GAAO,EADWP,QAAQgC,SAAA,IAAa;oCAG/C,IAAA,GAAA,CAAA,GAAAlB,mBAAAmB,GAAA,EAAC,OAAA;wCACCC,SAAAA;wCACa/E,GAAA,YAAA;wCACF,YAAA;wCACJ,WAAA,GAAA,OAAA,QAAA,KAAA,OAAA,aAAA;wCACE,YAAA;oCACC;oCACRgF,GAAW,OAAA;wCAAA;wCAAA;qCAAA,CAAA,GAAA,CACF,SAAA;+CAAA,SAAA,GAAA,CAAA,SAAA,KAAA;mDAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;gDAAA,OAAA;oDAAA,eAAA,GAAA,OAAA,cAAA,GAAA;gDAAA;gDAAA,UAAA;4CAAA,GAAA,GAAA,OAAA,MAAA,KAAA,OAAA;;;gCAETC,MAAY;4BACd;yBAKGC;oBAAAA,SAAY,KAAU;iBAARrC,SAAF,MAAEA;YACrB,EAAMsC,OAAOtC,QAAQuC,OAAA,IAAW;;QAG5BC,OAAO;YACLC,OAAO;YACPC,QAAQ,CAAA,OAAA;cACRC,IAAAA,KAAS,EAAA;cACTC,YAAY;gBACZC,IAAAA,KAAAA,CAAAA,MAAgB;uBAChBC,OAAO;gBACPC,UAAU;cACVC,YAAY;YACZC,YAAY;YACZC,WAAW,EAAA,KAAA;QAAA,UAAA,MAAA,SAAA,OAAA,MAAA;YACXC,EAAAA,OAAS,KAAA,QAAA,OAAA;cACTC,OAAAA,IAAW;YACXC,KAAAA,GAAAA,CAAAA,EAAW,CAAA,KAAA,CAAA,GAAA;WACC,GAAZC,UAAAA,EAAY,CAAA,CAAA,GAAA,mBAAA,IAAA,EAAA,OAAA;QAAA,OAAA;YAAA,OAAA;YAAA,QAAA;YAAA,cAAA,KAAA,GAAA,CAAA,GAAA,KAAA,CAAA,GAAA;YAAA,SAAA;YAAA,eAAA;YAAA,YAAA,IAAA,eAAA;YAAA,OAAA,IAAA,SAAA;YAAA,YAAA;YAAA,UAAA;YAAA,eAAA;YAAA,YAAA;YAAA,UAAA,GAAA,OAAA,GAAA;QAAA;QAAA,UAAA;oBACZC,KAAAA,GAAAA,CAAAA,GAAAA,GAAe,gBAAA,IAAA,EAAA,OAAA;gBAAA,OAAA;oBAAA,MAAA;oBAAA,SAAA;oBAAA,YAAA;oBAAA,SAAA,KAAA,OAAA,IAAA,KAAA;oBAAA,KAAA,IAAA;gBAAA;gBAAA,UAAA;0BACfnB,OAAAA,GAAAA,CAAAA,CAAY,EAAA,mBAAA,IAAA,EAAA,OAAA;wBAAA,OAAA;4BAAA,MAAA;4BAAA,WAAA;wBAAA;wBAAA,UAAA;gCACZoB,SAAAA,GAAY,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;gCAAA,OAAA;oCAAA,UAAA;oCAAA,YAAA;gCAAA;gCAAA,UAAA,IAAA,QAAA;4BAAA;4BACd,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;gCAAA,OAAA;oCAAA,UAAA;oCAAA,YAAA;oCAAA,YAAA;gCAAA;gCAAA,UAAA,IAAA,SAAA;4BAAA;;oBAECC,UAAAnB;oBAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,IAAA,EAAA,OAAA;wBAAA,OAAA;4BAAA,UAAA;4BAAA,WAAA;4BAAA,SAAA;4BAAA,SAAA,KAAA,OAAA,IAAA,KAAA;wBAAA;wBAAA,UAAA;4BAGP,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;gCAAA,UAAA,IAAA,MAAA;4BAAA;4BAUA,CAASoB,YAAYC,GAAAA,CAAAA,GAAA,EAAiBC,QAAA,SAAA,GAAA,EAAA,OAAA;gCAAA,UAAA,IAAA,KAAA;4BAAA;yBACpC;oBAAMC,SAAS,IAAIC;oBACnB,EAAMC,MAAMF,KAAAA,EAAOG,CAAAA,CAAAA,GAAAA,UAAA,CAAgBL,QAAAA,CAAS,GAAA,EAAA,OAAA;wBAAA,OAAA;4BAAA,MAAA;4BAAA,WAAA;wBAAA;wBAAA,UAAA;4BACxCI,IAAIE,SAAAA,IAAA,CAAc,EAAA,cAAgB,KAAA,CAAM,EAAA,EAAIC,MAAM,CAAA;gCAAA,OAAA;oCAAA,UAAA;oCAAA,YAAA;gCAAA;gCAAA,UAAA,IAAA,QAAA;4BAAA;4BACtD,GAAOC,MAAM3G,IAAA,CAAKuG,EAAAA,CAAAA,AAAIK,GAAAA,cAAA,CAAiB,IAAA,GAAA,EACpCC,GAAA,CAAI,GAAA;gCAAA,IAACC,GAAAA;oCAAAA,UAAAA;oCAAAA,YAAAA;oCAAAA,YAAAA;gCAAAA;gCAAAA,UAAAA,IAAAA,SAAAA;4BAAAA;;wBACIA,qBACMA,sBACLA,sBACDA,sBACEA;;qBALI;oBACdC,OAAA,AAAQD,CAAAA,EAAAA,EAAAA,IAAAA,eAAAA,CAAAA,IAAAA,AAAmB,CAAdL,YAAAA,CAAA,CAAc,CAAA,CAAA,GAAA,iBAAnBK,EAAAA,IAAAA,EAAAA,OAAAA;gBAAAA,OAAAA;oBAAAA,UAAAA,MAAAA;oBAAAA,WAAAA,MAA6BE;oBAAAA,OAAA,EAAA,GAAe;oBAAA,EAAIC,OAAA,AAAQ,GAAY,OAAZ,IAAA,KAAA,EAAY,KAAI,OAAJ,CAAIC,GAAAA,CAAA,IAAA;oBAAA,WAAA,aAAA,OAAA,IAAA,WAAA,EAAA;oBAAA,SAAA;oBAAA,YAAA;oBAAA,gBAAA;oBAAA,KAAA,IAAA;oBAAA,UAAA;gBAAA;gBAAA,UAAA;0BAChFC,aAAcL,CAAAA,EAAAA,CAAAA,aAAAA,GAAAA,CAAAA,GAAAA,EAAAA,KAAKL,YAAAA,CAAA,EAAc,EAAA,OAAA;wBAAA,KAAA,IAAA,OAAnBK,QAAAA;wBAAAA,KAAAA;wBAAAA,OAAAA;4BAAAA,QAAAA,GAAAA,OAAAA,IAAAA,KAAAA;4BAAAA,KAAmCE,MAAAA,KAAA,KAAe;4BAAA,CAAA,EAAIC,OAAA,CAAQ,CAAA;wBAAA;oBAAA,OAAY,IAAIC,IAAA;0BAC5FE,SAASN,EAAAA,EAAAA,aAAAA,GAAAA,CAAAA,GAAAA,CAAAA,KAAKL,aAAA,CAAc,EAAA,EAAA,QAAA;wBAAA,OAAA;4BAAA,CAAnBK,SAAAA;4BAAAA,cAAAA,UAAAA;4BAAAA,YAAAA,OAA+BE;wBAAAA;wBAAAA,MAAA,IAAA,CAAe,GAAA,WAAA;oBAAA;;kBACvDK,QAAQP,EAAAA,uBAAAA,KAAKL,aAAA,CAAc,qCAAnBK,2CAAAA,qBAA4CE,WAAA,KAAe;;YACnEM,UAAUR,EAAAA,uBAAAA,KAAKL,aAAA,CAAc,yBAAnBK,2CAAAA,qBAAgCE,WAAA,KAAe;QAC3D;OACCO,EAAAA,IAAA,CAAO,SAACC,IAAAA,KAAAA;QAAAA,UAAAA,MAAAA,SAAAA,OAAAA,MAAAA;eAAMA,EAAET,KAAK,IAAA,QAAA,OAAA;SACrBU,KAAA,CAAM,GAAGrB,GAAAA;IACd,IAAA,IAAA,KAAA,GAAA,CAAA,GAAA,KAAA,CAAA,GAAA;IAEA,OAAesB,AAAcC,aAAdD,CAAcC,EAAAA,CAAAA,GAAA,EAAgBvB,QAAA,SAAA,IAAA,EAAA,OAAA;QAAA,OAAA;YAAA,OAAA;YAAA,QAAA;YAAA,cAAA,KAAA,GAAA,CAAA,GAAA,KAAA,CAAA,GAAA;YAAA,SAAA;YAAA,eAAA;YAAA,gBAAA;YAAA,YAAA,IAAA,eAAA;YAAA,OAAA,IAAA,SAAA;YAAA,YAAA;YAAA,UAAA;YAAA,eAAA;YAAA,YAAA;YAAA,UAAA,GAAA,OAAA,GAAA;QAAA;QAAA,UAAA;;;;;;;;oBACrCwB,KAAAA,IAGEC,GAAAA,IAEEC,cAMFD,CAAAA,IAAAA,EAAAA,CAEE/C,MAAAA;gBAAAA,OAAAA,CAKJ+C;oBAAAA,KAEA/C,CAAAA;oBAAAA,SAAAA;oBAAAA,eAAAA;oBAAAA,gBAAAA;oBAAAA,SAAAA,GAAAA,OAAAA,IAAAA,KAAAA,OAAAA,OAAAA,IAAAA,KAAAA;gBAAAA;gBAAAA,UAAAA;;;;;;;;;;;;;;;;;;;;4BApBA8C,IAAAA,IAAAA,EAAUG,aAAAA,KAAAA,AAAmBJ,CAAAA,YAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,IAAAA,EAAAA,OAAAA;gBAAAA,OAAAA;oBAAAA,UAAAA;oBAAAA,SAAAA;oBAAAA,SAAAA,KAAAA,OAAAA,IAAAA,KAAAA,OAAAA,OAAAA,IAAAA,KAAAA;oBAAAA,SAAAA;oBAAAA,YAAAA;oBAAAA,KAAAA,IAAAA;oBAAAA,UAAAA;gBAAAA;gBAAAA,UAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAGpB,EAAA,GAAA,CAAA,IAAA,KAAA,GAAA,CAAA,KAAA,CAAA,EAAA,KAAA,CAAA,IAAA;;wBAAMK,KAAAA,CAAM,GAAA,mCAA6C,OAAPJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAAzDC,CAAAA,GAAAA,IAAO,eAAA,GAAA,EAAA,OAAA;gBAAA,OAAA;oBAAA,UAAA,GAAA,OAAA,IAAA,KAAA;oBAAA,YAAA;oBAAA,WAAA;oBAAA,OAAA,IAAA,WAAA;oBAAA,UAAA;oBAAA,cAAA;oBAAA,YAAA;oBAAA,OAAA;gBAAA;gBAAA,UAAA,IAAA,OAAA;YAAA;+BACTA,EAAAA,MAAKI,EAAA,EAALJ,CAAAA,GAAAA,CAAAA,GAAAA,mBAAAA,GAAAA,EAAAA,OAAAA;gBAAAA,OAAAA;oBAAAA,UAAAA,GAAAA,OAAAA,IAAAA,MAAAA;oBAAAA,SAAAA;oBAAAA,WAAAA;oBAAAA,UAAAA;oBAAAA,cAAAA;oBAAAA,YAAAA;oBAAAA,OAAAA;gBAAAA;gBAAAA,UAAAA,IAAAA,WAAAA;YAAAA;;;;;;oBACW,MAAA,QAAA,OAAA;;wBAAMA,KAAAA,CAAKK,GAAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;4BAAlBJ,CAAAA,GAAAA,GAAO,gBAAA,IAAA,EAAA,OAAA;gBAAA,OAAA;oBAAA,MAAA;oBAAA,SAAA;oBAAA,eAAA;oBAAA,gBAAA;oBAAA,SAAA,GAAA,OAAA,IAAA,KAAA,OAAA,OAAA,IAAA,GAAA;oBAAA,UAAA;gBAAA;gBAAA,UAAA;kCACb,EAAA,CAAA,CAAIA,EAAAA,GAAKK,QAAA,EAAU,MAAA,GAAA,EAAA,OAAA;wBAAA,OAAA;4BAAA,UAAA;4BAAA,YAAA;4BAAA,eAAA;4BAAA,eAAA;4BAAA,OAAA,IAAA,WAAA;wBAAA;wBAAA,UAAA;oBAAA;;;;;;;;;;;;;sCAAOjC,EAAAA,UAAY4B,KAAKK,IAAAA,GAAAA,CAAA,CAAA,CAAU/B,MAAAA;wBAAAA,OAAAA;4BAAAA,UAAAA;4BAAAA,SAAAA;4BAAAA,UAAAA;4BAAAA,cAAAA;4BAAAA,YAAAA;wBAAAA;wBAAAA,UAAAA,IAAAA,QAAAA;oBAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAK1C;;WAAM4B,eAAAA,CAAAA,CAAAA,GAAAA,CAAM,kBAAA,IAAA,EAAA,KAAoC,EAAA;QAAA,GAAPJ,IAAAA;YAAAA,OAAAA;YAAAA,QAAAA;YAAAA,cAAAA,KAAAA,GAAAA,CAAAA,GAAAA,KAAAA,CAAAA,GAAAA;YAAAA,SAAAA;YAAAA,eAAAA;YAAAA,SAAAA,IAAAA;YAAAA,YAAAA,IAAAA,eAAAA;YAAAA,OAAAA,IAAAA,SAAAA;YAAAA,YAAAA;YAAAA,WAAAA;YAAAA,eAAAA;YAAAA,YAAAA;YAAAA,UAAAA,GAAAA,OAAAA,GAAAA;QAAAA;QAAAA,UAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0CAAhDC,EAAAA,CAAAA,GAAAA,GAAO,gBAAA,GAAA,EAAA,QAAA;gCAAA,OAAA;oCAAA,UAAA;oCAAA,cAAA;oCAAA,YAAA;oCAAA,MAAA;gCAAA;gCAAA,UAAA,IAAA,KAAA;4BAAA;8CACTA,EAAAA,MAAKI,EAAA,EAALJ,SAAAA,GAAAA,EAAAA,QAAAA;gCAAAA,OAAAA;oCAAAA,YAAAA;oCAAAA,YAAAA,IAAAA;oCAAAA,YAAAA;oCAAAA,OAAAA,IAAAA,WAAAA;gCAAAA;gCAAAA,UAAAA,IAAAA,IAAAA;4BAAAA;;;;;;;;;;;;;;;;oBACW;;;wBAAMA,EAAAA,KAAK/C,GAAAA,CAAA,MAAA;;;;;;;oBAAlBA,QAAO,GAAA,CAAA,IAAA,OAAA,CAAA,IAAA,cAAA,MAAA;oBACb,IAAIA,GAAAA,IAAM,CAAA,aAAA,aAAA,IAAA,OAAA,KAAA,WAAA,WAAA;;WAAOoB,eAAAA,CAAAA,CAAAA,GAAAA,OAAYpB,OAAMsB,KAAAA,IAAAA,EAAAA,OAAAA;QAAAA,OAAAA;YAAAA,OAAAA;YAAAA,QAAAA;YAAAA,cAAAA,KAAAA,GAAAA,CAAAA,GAAAA,KAAAA,CAAAA,GAAAA;YAAAA,SAAAA;YAAAA,YAAAA;YAAAA,YAAAA,IAAAA,eAAAA;YAAAA,OAAAA,IAAAA,SAAAA;YAAAA,YAAAA;YAAAA,UAAAA;YAAAA,eAAAA;YAAAA,YAAAA;YAAAA,UAAAA,GAAAA,OAAAA,GAAAA;QAAAA;QAAAA,UAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAI1B,EAAA;;wBAAM4B,MAAM,yCAA+C,OAANL;;;;;oBAA5DE,SAAAA,gBAAAA,0BAAAA,IAAAA,CAAO,SAAA,uCAAA;QACC,yBAAA,CAAA,GAAM,IAAInB,MAAM,GAAA,QAAA,UAAgC,OAAXmB,KAAKO,MAAM;yBACjD,GAAA,uBAAA,cAAA;YAAA,GAAA;YAAA,GAAA;YAAA,GAAA;YAAA,GAAA;YAAA,SAAA;QAAA;wBADC,UAAd,IAAI,CAACP,KAAKI,EAAA,EAAI,CAAA;0BACKJ,IAAAA,CAAK/C,CAAAA,GAAA;;;sBAAlBA,OAAO;mBAAA,aAAA,uBAAA;WAAA;;sBACb,WAAA;;;;;0BAAOoB,YAAYpB,MAAMsB;;;;;;QAC3B;YAAA,MAAA,IAAA,QAAA;YAAA,OAAA,IAAA,UAAA,CAAA;YAAA,OAAA;QAAA;;;;;;QAEA;YAAA,GAASiC,GAAAA,IAAAA,SAAgB,EAAA;YAAA,CAAU,MAAA,IAAA,UAAA,CAAA;YAAA,OAAA;QAAA;;cAAV,AAAE7F,IAAAA,IAAAA,EAAF,MAAEA,GAAAA;YAAAA,OAAAA,IAAAA,UAAAA,CAAAA;YAAAA,OAAAA;QAAAA;;MACzB,IAAM8F,CAAAA,AAAcC,KAAR/F,QAAQ+F,GAAAA,CAAAA,GAAAA,QAAA,WAAA,IAAA,EAAA,OAAA;QAAA,OAAA;YAAA,OAAA;YAAA,QAAA;YAAA,cAAA,KAAA,GAAA,CAAA,GAAA,KAAA,CAAA,GAAA;YAAA,SAAA;YAAA,eAAA;YAAA,YAAA;YAAA,gBAAA;YAAA,SAAA,IAAA;YAAA,YAAA,IAAA,eAAA;YAAA,OAAA,IAAA,SAAA;YAAA,YAAA;YAAA,WAAA;YAAA,eAAA;YAAA,YAAA;YAAA,UAAA,GAAA,OAAA,GAAA;QAAA;QAAA,UAAA;YACpB,IAAMC,MAAA,CAAA,EAAA,CAAMzH,EAAAA,CAAAA,GAAAA,OAAA0H,KAAA,IAAQxB,GAAAA,GAAAA,CAAA,CAAQ,MAAM,CAAA;gBAAA,OAAA;oBAAA,UAAA;oBAAA,YAAA;oBAAA,eAAA;oBAAA,eAAA;oBAAA,OAAA,IAAA,WAAA;oBAAA,cAAA,IAAA;gBAAA;gBAAA,UAAA,IAAA,SAAA;YAAA;YAElC,IAA4B,MAAA,OAAA,GAAA,AAAIlG,UAAJ,CAAA,EAAA,CAAIA,EAAAA,CAAAA,GAAAA,OAAA2H,QAAA,EAAoB,EAAE,GAAA,EAAA,EAA/CC,KAAAA;gBAAAA,IAAqB,GAAA;oBAAA,MAAXC,IAAAA;oBAAAA,GAAW,SAAA;oBAAA,SAAA;gBAAA;gBAAA,UAAA,IAAA,OAAA,IAAA;YAAA,KAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;gBAAA,OAAA;oBAAA,SAAA;oBAAA,KAAA,IAAA;oBAAA,YAAA;gBAAA;gBAAA,UAAA,MAAA,MAAA,CAAA,SAAA;2BAAA,EAAA,IAAA;mBAAA,GAAA,CAAA,SAAA,GAAA,GAAA;2BAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,IAAA,EAAA,aAAA,OAAA,CAAA,QAAA,EAAA;wBAAA,UAAA;4BAC5B,EAAgC,WAAA,GAAA,CAAA,GAAA,QAAA,CAAA,GAAI7H,OAAAA,IAAAA,EAAA2H,OAAAA,CAAA;gCAAA,CAAS,MAAA;oCAAA,GAAtCG,QAAAA,KAAyB;gCAAA;gCAAA,IAAbC,MAAAA,UAAa;oCACJ,aAAA,GAAA,CAAA,GAAA,MAAA,CAAA,GAAI/H,SAAAA,GAAAA,CAAA2H,CAAAA,OAAA;wCAAS,OAAA;4CAAA,GAAlCK,OAAAA,IAAqB;4CAAA,QAAXC,IAAAA;4CAAAA,KAAW,OAAA;4CAAA,cAAA,KAAA,GAAA,CAAA,GAAA,IAAA;4CAAA,SAAA,GAAA,OAAA,IAAA,KAAA,OAAA,OAAA,IAAA,KAAA;4CAAA,YAAA,GAAA,OAAA,IAAA,WAAA,EAAA;wCAAA;wCAAA,UAAA,EAAA,KAAA;oCAAA;oCAEtBrB,SAASW,CAAAA,GAAAA,GAAAA,CAAAA,GAAAA,MAAAA,aAAAA,GAAAA,EAAAA,OAAAA,CAAAA;wCAAAA,GAAKW,IAAAA;4CAAAA,CAAA,KAAW,IAAA;4CAAA,SAAA;4CAAA,WAAA,IAAA;wCAAA;wCAAA,UAAA,EAAA,KAAA;oCAAA;iCAC/B;4BAAM7C,mBAAWkC,gBAAAA,0BAAAA,IAAKY,SAAA,uCAAa;4BACnC,EAAMC,EAAAA,IAAAA,MAAAA,EAAcb,CAAAA,KAAAA,WAAAA,EAAAA,GAAAA,CAAAA,GAAAA,iBAAAA,EAAAA,GAAKc,EAAAA,OAAAA;gCAAA,MAAiB,CAAA;oCAAA,UAAA;oCAAA,YAAA;oCAAA,SAAA;gCAAA;gCAAA,UAAA;4BAAA;yBAC1C;oBAAA,EAAMC,CAAAA,EAAAA,KAAAA;;YAAAA,eAAiBf,gBAAAA,0BAAAA,IAAKgB,eAAA,yCAAmB;YAE/C,CAAA,GAAAvI,OAAAA,MAAAwI,CAAAA,IAAAA,IAAA,EAAU,KAAA,IAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;gBAAA,OAAA;oBAAA,UAAA;oBAAA,SAAA;oBAAA,WAAA,IAAA;oBAAA,WAAA;gBAAA;gBAAA,UAAA,IAAA,OAAA;YAAA;;QACR,IAAI,CAAC5B,UAAWW,CAAAA,gBAAAA,0BAAAA,IAAKkB,eAAA,MAAmBlB,gBAAAA,0BAAAA,IAAKmB,WAAA,GAAc;YACzDX,cAAc;YACd,UAAA,KAAA;QAAA,UAAA,MAAA,SAAA,OAAA,MAAA;QACF,IAAA,KAAA,GAAA,CAAA,GAAA,KAAA,CAAA,GAAA;UACA,CAAA,AAAgB,GAAZY,UAAAA,EAAY,CAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;QAAA,OAAA;YAAA,OAAA;YAAA,QAAA;YAAA,cAAA,KAAA,GAAA,CAAA,GAAA,KAAA,CAAA,GAAA;YAAA,YAAA;YAAA,QAAA;YAAA,SAAA;YAAA,YAAA;YAAA,gBAAA;YAAA,eAAA;YAAA,YAAA;QAAA;QAAA,UAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,OAAA;YAAA,OAAA;gBAAA,UAAA,GAAA,OAAA,GAAA;gBAAA,YAAA;gBAAA,OAAA;gBAAA,eAAA;YAAA;YAAA,UAAA,QAAA,IAAA;QAAA;IAAA;QAChBZ,cAAc;QACdE,CAAAA,SAAAA,EAAY,CAAA;UACZtB,MAAAA,CAAAA,IAAAA,GAAcC,OAAAA,CAAQvB,MAAAA,IACnBuD,GAAAA,CAAA,CAAK,SAACC;YAAY,IAAI,CAACF,GAAAA,KAAAA,CAAAA,EAAW;gBAAEd,OAAAA,KAAYgB,CAAAA,MAAAA,KAAAA,IAAAA,MAAAA,OAAAA,CAAAA,MAAAA,UAAAA,OAAAA;cAAoB,WAAZZ,GAAAA,KAAAA,KAAY,KAAA,OAAA,OAAA,IAAA,KAAA,KAAA,OAAA,MAAA;YAAQ;QAAE,GAC9Ea,KAAA,CAAM,MAAA;YAAQ,IAAI,CAACH,KAAAA,MAAWV,YAAY;+BAAO,GACjDc,OAAA,CAAQ,uBAAQ,IAAI,CAACJ,WAAWZ,UAAsB,IAAR,8BACjD,OAAO;QAA0B,EAAA,sBAAA,CAAA,GAAA,aAAA,QAAA,EAAA,WAAA,OAAA,SAAA,UAAA;MACnC,EAAG,CAAA,QAAA,CAAA,GAAA,aAAA,MAAA,EAAA;QAACnB,EAAAA,uBAAAA,CAAAA,GAAAA,aAAAA,QAAAA,EAAAA,aAAAA,GAAAA,IAAAA,YAAAA,UAAAA,UAAAA,aAAAA;QAAQvB,eAAAA,CAAAA,GAAAA,aAAAA,MAAAA,EAAAA,aAAAA,GAAAA,IAAAA;QAAUkC,aAAAA,CAAAA,EAAAA,CAAAA,aAAAA,WAAAA,CAAAA,CAAAA,GAAKkB,eAAA;YAAiBlB,QAAAA,MAAAA,GAAAA,OAAAA,gBAAAA,IAAKmB,WAAW;SAAC,GAAA,OAAA;YAE7D,EAAA1I,EAAAA,SAAAwI,EAAAA,OAAA,EAAU,cAAA;cACR,IAAI,CAAC5B,CAAAA,SAAAA,QAAU,CAACwB,eAAgBb,CAAAA,gBAAAA,0BAAAA,IAAKkB,eAAA,MAAmBlB,gBAAAA,0BAAAA,IAAKmB,WAAA,GAAc;gBAC3E,IAAMM,CAAAA,UAAWC,EAAAA,QAAAA,EAAY,GAAA,WAAA,KAAA,SAAA,WAAA,IAAA,KAAA,YAAA,KAAA,SAAA,YAAA,IAAA,KAAA,YAAA,KAAA,SAAA,YAAA,IAAA,KAAA,aAAA,KAAA,SAAA,aAAA,IAAA,KAAA,OAAA,KAAA,SAAA,OAAA,IAAA,KAAA,OAAA,KAAA,SAAA,OAAA,EAAA;sBAC3BtC,KAAAA,SAAcC,QAAQvB,UACnBuD,IAAA,CAAK,SAACC;wBAAYhB,YAAYgB;wBAAQZ,YAAY;kBAAQ,GAC1Da,KAAA,CAAM,YAA0D;YACrE,GAAGR,iBAAiB,KAAK;;UACzB;KAAO;qBAAMY,SAAAA,EAAAA,GAAcF;;QAC7B,GAAG,CAAA,WAAA,YAAA,YAAA;YAACpC,eAAAA;cAAQwB,EAAAA,OAAAA,OAAAA,EAAAA,qBAAAA,OAAAA,OAAAA;cAAaE,KAAAA,OAAAA,GAAAA,sBAAAA;YAAgBjD;YAAUkC,GAAAA,aAAAA,GAAAA,CAAAA,UAAAA,YAAAA,IAAKkB,eAAA;YAAiBlB,GAAAA,aAAAA,0BAAAA,IAAKmB,WAAW;YAAC,cAAA;YAE1F,EAAMS,KAAAA,UAAM5B,SAAAA,CAAAA,MAAAA,IAAAA,sBAAAA,IAAK6B,cAAA,yCAAkB;YAEnC,EAAIC,EAAAA,OAAAA,OAAAA,EAAAA,qBAAAA,OAAAA,OAAAA;QACJ,IAAI9B,CAAAA,gBAAAA,0BAAAA,IAAKkB,eAAA,MAAmBlB,gBAAAA,0BAAAA,IAAKmB,WAAA,GAAa;;UAC5CW;KAAAA,EAAW;YAAC9B,IAAImB,SAAAA,CAAAA,CAAW,EAAA,aAAA,OAAA,OAAA;eAAA,SAAA,MAAA,CAAA,SAAA;mBAAA,gBAAA,GAAA;;OAC7B;QAAA,MAAA;QAAWd,SAASrH;KAAAA,GAAA,GAAS,GAAG;cAE5B,IAAMF,GAAAA,KAAkB,EAAC,EAAA,EAAA;cACzB,IAAIkH,CAAAA,KAAAA,IAAAA,IAAAA,GAAAA,YAAAA,GAAAA,CAAAA,SAAAA;mBAAAA,EAAAA,CAAAA,CAAAA,GAAK+B,UAAA,MAAe,SAASvD,KAAKC,KAAA,EAAO3F,MAAMkJ,IAAA,CAAKxD,KAAKC,KAAK;;gBAClE,GAAA,SAAIuB,CAAAA,gBAAAA,0BAAAA,IAAKiC,gBAAA,KAAoBzD,KAAKK,WAAA,EAAa/F,MAAMkJ,IAAA,CAAKxD,KAAKK,WAAW;gBAC1E,IAAImB,CAAAA,EAAAA,IAAAA,IAAAA,MAAAA,0BAAAA,IAAKkC,cAAA,KAAkB1D,KAAKM,OAAA,EAAS;;;6BACvC,OAAI,2IAAA;wBAAJ,GAAI;gCAAEhG,CAAAA,CAAAA,IAAMkJ,IAAA,CAAK,CAAA,GAAIG,KAAK3D,KAAKM,OAAO,EAAEsD,kBAAA;8BAAuB,EAAA,CAAA,QAAA,EAAA,EAAA;qCAAQ,CAAe;4BAAA,SAAA;wBAAA;wBACxF,GAAA;0BACA,EAAIpC,CAAAA,UAAAA,KAAAA,CAAAA,EAAAA,CAAAA,QAAAA,EAAAA,aAAAA,IAAKqC,WAAA,KAAe7D,KAAKO,MAAA,EAAQjG,MAAMkJ,IAAA,CAAK,KAAgB,OAAXxD,KAAKO,MAAM;0BAChE,GAAA,CAAIiB,CAAAA,CAAAA,CAAAA,QAAAA,EAAAA,EAAAA,EAAAA,sCAAAA;4BAAAA,SAAAA,aAAAA,IAAKsC,aAAA,KAAiB9D,KAAKQ,QAAA,EAAUlG,MAAMkJ,IAAA,CAAK,IAAiB,OAAbxD,KAAKQ,QAAQ,EAAA;;wBACrE,OAAOlG,MAAMyJ,IAAA,CAAK;kBACpB;;;;;;;;;;;;;;;;;;uDACF,IAAWhC,cAAAA,qBAAAA,KAAY;oBACrBuB,IAAAA,CAAAA,MAAW,IAAA,GAAA,CAAA,OAAA,MAAA,OAAA,EAAA;0BAAC,GAAA,GAAA,CAAA,IAAA,wCAAA;4BAAA,SAAA;;wBAAe,IAAA,CAAA,aAAA,OAAA,CAAA,GAAA,CAAA,KAAA;4BAC7B,GAAWrB,CAAAA,OAAU,CAAA,WAAA;gCACnBqB,KAAW5H,MAAAA,SAAAA,CAAQuC,OAAA,GAAU;oCAACvC,IAAQuC,KAAO,KAAA,IAAA,IAAA;oCAAI,QAAA,MAAA,CAAA;oCAAC,OAAA;gCAAsB;gCAC1E,CAAWvC,QAAQuC,IAAAA,GAAA,EAAS,EAAA,CAAA,MAAA,CAAA;4BAC1BqF,GAAAA,IAAW;4BAAC5H,QAAQuC,KAAAA,EAAO,KAAA,CAAA,GAAA,CAAA,IAAA;wBAAA;oBAC7B,GAAO,IAAA,IAAA,CAAA,UAAA,GAAA,CAAA,OAAA,CAAA,MAAA,OAAA,EAAA,CACLqF,WAAWzC,SAAS;sBAAC;gBAPvB,qBAAuB;;;;;;;;;;;;;;;eAOe,GAAI,CAAA;gBAAC;;WAAY;KAAA;MACvD,EAAA,aAAA,SAAA,EAAA;QAEA,IAAMmD,WAAAA,EAAAA,QAAcxC,gBAAAA,0BAAAA,IAAKyC,YAAA,yCAAgB;YACnCC,kCAAAA,2BAAAA;;;gBAAAA,mCAAAA,iBAAAA,qBAAAA,SAAY1C;gBAClB,EAAM/C,EAAAA,CAAAA,MAAAA,OAAAA,EAAAA,EAAW+C,gBAAAA,0BAAAA,IAAK2C,SAAA,yCAAa;oBAC7BzF,IAAAA,OAAa8C,CAAAA,GAAAA,aAAAA,EAAAA,IAAAA,CAAAA,SAAAA;+BAAAA,EAAAA,EAAAA,KAAAA,GAAAA,IAAK4C,WAAA,KAAe;;oBACjCzF,IAAAA,SAAa6C,CAAAA,SAAAA,IAAAA,CAAAA,EAAAA,0BAAAA,IAAK6C,WAAA,KAAe;gBACvC,EAAMC,YAAY9C,CAAAA,gBAAAA,0BAAAA,IAAK+C,UAAA,KAAc;YACrC,IAAMC,UAAUhD,CAAAA,gBAAAA,0BAAAA,IAAKiD,gBAAA,KAAoB;YALzC,IAAMP,IAAAA,YAAY1C,eAAAA,aAAZ0C,SAAAA,IAAY1C,IAAK0C,SAAA,YAAjBA,QAAAA,qBAA8B,IAA9BA;;YAAAA;YAAAA;;;qBAAAA,6BAAAA;oBAAAA;;;oBAAAA;0BAAAA;;;;QAMN,IAAMQ,SAAAA,GAAYlD,CAAAA,EAAAA,KAAAA,GAAAA,MAAAA,0BAAAA,IAAKmD,kBAAA,MAAuB,KAAA,IAAYnD,IAAImD,kBAAA,GAAqB,MAAM;QACzF,IAAMC,MAAAA,gBAAepD,MAAAA,UAAAA,0BAAAA,IAAKqD,aAAA,yCAAiB;YAC3C,EAAMC,SAAAA,SAAAA,aAActD,gBAAAA,0BAAAA,IAAKuD,YAAA,yCAAgB;gBAEnCC,IAAAA,OAAAA,IAAQxD,IAAAA,YAAAA,0BAAAA,IAAKwD,KAAA,yCAAS;oBACtBC,kCAAAA,2BAAAA;;oBAAAA,QAAAA,YAAAA,KAAazD,wBAAbyD,SAAAA,6BAAAA,QAAAA,yBAAAA,iCAAazD,WAAAA,0BAAAA,IAAK0D,WAAA,yCAAe;wBAAjCD,IAAAA,KAAAA;wBACAE,IAAAA,QAAAA,KAAAA,EAAa3D,CAAAA,CAAAA,cAAAA,0BAAAA,IAAK4D,WAAA,2CAAe;wBACjCC,IAAAA,OAAAA,KAAAA,GAAAA,CAAAA,IAAAA,CAAiB7D,uCAAAA;4BAAAA,KAAAA,IAAAA,sBAAAA,IAAK8D,gBAAA,2CAAoB;;oBAC1CC,wBAAc/D,gBAAAA,0BAAAA,IAAKgE,YAAA,2CAAgBL;;oBAHnCF;oBAAAA;;;6BAAAA,6BAAAA;4BAAAA;;;4BAAAA;kCAAAA;;;;gBAIAQ,OAAAA,UAAiBjE,CAAAA,gBAAAA,0BAAAA,IAAKkE,gBAAA,MAAqB;YAEjD,EAAMC,eAAezB,cAAc,UAAUA,cAAc;QAC3D,IAAM0B,YAAY1B,cAAc,WAAWA,cAAc;QAEzD,IAAM2B,GAAAA;mBAAAA,EAAWvC,SAASS,IAAA,CAAK,KAAQ,OAAHX,KAAG;;MACvC,CAAA;QAAA,EAAMrH;QAAAA,OAAcR,KAAKC;KAAA,CAAI,GAAIqK,SAASrL,MAAA,GAAS,IAAKwJ;MAExD,EAAA,EAAM8B,SAAS,EAAA,SAAA,EAA2BpE,OAAdhG,QAAQqK,EAAE,EAAA,KAAO,OAAHrE;QAC1C,IAAMsE,GAAAA,SAAYL,eACd,cACIC,OADUE,QAAM,gBAG0D,OAF1EF,YACE,4EACA,0EAAwE,eAE9E,cACIA,OADUE,QAAM,gBAG0D,OAF1EF,YACE,4EACA,0EAAwE;gBAIhF;;gBADF,oBACE,CAAA,CAAA,GAAApJ,QAAAA,OAAAA,CAAAA,GAAAyJ,GAAAA,CAAA,EAAAzJ;wBAAA,OAAA;oBAAAA,aAAAA,KAAA0J,QAAA,EAAA;;;;;;;;;;;;;;;;YACE/G,UAAA;cAAA,aAAA,GAAA,CAAA,GAAA3C,mBAAAmB,GAAA,EAAC,SAAA;kBAAOwB,OAAAA,GAAA6G,CAAAA,KAAAA,GAAAA,OAAAA;WAAA,GAAA,UAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,UACR,WACE9H,EADF,GAAA,CAAA,CACS,EADT1B,mBAAAyJ,IAAA,EAAC,OAAA;sBAEG9H,CAAAA,MAAO;sBACPC,QAAQ;wBACRC,SAAS;qBACT8H,UAAAA,EAAAA,OAAAA,EAAAA,KAAe;oBACfC,CAAU,UAAVA,CAAAA,OAAAA,EAAAA;sBACcxB,SAAdA,GAAAA,WAAcA,CAAAA,EAAAA,aAAe,IAAI,GAAe,OAAZA,cAAY,QAAO,KAAA;uBACtC,QAAjByB,IAAAA,aAAiB,EAAA,OAA8B3B,OAAtB4B,SAAS9B,UAAQ,MAAc,OAATE,WAAS;wBACxDhG,GAAAA,SAAAA;wBACAD,UAAU,GAAW,OAARA,UAAQ;wBACrBE,YAAAA;sBACAH,OAAO8F;uCACPrF,QAAAA,MAAAA,CAAe,GAAA,GAAA,CAAA;gBAAA,gBAAA,SAAA,gBAAA;sBACfnB,GAAAA,CAAAA,4BAAAA,sCAAAA,SAAY,OAAA,KAAA,IAAA,KAAA,YAAA,GAAA,gBAAA,KAAA,GAAA,KAAA,MAAA;kBACd,OAAA,CAAA,4BAAA,sCAAA,gBAAA,MAAA,IAAA,KAAA,aAAA,GAAA,gBAAA,MAAA,GAAA,KAAA,MAAA;kBAGCqB,KAAAA,KAAA,GAAA,CAAA,GAAA;sBAAAsG,QAAAA,CAAAA,GAAAA,MACC,aAAA,GAAA,CAAA,GAAAjJ,mBAAAmB,GAAA,EAAC,OAAA;0BAAIO,MAAAA,CAAO,IAAA,GAAA;8BAAEE,GAAAA,KAAQ,CAAA,GAAA;8BAAGmI,KAAAA,GAAAA,CAAAA,GAAYhB,KAAAA,GAAAA,CAAAA,KAAAA,QAAAA,OAAAA,KAAAA;8BAAaiB,MAAAA,MAAY,QAAA;;uBAAGrI;gBAAAA,GAAAA,CAAO;YAAA;mBAAO,SAAA,IAAA,GAAA,CAAA,GAAA,mBAAA,IAAA,YAAA,OAIjF,aAAA,GAAA,CAAA,GAAA3B,mBAAAyJ,IAAA,EAAC,OAAA;4BAAI/H,OAAO;kCAAEG,SAAS;0CAAQoI,MAAM;8BAAGL,UAAU;8BAAUM,WAAAA,GAAAA,SAAW;6BAAE,EAAA,OAAA,QAAA;uCAEtEvH,UAAA;iCAAA6F,iBACC,aAAA,GAAA,EAAA,GAAAxI,mBAAAyJ,IAAA,EAAC,OAAA;sCACC/H,KAAAA,EAAO;0CACLqI,YAAYpB;wCACZ3G,OAAO6G;wCACPxG,SAAS;0CACTR,MAAAA,AAAS,GAAA,UAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,cAAA;wBAAA,SAAA;oBAAA;0CACT8H,KAAAA,AAAe,UAAA,GAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,aAAA;wBAAA,SAAA;oBAAA;0CACf7H,SAAAA,AAAY,GAAA,UAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,iBAAA;wBAAA,SAAA;oBAAA;0CACZC,MAAAA,AAAgB,UAAA,GAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,cAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;0CAChBiI,UAAAA,AAAY,EAAA,WAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,iBAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;0CACZG,UAAU,EAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,mBAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;0CACV/H,QAAAA,AAAW,GAAA,UAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,eAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;0CACXgI,KAAK,UAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,qBAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;sCACP,uBAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,0BAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;sCAEAzH,UAAA,OAAA,aAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,oBAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;0CAAA,aAAA,CAAA,AAAA3C,EAAA,CAAA,GAAAA,OAAAA,GAAAA,CAAAA,GAAAA,KAAAmB,GAAA,EAAC,QAAA,CAAA,GAAA,EAAA,qBAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;8CACCO,MAAAA,AAAO,CAAA,YAAA,GAAA,CAAA,GAAA,mBAAA,GAAA,EAAA,kBAAA;wBAAA,SAAA;wBAAA,MAAA;oBAAA;gDACLS,YAAY;8CACZF,GACAoI,OADU,QACK;8CAEfC,eAAe;4CACfC,YAAY;wCAGb5H,UAAA6F;oCAAA,yBAAA;oCAEFC,cACC,aAAA,GAAA,CAAA,GAAAzI,mBAAAmB,GAAA,EAAC,QAAA;2DACCO,OAAO;6CACLS,YAAY","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/ui/OverlayRenderer.tsx\nvar OverlayRenderer_exports = {};\n__export(OverlayRenderer_exports, {\n OverlayRenderer: () => OverlayRenderer\n});\nmodule.exports = __toCommonJS(OverlayRenderer_exports);\nvar import_react = __toESM(require(\"react\"), 1);\n\n// src/utils/overlays.ts\nvar OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\nfunction timeStringToSeconds(timeStr) {\n if (!timeStr) return 0;\n const parts = timeStr.split(\":\");\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1e3;\n }\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1e3;\n }\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\nfunction isOverlayActive(overlay, currentTime) {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\nfunction resolveImageUrl(imageUrl, apiBaseUrl = OVERLAY_API_BASE) {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n\n// src/ui/OverlayRenderer.tsx\nvar import_jsx_runtime = require(\"react/jsx-runtime\");\nfunction computeVideoDimensions(video) {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n let renderWidth;\n let renderHeight;\n let offsetX;\n let offsetY;\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight\n };\n}\nfunction ImageOverlay({ overlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src,\n alt: overlay.name,\n draggable: false,\n style: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\"\n }\n }\n );\n}\nfunction TextOverlay({ overlay }) {\n const text = overlay.content || \"\";\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3\n },\n children: text\n }\n );\n}\nfunction parseRSSXml(xmlText, maxItems) {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xmlText, \"text/xml\");\n if (doc.querySelector(\"parsererror\")) throw new Error(\"Invalid RSS XML\");\n return Array.from(doc.querySelectorAll(\"item\")).map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\"\n })).filter((i) => i.title).slice(0, maxItems);\n}\nasync function fetchRSSItems(rssUrl, maxItems) {\n const encoded = encodeURIComponent(rssUrl);\n try {\n const resp2 = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp2.ok) {\n const data = await resp2.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch {\n }\n try {\n const resp2 = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp2.ok) {\n const text2 = await resp2.text();\n if (text2) return parseRSSXml(text2, maxItems);\n }\n } catch {\n }\n const resp = await fetch(`https://thingproxy.freeboard.io/fetch/${rssUrl}`);\n if (!resp.ok) throw new Error(`RSS fetch failed: ${resp.status}`);\n const text = await resp.text();\n return parseRSSXml(text, maxItems);\n}\nfunction ScrollerOverlay({ overlay }) {\n const cfg = overlay.scroller_config;\n const uid = (0, import_react.useId)().replace(/:/g, \"\");\n const [rssItems, setRssItems] = (0, import_react.useState)([]);\n const [rssLoading, setRssLoading] = (0, import_react.useState)(true);\n const [rssError, setRssError] = (0, import_react.useState)(false);\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n (0, import_react.useEffect)(() => {\n if (!rssUrl || cfg?.use_custom_text && cfg?.custom_text) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n fetchRSSItems(rssUrl, maxItems).then((items) => {\n if (!cancelled) {\n setRssItems(items);\n setRssError(false);\n }\n }).catch(() => {\n if (!cancelled) setRssError(true);\n }).finally(() => {\n if (!cancelled) setRssLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n (0, import_react.useEffect)(() => {\n if (!rssUrl || !autoRefresh || cfg?.use_custom_text && cfg?.custom_text) return;\n const interval = setInterval(() => {\n fetchRSSItems(rssUrl, maxItems).then((items) => {\n setRssItems(items);\n setRssError(false);\n }).catch(() => {\n });\n }, updateInterval * 60 * 1e3);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n const sep = cfg?.separator_char ?? \"\\u25C6\";\n let segments;\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try {\n parts.push(new Date(item.pubDate).toLocaleDateString());\n } catch {\n }\n }\n if (cfg?.show_author && item.author) parts.push(`\\u2014 ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed\\u2026\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed\\u2026\"] : [\"RSS Ticker\"];\n }\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== void 0 ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, fullText.length * 9 / scrollSpeed);\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal ? `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\" : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }` : `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\" : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"style\", { children: keyframes }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : void 0,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\"\n },\n children: [\n showAccentLine && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { height: 3, background: accentColor, flexShrink: 0, width: \"100%\" } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }, children: [\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1\n },\n children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\"\n },\n children: label\n }\n ),\n labelLine2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\"\n },\n children: labelLine2\n }\n )\n ]\n }\n ),\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: 3, background: accentColor, flexShrink: 0 } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\"\n },\n children: isHorizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map((copy) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { paddingRight: `${itemSpacing}px` }, children: segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { opacity: 0.5, margin: \"0 8px\" }, children: sep }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }, children: seg })\n ] }, i)) }, copy))\n }\n ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map(\n (copy) => segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { paddingBottom: `${itemSpacing / 4}px` }, children: seg }, `${copy}-${i}`))\n )\n }\n )\n }\n )\n ] })\n ]\n }\n )\n ] });\n}\nfunction parseConfig(content) {\n if (!content) return null;\n try {\n return JSON.parse(content);\n } catch {\n return null;\n }\n}\nfunction ScoreBugOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.homeTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.homeScore })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.period }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.clock })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.awayTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.awayScore })\n ] })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction LowerThirdOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }, children: cfg.subtitle })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction QrCodeOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: qrUrl, alt: \"QR Code\", style: { width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" } }) }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.ctaText }),\n cfg.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.description })\n ] });\n}\nfunction ComingUpNextOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: Math.max(2, size.w * 0.015), flexShrink: 0, backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.6}px ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor }, children: \"Coming Up Next\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.5em\", fontWeight: 700, lineHeight: 1.2, marginTop: f * 0.2, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.title }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.6, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.subtitle }),\n cfg.scheduledTime && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 600, marginTop: f * 0.4, color: cfg.accentColor }, children: cfg.scheduledTime })\n ] }),\n cfg.thumbnailUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, width: Math.max(40, size.h * 0.75), overflow: \"hidden\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.thumbnailUrl, alt: \"thumbnail\", style: { width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" } }) })\n ] });\n}\nfunction ContextualTriggerOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const icons = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\u{1F389}\", info: \"\\u2139\\uFE0F\", warning: \"\\u{1F514}\" };\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontSize: \"2em\", flexShrink: 0 }, children: icons[cfg.iconType] || \"\\u26A1\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.message })\n ] })\n ] });\n}\nfunction OddsBettingOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventTitle }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }, children: (cfg.options || []).slice(0, 5).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }, children: opt.label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }, children: opt.odds })\n ] }, i)) }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }, children: cfg.sponsorText })\n ] });\n}\nfunction BreakingNewsOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const urgencyColors = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }, children: label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, padding: `0 ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n cfg.body && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.body })\n ] })\n ] });\n}\nfunction calcCountdownRemaining(targetTime) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 864e5),\n h: Math.floor(diff % 864e5 / 36e5),\n m: Math.floor(diff % 36e5 / 6e4),\n s: Math.floor(diff % 6e4 / 1e3),\n expired: diff === 0\n };\n}\nfunction CountdownOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = (0, import_react.useState)(\n () => targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n (0, import_react.useEffect)(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1e3);\n return () => clearInterval(id);\n }, [targetTime]);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n const pad = (n) => String(n).padStart(2, \"0\");\n const units = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" }\n ];\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventName }),\n remaining.expired ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }, children: cfg.message || \"Event ended\" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { display: \"flex\", gap: f * 0.6, alignItems: \"center\" }, children: units.filter((u) => u.show).map((u, i, arr) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }, children: u.value }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }, children: u.label })\n ] }),\n i < arr.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }, children: \":\" })\n ] }, u.label)) }),\n !remaining.expired && cfg.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }, children: cfg.message })\n ] });\n}\nfunction ShapeOverlay({ overlay, size }) {\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }, children: overlay.name }) });\n}\nfunction hexToRgb(hex) {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${num >> 16 & 255},${num >> 8 & 255},${num & 255}`;\n}\nvar FADE_DURATION_MS = 1e3;\nvar OverlayRenderer = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace\n}) => {\n const [dims, setDims] = (0, import_react.useState)(null);\n const rafRef = (0, import_react.useRef)(null);\n const [fadeMap, setFadeMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());\n const removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());\n const updateDims = (0, import_react.useCallback)(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (!computed || prev && prev.nativeWidth === computed.nativeWidth && prev.nativeHeight === computed.nativeHeight && prev.displayWidth === computed.displayWidth && prev.displayHeight === computed.displayHeight && prev.offsetX === computed.offsetX && prev.offsetY === computed.offsetY) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n (0, import_react.useEffect)(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n const activeOverlays = (0, import_react.useMemo)(\n () => overlays.filter((o) => isOverlayActive(o, currentTime)),\n [overlays, currentTime]\n );\n (0, import_react.useEffect)(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id);\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n return next;\n });\n }, [activeOverlays]);\n (0, import_react.useEffect)(() => {\n const toFadeIn = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n (0, import_react.useEffect)(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n if (!dims || fadeMap.size === 0) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n \"aria-hidden\": \"true\",\n style: {\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8\n },\n children: [...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX = coordinateSpace?.width ? dims.displayWidth / coordinateSpace.width : dims.scaleX;\n const scaleY = coordinateSpace?.height ? dims.displayHeight / coordinateSpace.height : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const opacity = visible ? baseOpacity : 0;\n const sz = { w: width, h: height };\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: `opacity ${FADE_DURATION_MS}ms ease`,\n zIndex: overlay.z_index,\n overflow: \"hidden\"\n },\n children: [\n overlay.type === \"image\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageOverlay, { overlay }),\n overlay.type === \"text\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextOverlay, { overlay }),\n overlay.type === \"scroller\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollerOverlay, { overlay }),\n overlay.type === \"shape\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeOverlay, { overlay, size: sz }),\n overlay.type === \"score_bug\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScoreBugOverlay, { overlay, size: sz }),\n overlay.type === \"lower_third\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LowerThirdOverlay, { overlay, size: sz }),\n overlay.type === \"qr_code\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QrCodeOverlay, { overlay, size: sz }),\n overlay.type === \"coming_up_next\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComingUpNextOverlay, { overlay, size: sz }),\n overlay.type === \"contextual_trigger\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextualTriggerOverlay, { overlay, size: sz }),\n overlay.type === \"odds_betting\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OddsBettingOverlay, { overlay, size: sz }),\n overlay.type === \"breaking_news\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakingNewsOverlay, { overlay, size: sz }),\n overlay.type === \"countdown\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountdownOverlay, { overlay, size: sz })\n ]\n },\n overlay.id\n );\n })\n }\n );\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n OverlayRenderer\n});\n","import React, { useEffect, useRef, useState, useCallback, useMemo, useId } from \"react\";\nimport {\n type SwirlOverlay,\n isOverlayActive,\n resolveImageUrl,\n} from \"../utils/overlays\";\n\ninterface VideoDimensions {\n nativeWidth: number;\n nativeHeight: number;\n displayWidth: number;\n displayHeight: number;\n offsetX: number;\n offsetY: number;\n scaleX: number;\n scaleY: number;\n}\n\ninterface OverlayRendererProps {\n overlays: SwirlOverlay[];\n currentTime: number;\n videoRef: React.RefObject<HTMLVideoElement | null>;\n coordinateSpace?: { width: number; height: number } | null;\n}\n\nfunction computeVideoDimensions(\n video: HTMLVideoElement\n): VideoDimensions | null {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n\n let renderWidth: number;\n let renderHeight: number;\n let offsetX: number;\n let offsetY: number;\n\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight,\n };\n}\n\nfunction ImageOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return (\n <img\n src={src}\n alt={overlay.name}\n draggable={false}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n />\n );\n}\n\nfunction TextOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const text = overlay.content || \"\";\n return (\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3,\n }}\n >\n {text}\n </div>\n );\n}\n\ninterface RSSItem {\n title: string;\n description: string;\n pubDate: string;\n author: string;\n category: string;\n}\n\nfunction parseRSSXml(xmlText: string, maxItems: number): RSSItem[] {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xmlText, \"text/xml\");\n if (doc.querySelector(\"parsererror\")) throw new Error(\"Invalid RSS XML\");\n return Array.from(doc.querySelectorAll(\"item\"))\n .map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\",\n }))\n .filter((i) => i.title)\n .slice(0, maxItems);\n}\n\nasync function fetchRSSItems(rssUrl: string, maxItems: number): Promise<RSSItem[]> {\n const encoded = encodeURIComponent(rssUrl);\n\n try {\n const resp = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp.ok) {\n const data = await resp.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch { /* fall through to next proxy */ }\n\n try {\n const resp = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text) return parseRSSXml(text, maxItems);\n }\n } catch { /* fall through to next proxy */ }\n\n const resp = await fetch(`https://thingproxy.freeboard.io/fetch/${rssUrl}`);\n if (!resp.ok) throw new Error(`RSS fetch failed: ${resp.status}`);\n const text = await resp.text();\n return parseRSSXml(text, maxItems);\n}\n\nfunction ScrollerOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const cfg = overlay.scroller_config;\n const uid = useId().replace(/:/g, \"\");\n\n const [rssItems, setRssItems] = useState<RSSItem[]>([]);\n const [rssLoading, setRssLoading] = useState(true);\n const [rssError, setRssError] = useState(false);\n\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n\n useEffect(() => {\n if (!rssUrl || (cfg?.use_custom_text && cfg?.custom_text)) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n fetchRSSItems(rssUrl, maxItems)\n .then((items) => { if (!cancelled) { setRssItems(items); setRssError(false); } })\n .catch(() => { if (!cancelled) setRssError(true); })\n .finally(() => { if (!cancelled) setRssLoading(false); });\n return () => { cancelled = true; };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n useEffect(() => {\n if (!rssUrl || !autoRefresh || (cfg?.use_custom_text && cfg?.custom_text)) return;\n const interval = setInterval(() => {\n fetchRSSItems(rssUrl, maxItems)\n .then((items) => { setRssItems(items); setRssError(false); })\n .catch(() => { /* keep showing last good items or error state */ });\n }, updateInterval * 60 * 1000);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n const sep = cfg?.separator_char ?? \"◆\";\n\n let segments: string[];\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts: string[] = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try { parts.push(new Date(item.pubDate).toLocaleDateString()); } catch { /* ignore */ }\n }\n if (cfg?.show_author && item.author) parts.push(`— ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed…\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed…\"] : [\"RSS Ticker\"];\n }\n\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== undefined ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, (fullText.length * 9) / scrollSpeed);\n\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal\n ? `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\"\n : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }`\n : `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\"\n : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n\n return (\n <>\n <style>{keyframes}</style>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : undefined,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n >\n {/* Top accent line */}\n {showAccentLine && (\n <div style={{ height: 3, background: accentColor, flexShrink: 0, width: \"100%\" }} />\n )}\n\n {/* Main row */}\n <div style={{ display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }}>\n {/* Label badge */}\n {label && (\n <div\n style={{\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1,\n }}\n >\n <span\n style={{\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\",\n }}\n >\n {label}\n </span>\n {labelLine2 && (\n <span\n style={{\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\",\n }}\n >\n {labelLine2}\n </span>\n )}\n </div>\n )}\n\n {/* Accent divider */}\n {label && (\n <div style={{ width: 3, background: accentColor, flexShrink: 0 }} />\n )}\n\n {/* Scrolling text */}\n <div\n style={{\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n {isHorizontal ? (\n <div\n style={{\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) => (\n <span key={copy} style={{ paddingRight: `${itemSpacing}px` }}>\n {segments.map((seg, i) => (\n <React.Fragment key={i}>\n {i > 0 && (\n <span style={{ opacity: 0.5, margin: \"0 8px\" }}>{sep}</span>\n )}\n <span style={{ textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }}>{seg}</span>\n </React.Fragment>\n ))}\n </span>\n ))}\n </div>\n ) : (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) =>\n segments.map((seg, i) => (\n <div key={`${copy}-${i}`} style={{ paddingBottom: `${itemSpacing / 4}px` }}>\n {seg}\n </div>\n ))\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n\nfunction parseConfig<T>(content?: string): T | null {\n if (!content) return null;\n try { return JSON.parse(content) as T; } catch { return null; }\n}\n\ninterface OverlaySize { w: number; h: number; }\n\ninterface ScoreBugCfg { homeTeam: string; awayTeam: string; homeScore: number; awayScore: number; period: string; clock: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface LowerThirdCfg { headline: string; subtitle: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; style: string; }\ninterface QrCodeCfg { url: string; ctaText: string; description: string; size: number; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ComingUpNextCfg { title: string; subtitle: string; scheduledTime: string; thumbnailUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ContextualTriggerCfg { triggerType: string; headline: string; message: string; iconType: string; backgroundColor: string; textColor: string; accentColor: string; animationStyle: string; }\ninterface OddsBettingCfg { eventTitle: string; options: Array<{ label: string; odds: string }>; sponsorText: string; backgroundColor: string; textColor: string; accentColor: string; oddsFormat: string; }\ninterface BreakingNewsCfg { headline: string; body: string; urgency: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface CountdownCfg { eventName: string; targetTime: string; message: string; showDays: boolean; showHours: boolean; showMinutes: boolean; showSeconds: boolean; backgroundColor: string; textColor: string; accentColor: string; }\n\nfunction ScoreBugOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ScoreBugCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }}>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.homeTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.homeScore}</div>\n </div>\n <div style={{ fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }}>\n <div>{cfg.period}</div>\n <div>{cfg.clock}</div>\n </div>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.awayTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.awayScore}</div>\n </div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction LowerThirdOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<LowerThirdCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }}>\n <div style={{ fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }}>{cfg.subtitle}</div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction QrCodeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<QrCodeCfg>(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }}>\n <div style={{ flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }}>\n <img src={qrUrl} alt=\"QR Code\" style={{ width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" }} />\n </div>\n <div style={{ fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.ctaText}</div>\n {cfg.description && <div style={{ fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.description}</div>}\n </div>\n );\n}\n\nfunction ComingUpNextOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ComingUpNextCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: Math.max(2, size.w * 0.015), flexShrink: 0, backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.6}px ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor }}>Coming Up Next</div>\n <div style={{ fontSize: \"1.5em\", fontWeight: 700, lineHeight: 1.2, marginTop: f * 0.2, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.title}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.6, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.subtitle}</div>\n {cfg.scheduledTime && <div style={{ fontSize: \"1em\", fontWeight: 600, marginTop: f * 0.4, color: cfg.accentColor }}>{cfg.scheduledTime}</div>}\n </div>\n {cfg.thumbnailUrl && (\n <div style={{ flexShrink: 0, width: Math.max(40, size.h * 0.75), overflow: \"hidden\" }}>\n <img src={cfg.thumbnailUrl} alt=\"thumbnail\" style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }} />\n </div>\n )}\n </div>\n );\n}\n\nfunction ContextualTriggerOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ContextualTriggerCfg>(overlay.content);\n if (!cfg) return null;\n const icons: Record<string, string> = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\uD83C\\uDF89\", info: \"\\u2139\\uFE0F\", warning: \"\\uD83D\\uDD14\" };\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <span style={{ fontSize: \"2em\", flexShrink: 0 }}>{icons[cfg.iconType] || \"\\u26A1\"}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.message}</div>\n </div>\n </div>\n );\n}\n\nfunction OddsBettingOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<OddsBettingCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventTitle}</div>\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }}>\n {(cfg.options || []).slice(0, 5).map((opt, i) => (\n <div key={i} style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }}>\n <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }}>{opt.label}</span>\n <span style={{ fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }}>{opt.odds}</span>\n </div>\n ))}\n </div>\n {cfg.sponsorText && <div style={{ fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }}>{cfg.sponsorText}</div>}\n </div>\n );\n}\n\nfunction BreakingNewsOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<BreakingNewsCfg>(overlay.content);\n if (!cfg) return null;\n const urgencyColors: Record<string, string> = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }}>{label}</div>\n <div style={{ flex: 1, padding: `0 ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n {cfg.body && <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.body}</div>}\n </div>\n </div>\n );\n}\n\nfunction calcCountdownRemaining(targetTime: string) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 86400000),\n h: Math.floor((diff % 86400000) / 3600000),\n m: Math.floor((diff % 3600000) / 60000),\n s: Math.floor((diff % 60000) / 1000),\n expired: diff === 0,\n };\n}\n\nfunction CountdownOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<CountdownCfg>(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = useState(() =>\n targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n\n useEffect(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1000);\n return () => clearInterval(id);\n }, [targetTime]);\n\n if (!cfg) return null;\n\n const f = Math.max(6, size.w * 0.055);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const units: Array<{ show: boolean; value: string; label: string }> = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" },\n ];\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventName}</div>\n {remaining.expired ? (\n <div style={{ fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }}>{cfg.message || \"Event ended\"}</div>\n ) : (\n <div style={{ display: \"flex\", gap: f * 0.6, alignItems: \"center\" }}>\n {units.filter(u => u.show).map((u, i, arr) => (\n <React.Fragment key={u.label}>\n <div style={{ textAlign: \"center\" }}>\n <div style={{ fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }}>{u.value}</div>\n <div style={{ fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }}>{u.label}</div>\n </div>\n {i < arr.length - 1 && <div style={{ fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }}>:</div>}\n </React.Fragment>\n ))}\n </div>\n )}\n {!remaining.expired && cfg.message && <div style={{ fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }}>{cfg.message}</div>}\n </div>\n );\n}\n\nfunction ShapeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }}>\n <div style={{ fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }}>{overlay.name}</div>\n </div>\n );\n}\n\nfunction hexToRgb(hex: string): string {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${(num >> 16) & 255},${(num >> 8) & 255},${num & 255}`;\n}\n\ninterface OverlayFadeState {\n overlay: SwirlOverlay;\n visible: boolean;\n}\n\nconst FADE_DURATION_MS = 1000;\n\nexport const OverlayRenderer: React.FC<OverlayRendererProps> = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace,\n}) => {\n const [dims, setDims] = useState<VideoDimensions | null>(null);\n const rafRef = useRef<number | null>(null);\n const [fadeMap, setFadeMap] = useState<Map<number, OverlayFadeState>>(new Map());\n const removeTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n const updateDims = useCallback(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (\n !computed ||\n (prev &&\n prev.nativeWidth === computed.nativeWidth &&\n prev.nativeHeight === computed.nativeHeight &&\n prev.displayWidth === computed.displayWidth &&\n prev.displayHeight === computed.displayHeight &&\n prev.offsetX === computed.offsetX &&\n prev.offsetY === computed.offsetY)\n ) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n\n useEffect(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n\n const activeOverlays = useMemo(\n () => overlays.filter((o) => isOverlayActive(o, currentTime)),\n [overlays, currentTime]\n );\n\n useEffect(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n\n setFadeMap((prev) => {\n const next = new Map(prev);\n\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id)!;\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n\n return next;\n });\n }, [activeOverlays]);\n\n useEffect(() => {\n const toFadeIn: number[] = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n\n useEffect(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n\n if (!dims || fadeMap.size === 0) return null;\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8,\n }}\n >\n {[...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX =\n coordinateSpace?.width\n ? dims.displayWidth / coordinateSpace.width\n : dims.scaleX;\n const scaleY =\n coordinateSpace?.height\n ? dims.displayHeight / coordinateSpace.height\n : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const opacity = visible ? baseOpacity : 0;\n const sz: OverlaySize = { w: width, h: height };\n\n return (\n <div\n key={overlay.id}\n style={{\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: `opacity ${FADE_DURATION_MS}ms ease`,\n zIndex: overlay.z_index,\n overflow: \"hidden\",\n }}\n >\n {overlay.type === \"image\" && <ImageOverlay overlay={overlay} />}\n {overlay.type === \"text\" && <TextOverlay overlay={overlay} />}\n {overlay.type === \"scroller\" && <ScrollerOverlay overlay={overlay} />}\n {overlay.type === \"shape\" && <ShapeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"score_bug\" && <ScoreBugOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"lower_third\" && <LowerThirdOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"qr_code\" && <QrCodeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"coming_up_next\" && <ComingUpNextOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"contextual_trigger\" && <ContextualTriggerOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"odds_betting\" && <OddsBettingOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"breaking_news\" && <BreakingNewsOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"countdown\" && <CountdownOverlay overlay={overlay} size={sz} />}\n </div>\n );\n })}\n </div>\n );\n};\n","const OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\n\nexport interface OverlayCoordinateSpace {\n width: number;\n height: number;\n}\n\nexport interface SwirlScrollerConfig {\n rss_url?: string;\n update_interval?: number;\n scroll_speed?: number;\n direction?: string;\n font_size?: number;\n font_family?: string;\n font_weight?: string;\n text_color?: string;\n background_color?: string;\n background_opacity?: number;\n border_color?: string;\n border_width?: number;\n border_radius?: number;\n padding?: number;\n margin?: number;\n show_title?: boolean;\n show_description?: boolean;\n show_timestamp?: boolean;\n show_author?: boolean;\n show_category?: boolean;\n max_items?: number;\n item_spacing?: number;\n fade_in_out?: boolean;\n fade_distance?: number;\n auto_refresh?: boolean;\n use_custom_text?: boolean;\n custom_text?: string;\n // Broadcast ticker branding\n label?: string;\n label_line2?: string;\n label_color?: string;\n label_text_color?: string;\n accent_color?: string;\n show_accent_line?: boolean;\n separator_char?: string;\n preset?: string;\n}\n\nexport type SwirlOverlayType =\n | \"image\"\n | \"text\"\n | \"scroller\"\n | \"shape\"\n | \"score_bug\"\n | \"lower_third\"\n | \"qr_code\"\n | \"coming_up_next\"\n | \"contextual_trigger\"\n | \"odds_betting\"\n | \"breaking_news\"\n | \"countdown\";\n\nexport interface SwirlOverlay {\n id: number;\n project_id: number;\n name: string;\n type: SwirlOverlayType | string;\n visible: boolean;\n x: number;\n y: number;\n width: number;\n height: number;\n opacity: number;\n start_time: string;\n duration: string;\n content?: string;\n image_url?: string;\n scroller_config?: SwirlScrollerConfig;\n z_index: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport function timeStringToSeconds(timeStr: string): number {\n if (!timeStr) return 0;\n\n const parts = timeStr.split(\":\");\n\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1000;\n }\n\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1000;\n }\n\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\n\nexport function isOverlayActive(\n overlay: SwirlOverlay,\n currentTime: number\n): boolean {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\n\nexport async function fetchProjectOverlays(\n projectId: number,\n apiBaseUrl: string = OVERLAY_API_BASE\n): Promise<SwirlOverlay[]> {\n const response = await fetch(\n `${apiBaseUrl}/adstorm/swirl/projects/${projectId}/overlays`\n );\n if (!response.ok) {\n throw new Error(\n `Failed to fetch overlays: ${response.status} ${response.statusText}`\n );\n }\n const data = await response.json();\n return Array.isArray(data) ? data : [];\n}\n\nexport function resolveImageUrl(\n imageUrl: string,\n apiBaseUrl: string = OVERLAY_API_BASE\n): string {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n"]}
1
+ {"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/ui/OverlayRenderer.cjs","../../src/ui/OverlayRenderer.tsx","../../src/utils/overlays.ts"],"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","OverlayRenderer_exports","OverlayRenderer","exports","OVERLAY_API_BASE","timeStringToSeconds","timeStr","parts","split","length","hours","parseInt","minutes","secStr","dotIdx","indexOf","seconds","substring","msFrag","ms","padEnd","parseFloat","isFinite","num","Math","max","isOverlayActive","overlay","currentTime","visible","startSec","start_time","durationSec","duration","resolveImageUrl","imageUrl","apiBaseUrl","startsWith","URL","url","origin","computeVideoDimensions","video","nativeWidth","videoWidth","nativeHeight","videoHeight","offsetWidth","offsetHeight","displayWidth","displayHeight","renderWidth","renderHeight","offsetX","offsetY","videoAspect","image_url","import_jsx_runtime","jsx","src","width","objectFit","pointerEvents","userSelect","content","style","height","display","alignItems","color","fontSize","fontFamily","fontWeight","textAlign","padding","boxSizing","wordBreak","textShadow","lineHeight","children","text","parseRSSXml","xmlText","maxItems","parser","DOMParser","doc","parseFromString","querySelector","Error","Array","querySelectorAll","map","item","title","textContent","replace","trim","description","pubDate","author","category","filter","i","slice","fetchRSSItems","rssUrl","encoded","resp","data","encodeURIComponent","window","location","fetch","ok","includes","json","contents","ScrollerOverlay","cfg","scroller_config","uid","import_react","useId","useState","rssItems","setRssItems","rssLoading","setRssLoading","rssError","setRssError","rss_url","autoRefresh","updateInterval","max_items","auto_refresh","useEffect","update_interval","cancelled","then","items","catch","finally","use_custom_text","custom_text","interval","setInterval","clearInterval","sep","separator_char","segments","show_title","push","show_description","show_timestamp","Date","toLocaleDateString","show_author","show_category","join","scrollSpeed","scroll_speed","direction","font_size","font_family","font_weight","textColor","text_color","bgColor","background_color","bgOpacity","background_opacity","borderRadius","itemSpacing","label","border_radius","item_spacing","labelLine2","label_line2","labelColor","label_color","labelTextColor","label_text_color","accentColor","accent_color","showAccentLine","show_accent_line","isHorizontal","isReverse","fullText","animId","id","keyframes","jsxs","Fragment","flexDirection","overflow","backgroundColor","hexToRgb","background","flexShrink","flex","minHeight","justifyContent","minWidth"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/ui/OverlayRenderer.tsx\nvar OverlayRenderer_exports = {};\n__export(OverlayRenderer_exports, {\n OverlayRenderer: () => OverlayRenderer\n});\nmodule.exports = __toCommonJS(OverlayRenderer_exports);\nvar import_react = __toESM(require(\"react\"), 1);\n\n// src/utils/overlays.ts\nvar OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\nfunction timeStringToSeconds(timeStr) {\n if (!timeStr) return 0;\n const parts = timeStr.split(\":\");\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1e3;\n }\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1e3;\n }\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\nfunction isOverlayActive(overlay, currentTime) {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\nfunction resolveImageUrl(imageUrl, apiBaseUrl = OVERLAY_API_BASE) {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n\n// src/ui/OverlayRenderer.tsx\nvar import_jsx_runtime = require(\"react/jsx-runtime\");\nfunction computeVideoDimensions(video) {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n let renderWidth;\n let renderHeight;\n let offsetX;\n let offsetY;\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight\n };\n}\nfunction ImageOverlay({ overlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src,\n alt: overlay.name,\n draggable: false,\n style: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\"\n }\n }\n );\n}\nfunction TextOverlay({ overlay }) {\n const text = overlay.content || \"\";\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3\n },\n children: text\n }\n );\n}\nfunction parseRSSXml(xmlText, maxItems) {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xmlText, \"text/xml\");\n if (doc.querySelector(\"parsererror\")) throw new Error(\"Invalid RSS XML\");\n return Array.from(doc.querySelectorAll(\"item\")).map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\"\n })).filter((i) => i.title).slice(0, maxItems);\n}\nasync function fetchRSSItems(rssUrl, maxItems) {\n const encoded = encodeURIComponent(rssUrl);\n try {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n const resp = await fetch(`${origin}/api/rss-proxy?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text.includes(\"<item\")) return parseRSSXml(text, maxItems);\n }\n } catch {\n }\n try {\n const resp = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp.ok) {\n const data = await resp.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch {\n }\n try {\n const resp = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text) return parseRSSXml(text, maxItems);\n }\n } catch {\n }\n throw new Error(\"All RSS proxies failed\");\n}\nfunction ScrollerOverlay({ overlay }) {\n const cfg = overlay.scroller_config;\n const uid = (0, import_react.useId)().replace(/:/g, \"\");\n const [rssItems, setRssItems] = (0, import_react.useState)([]);\n const [rssLoading, setRssLoading] = (0, import_react.useState)(true);\n const [rssError, setRssError] = (0, import_react.useState)(false);\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n (0, import_react.useEffect)(() => {\n if (!rssUrl || cfg?.use_custom_text && cfg?.custom_text) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n fetchRSSItems(rssUrl, maxItems).then((items) => {\n if (!cancelled) {\n setRssItems(items);\n setRssError(false);\n }\n }).catch(() => {\n if (!cancelled) setRssError(true);\n }).finally(() => {\n if (!cancelled) setRssLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n (0, import_react.useEffect)(() => {\n if (!rssUrl || !autoRefresh || cfg?.use_custom_text && cfg?.custom_text) return;\n const interval = setInterval(() => {\n fetchRSSItems(rssUrl, maxItems).then((items) => {\n setRssItems(items);\n setRssError(false);\n }).catch(() => {\n });\n }, updateInterval * 60 * 1e3);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n const sep = cfg?.separator_char ?? \"\\u25C6\";\n let segments;\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try {\n parts.push(new Date(item.pubDate).toLocaleDateString());\n } catch {\n }\n }\n if (cfg?.show_author && item.author) parts.push(`\\u2014 ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed\\u2026\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed\\u2026\"] : [\"RSS Ticker\"];\n }\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== void 0 ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, fullText.length * 9 / scrollSpeed);\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal ? `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\" : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }` : `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\" : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"style\", { children: keyframes }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : void 0,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\"\n },\n children: [\n showAccentLine && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { height: 3, background: accentColor, flexShrink: 0, width: \"100%\" } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }, children: [\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1\n },\n children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\"\n },\n children: label\n }\n ),\n labelLine2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\"\n },\n children: labelLine2\n }\n )\n ]\n }\n ),\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: 3, background: accentColor, flexShrink: 0 } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\"\n },\n children: isHorizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map((copy) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { paddingRight: `${itemSpacing}px` }, children: segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { opacity: 0.5, margin: \"0 8px\" }, children: sep }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }, children: seg })\n ] }, i)) }, copy))\n }\n ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map(\n (copy) => segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { paddingBottom: `${itemSpacing / 4}px` }, children: seg }, `${copy}-${i}`))\n )\n }\n )\n }\n )\n ] })\n ]\n }\n )\n ] });\n}\nfunction parseConfig(content) {\n if (!content) return null;\n try {\n return JSON.parse(content);\n } catch {\n return null;\n }\n}\nfunction ScoreBugOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.homeTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.homeScore })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.period }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.clock })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.awayTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.awayScore })\n ] })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction LowerThirdOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }, children: cfg.subtitle })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction QrCodeOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: qrUrl, alt: \"QR Code\", style: { width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" } }) }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.ctaText }),\n cfg.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.description })\n ] });\n}\nfunction ComingUpNextOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: Math.max(2, size.w * 0.015), flexShrink: 0, backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.6}px ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor }, children: \"Coming Up Next\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.5em\", fontWeight: 700, lineHeight: 1.2, marginTop: f * 0.2, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.title }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.6, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.subtitle }),\n cfg.scheduledTime && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 600, marginTop: f * 0.4, color: cfg.accentColor }, children: cfg.scheduledTime })\n ] }),\n cfg.thumbnailUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, width: Math.max(40, size.h * 0.75), overflow: \"hidden\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.thumbnailUrl, alt: \"thumbnail\", style: { width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" } }) })\n ] });\n}\nfunction ContextualTriggerOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const icons = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\u{1F389}\", info: \"\\u2139\\uFE0F\", warning: \"\\u{1F514}\" };\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontSize: \"2em\", flexShrink: 0 }, children: icons[cfg.iconType] || \"\\u26A1\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.message })\n ] })\n ] });\n}\nfunction OddsBettingOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventTitle }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }, children: (cfg.options || []).slice(0, 5).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }, children: opt.label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }, children: opt.odds })\n ] }, i)) }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }, children: cfg.sponsorText })\n ] });\n}\nfunction BreakingNewsOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const urgencyColors = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }, children: label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, padding: `0 ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n cfg.body && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.body })\n ] })\n ] });\n}\nfunction calcCountdownRemaining(targetTime) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 864e5),\n h: Math.floor(diff % 864e5 / 36e5),\n m: Math.floor(diff % 36e5 / 6e4),\n s: Math.floor(diff % 6e4 / 1e3),\n expired: diff === 0\n };\n}\nfunction CountdownOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = (0, import_react.useState)(\n () => targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n (0, import_react.useEffect)(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1e3);\n return () => clearInterval(id);\n }, [targetTime]);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n const pad = (n) => String(n).padStart(2, \"0\");\n const units = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" }\n ];\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventName }),\n remaining.expired ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }, children: cfg.message || \"Event ended\" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { display: \"flex\", gap: f * 0.6, alignItems: \"center\" }, children: units.filter((u) => u.show).map((u, i, arr) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }, children: u.value }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }, children: u.label })\n ] }),\n i < arr.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }, children: \":\" })\n ] }, u.label)) }),\n !remaining.expired && cfg.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }, children: cfg.message })\n ] });\n}\nfunction ShapeOverlay({ overlay, size }) {\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }, children: overlay.name }) });\n}\nfunction hexToRgb(hex) {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${num >> 16 & 255},${num >> 8 & 255},${num & 255}`;\n}\nvar FADE_DURATION_MS = 1e3;\nvar OverlayRenderer = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace\n}) => {\n const [dims, setDims] = (0, import_react.useState)(null);\n const rafRef = (0, import_react.useRef)(null);\n const [fadeMap, setFadeMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());\n const removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());\n const updateDims = (0, import_react.useCallback)(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (!computed || prev && prev.nativeWidth === computed.nativeWidth && prev.nativeHeight === computed.nativeHeight && prev.displayWidth === computed.displayWidth && prev.displayHeight === computed.displayHeight && prev.offsetX === computed.offsetX && prev.offsetY === computed.offsetY) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n (0, import_react.useEffect)(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n const activeOverlays = (0, import_react.useMemo)(\n () => overlays.filter((o) => isOverlayActive(o, currentTime)),\n [overlays, currentTime]\n );\n (0, import_react.useEffect)(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id);\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n return next;\n });\n }, [activeOverlays]);\n (0, import_react.useEffect)(() => {\n const toFadeIn = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n (0, import_react.useEffect)(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n if (!dims || fadeMap.size === 0) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n \"aria-hidden\": \"true\",\n style: {\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8\n },\n children: [...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX = coordinateSpace?.width ? dims.displayWidth / coordinateSpace.width : dims.scaleX;\n const scaleY = coordinateSpace?.height ? dims.displayHeight / coordinateSpace.height : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const opacity = visible ? baseOpacity : 0;\n const sz = { w: width, h: height };\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: `opacity ${FADE_DURATION_MS}ms ease`,\n zIndex: overlay.z_index,\n overflow: \"hidden\"\n },\n children: [\n overlay.type === \"image\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageOverlay, { overlay }),\n overlay.type === \"text\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextOverlay, { overlay }),\n overlay.type === \"scroller\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollerOverlay, { overlay }),\n overlay.type === \"shape\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeOverlay, { overlay, size: sz }),\n overlay.type === \"score_bug\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScoreBugOverlay, { overlay, size: sz }),\n overlay.type === \"lower_third\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LowerThirdOverlay, { overlay, size: sz }),\n overlay.type === \"qr_code\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QrCodeOverlay, { overlay, size: sz }),\n overlay.type === \"coming_up_next\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComingUpNextOverlay, { overlay, size: sz }),\n overlay.type === \"contextual_trigger\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextualTriggerOverlay, { overlay, size: sz }),\n overlay.type === \"odds_betting\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OddsBettingOverlay, { overlay, size: sz }),\n overlay.type === \"breaking_news\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakingNewsOverlay, { overlay, size: sz }),\n overlay.type === \"countdown\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountdownOverlay, { overlay, size: sz })\n ]\n },\n overlay.id\n );\n })\n }\n );\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n OverlayRenderer\n});\n","import React, { useEffect, useRef, useState, useCallback, useMemo, useId } from \"react\";\nimport {\n type SwirlOverlay,\n isOverlayActive,\n resolveImageUrl,\n} from \"../utils/overlays\";\n\ninterface VideoDimensions {\n nativeWidth: number;\n nativeHeight: number;\n displayWidth: number;\n displayHeight: number;\n offsetX: number;\n offsetY: number;\n scaleX: number;\n scaleY: number;\n}\n\ninterface OverlayRendererProps {\n overlays: SwirlOverlay[];\n currentTime: number;\n videoRef: React.RefObject<HTMLVideoElement | null>;\n coordinateSpace?: { width: number; height: number } | null;\n}\n\nfunction computeVideoDimensions(\n video: HTMLVideoElement\n): VideoDimensions | null {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n\n let renderWidth: number;\n let renderHeight: number;\n let offsetX: number;\n let offsetY: number;\n\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight,\n };\n}\n\nfunction ImageOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return (\n <img\n src={src}\n alt={overlay.name}\n draggable={false}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n />\n );\n}\n\nfunction TextOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const text = overlay.content || \"\";\n return (\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3,\n }}\n >\n {text}\n </div>\n );\n}\n\ninterface RSSItem {\n title: string;\n description: string;\n pubDate: string;\n author: string;\n category: string;\n}\n\nfunction parseRSSXml(xmlText: string, maxItems: number): RSSItem[] {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xmlText, \"text/xml\");\n if (doc.querySelector(\"parsererror\")) throw new Error(\"Invalid RSS XML\");\n return Array.from(doc.querySelectorAll(\"item\"))\n .map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\",\n }))\n .filter((i) => i.title)\n .slice(0, maxItems);\n}\n\nasync function fetchRSSItems(rssUrl: string, maxItems: number): Promise<RSSItem[]> {\n const encoded = encodeURIComponent(rssUrl);\n\n try {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n const resp = await fetch(`${origin}/api/rss-proxy?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text.includes(\"<item\")) return parseRSSXml(text, maxItems);\n }\n } catch { /* fall through */ }\n\n try {\n const resp = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp.ok) {\n const data = await resp.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch { /* fall through */ }\n\n try {\n const resp = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text) return parseRSSXml(text, maxItems);\n }\n } catch { /* fall through */ }\n\n throw new Error(\"All RSS proxies failed\");\n}\n\nfunction ScrollerOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const cfg = overlay.scroller_config;\n const uid = useId().replace(/:/g, \"\");\n\n const [rssItems, setRssItems] = useState<RSSItem[]>([]);\n const [rssLoading, setRssLoading] = useState(true);\n const [rssError, setRssError] = useState(false);\n\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n\n useEffect(() => {\n if (!rssUrl || (cfg?.use_custom_text && cfg?.custom_text)) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n fetchRSSItems(rssUrl, maxItems)\n .then((items) => { if (!cancelled) { setRssItems(items); setRssError(false); } })\n .catch(() => { if (!cancelled) setRssError(true); })\n .finally(() => { if (!cancelled) setRssLoading(false); });\n return () => { cancelled = true; };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n useEffect(() => {\n if (!rssUrl || !autoRefresh || (cfg?.use_custom_text && cfg?.custom_text)) return;\n const interval = setInterval(() => {\n fetchRSSItems(rssUrl, maxItems)\n .then((items) => { setRssItems(items); setRssError(false); })\n .catch(() => { /* keep showing last good items or error state */ });\n }, updateInterval * 60 * 1000);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n const sep = cfg?.separator_char ?? \"◆\";\n\n let segments: string[];\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts: string[] = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try { parts.push(new Date(item.pubDate).toLocaleDateString()); } catch { /* ignore */ }\n }\n if (cfg?.show_author && item.author) parts.push(`— ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed…\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed…\"] : [\"RSS Ticker\"];\n }\n\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== undefined ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, (fullText.length * 9) / scrollSpeed);\n\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal\n ? `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\"\n : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }`\n : `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\"\n : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n\n return (\n <>\n <style>{keyframes}</style>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : undefined,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n >\n {/* Top accent line */}\n {showAccentLine && (\n <div style={{ height: 3, background: accentColor, flexShrink: 0, width: \"100%\" }} />\n )}\n\n {/* Main row */}\n <div style={{ display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }}>\n {/* Label badge */}\n {label && (\n <div\n style={{\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1,\n }}\n >\n <span\n style={{\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\",\n }}\n >\n {label}\n </span>\n {labelLine2 && (\n <span\n style={{\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\",\n }}\n >\n {labelLine2}\n </span>\n )}\n </div>\n )}\n\n {/* Accent divider */}\n {label && (\n <div style={{ width: 3, background: accentColor, flexShrink: 0 }} />\n )}\n\n {/* Scrolling text */}\n <div\n style={{\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n {isHorizontal ? (\n <div\n style={{\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) => (\n <span key={copy} style={{ paddingRight: `${itemSpacing}px` }}>\n {segments.map((seg, i) => (\n <React.Fragment key={i}>\n {i > 0 && (\n <span style={{ opacity: 0.5, margin: \"0 8px\" }}>{sep}</span>\n )}\n <span style={{ textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }}>{seg}</span>\n </React.Fragment>\n ))}\n </span>\n ))}\n </div>\n ) : (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) =>\n segments.map((seg, i) => (\n <div key={`${copy}-${i}`} style={{ paddingBottom: `${itemSpacing / 4}px` }}>\n {seg}\n </div>\n ))\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n\nfunction parseConfig<T>(content?: string): T | null {\n if (!content) return null;\n try { return JSON.parse(content) as T; } catch { return null; }\n}\n\ninterface OverlaySize { w: number; h: number; }\n\ninterface ScoreBugCfg { homeTeam: string; awayTeam: string; homeScore: number; awayScore: number; period: string; clock: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface LowerThirdCfg { headline: string; subtitle: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; style: string; }\ninterface QrCodeCfg { url: string; ctaText: string; description: string; size: number; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ComingUpNextCfg { title: string; subtitle: string; scheduledTime: string; thumbnailUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ContextualTriggerCfg { triggerType: string; headline: string; message: string; iconType: string; backgroundColor: string; textColor: string; accentColor: string; animationStyle: string; }\ninterface OddsBettingCfg { eventTitle: string; options: Array<{ label: string; odds: string }>; sponsorText: string; backgroundColor: string; textColor: string; accentColor: string; oddsFormat: string; }\ninterface BreakingNewsCfg { headline: string; body: string; urgency: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface CountdownCfg { eventName: string; targetTime: string; message: string; showDays: boolean; showHours: boolean; showMinutes: boolean; showSeconds: boolean; backgroundColor: string; textColor: string; accentColor: string; }\n\nfunction ScoreBugOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ScoreBugCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }}>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.homeTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.homeScore}</div>\n </div>\n <div style={{ fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }}>\n <div>{cfg.period}</div>\n <div>{cfg.clock}</div>\n </div>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.awayTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.awayScore}</div>\n </div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction LowerThirdOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<LowerThirdCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }}>\n <div style={{ fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }}>{cfg.subtitle}</div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction QrCodeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<QrCodeCfg>(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }}>\n <div style={{ flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }}>\n <img src={qrUrl} alt=\"QR Code\" style={{ width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" }} />\n </div>\n <div style={{ fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.ctaText}</div>\n {cfg.description && <div style={{ fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.description}</div>}\n </div>\n );\n}\n\nfunction ComingUpNextOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ComingUpNextCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: Math.max(2, size.w * 0.015), flexShrink: 0, backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.6}px ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor }}>Coming Up Next</div>\n <div style={{ fontSize: \"1.5em\", fontWeight: 700, lineHeight: 1.2, marginTop: f * 0.2, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.title}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.6, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.subtitle}</div>\n {cfg.scheduledTime && <div style={{ fontSize: \"1em\", fontWeight: 600, marginTop: f * 0.4, color: cfg.accentColor }}>{cfg.scheduledTime}</div>}\n </div>\n {cfg.thumbnailUrl && (\n <div style={{ flexShrink: 0, width: Math.max(40, size.h * 0.75), overflow: \"hidden\" }}>\n <img src={cfg.thumbnailUrl} alt=\"thumbnail\" style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }} />\n </div>\n )}\n </div>\n );\n}\n\nfunction ContextualTriggerOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ContextualTriggerCfg>(overlay.content);\n if (!cfg) return null;\n const icons: Record<string, string> = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\uD83C\\uDF89\", info: \"\\u2139\\uFE0F\", warning: \"\\uD83D\\uDD14\" };\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <span style={{ fontSize: \"2em\", flexShrink: 0 }}>{icons[cfg.iconType] || \"\\u26A1\"}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.message}</div>\n </div>\n </div>\n );\n}\n\nfunction OddsBettingOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<OddsBettingCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventTitle}</div>\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }}>\n {(cfg.options || []).slice(0, 5).map((opt, i) => (\n <div key={i} style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }}>\n <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }}>{opt.label}</span>\n <span style={{ fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }}>{opt.odds}</span>\n </div>\n ))}\n </div>\n {cfg.sponsorText && <div style={{ fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }}>{cfg.sponsorText}</div>}\n </div>\n );\n}\n\nfunction BreakingNewsOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<BreakingNewsCfg>(overlay.content);\n if (!cfg) return null;\n const urgencyColors: Record<string, string> = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }}>{label}</div>\n <div style={{ flex: 1, padding: `0 ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n {cfg.body && <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.body}</div>}\n </div>\n </div>\n );\n}\n\nfunction calcCountdownRemaining(targetTime: string) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 86400000),\n h: Math.floor((diff % 86400000) / 3600000),\n m: Math.floor((diff % 3600000) / 60000),\n s: Math.floor((diff % 60000) / 1000),\n expired: diff === 0,\n };\n}\n\nfunction CountdownOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<CountdownCfg>(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = useState(() =>\n targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n\n useEffect(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1000);\n return () => clearInterval(id);\n }, [targetTime]);\n\n if (!cfg) return null;\n\n const f = Math.max(6, size.w * 0.055);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const units: Array<{ show: boolean; value: string; label: string }> = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" },\n ];\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventName}</div>\n {remaining.expired ? (\n <div style={{ fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }}>{cfg.message || \"Event ended\"}</div>\n ) : (\n <div style={{ display: \"flex\", gap: f * 0.6, alignItems: \"center\" }}>\n {units.filter(u => u.show).map((u, i, arr) => (\n <React.Fragment key={u.label}>\n <div style={{ textAlign: \"center\" }}>\n <div style={{ fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }}>{u.value}</div>\n <div style={{ fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }}>{u.label}</div>\n </div>\n {i < arr.length - 1 && <div style={{ fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }}>:</div>}\n </React.Fragment>\n ))}\n </div>\n )}\n {!remaining.expired && cfg.message && <div style={{ fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }}>{cfg.message}</div>}\n </div>\n );\n}\n\nfunction ShapeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }}>\n <div style={{ fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }}>{overlay.name}</div>\n </div>\n );\n}\n\nfunction hexToRgb(hex: string): string {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${(num >> 16) & 255},${(num >> 8) & 255},${num & 255}`;\n}\n\ninterface OverlayFadeState {\n overlay: SwirlOverlay;\n visible: boolean;\n}\n\nconst FADE_DURATION_MS = 1000;\n\nexport const OverlayRenderer: React.FC<OverlayRendererProps> = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace,\n}) => {\n const [dims, setDims] = useState<VideoDimensions | null>(null);\n const rafRef = useRef<number | null>(null);\n const [fadeMap, setFadeMap] = useState<Map<number, OverlayFadeState>>(new Map());\n const removeTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n const updateDims = useCallback(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (\n !computed ||\n (prev &&\n prev.nativeWidth === computed.nativeWidth &&\n prev.nativeHeight === computed.nativeHeight &&\n prev.displayWidth === computed.displayWidth &&\n prev.displayHeight === computed.displayHeight &&\n prev.offsetX === computed.offsetX &&\n prev.offsetY === computed.offsetY)\n ) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n\n useEffect(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n\n const activeOverlays = useMemo(\n () => overlays.filter((o) => isOverlayActive(o, currentTime)),\n [overlays, currentTime]\n );\n\n useEffect(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n\n setFadeMap((prev) => {\n const next = new Map(prev);\n\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id)!;\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n\n return next;\n });\n }, [activeOverlays]);\n\n useEffect(() => {\n const toFadeIn: number[] = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n\n useEffect(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n\n if (!dims || fadeMap.size === 0) return null;\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8,\n }}\n >\n {[...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX =\n coordinateSpace?.width\n ? dims.displayWidth / coordinateSpace.width\n : dims.scaleX;\n const scaleY =\n coordinateSpace?.height\n ? dims.displayHeight / coordinateSpace.height\n : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const opacity = visible ? baseOpacity : 0;\n const sz: OverlaySize = { w: width, h: height };\n\n return (\n <div\n key={overlay.id}\n style={{\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: `opacity ${FADE_DURATION_MS}ms ease`,\n zIndex: overlay.z_index,\n overflow: \"hidden\",\n }}\n >\n {overlay.type === \"image\" && <ImageOverlay overlay={overlay} />}\n {overlay.type === \"text\" && <TextOverlay overlay={overlay} />}\n {overlay.type === \"scroller\" && <ScrollerOverlay overlay={overlay} />}\n {overlay.type === \"shape\" && <ShapeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"score_bug\" && <ScoreBugOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"lower_third\" && <LowerThirdOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"qr_code\" && <QrCodeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"coming_up_next\" && <ComingUpNextOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"contextual_trigger\" && <ContextualTriggerOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"odds_betting\" && <OddsBettingOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"breaking_news\" && <BreakingNewsOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"countdown\" && <CountdownOverlay overlay={overlay} size={sz} />}\n </div>\n );\n })}\n </div>\n );\n};\n","const OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\n\nexport interface OverlayCoordinateSpace {\n width: number;\n height: number;\n}\n\nexport interface SwirlScrollerConfig {\n rss_url?: string;\n update_interval?: number;\n scroll_speed?: number;\n direction?: string;\n font_size?: number;\n font_family?: string;\n font_weight?: string;\n text_color?: string;\n background_color?: string;\n background_opacity?: number;\n border_color?: string;\n border_width?: number;\n border_radius?: number;\n padding?: number;\n margin?: number;\n show_title?: boolean;\n show_description?: boolean;\n show_timestamp?: boolean;\n show_author?: boolean;\n show_category?: boolean;\n max_items?: number;\n item_spacing?: number;\n fade_in_out?: boolean;\n fade_distance?: number;\n auto_refresh?: boolean;\n use_custom_text?: boolean;\n custom_text?: string;\n // Broadcast ticker branding\n label?: string;\n label_line2?: string;\n label_color?: string;\n label_text_color?: string;\n accent_color?: string;\n show_accent_line?: boolean;\n separator_char?: string;\n preset?: string;\n}\n\nexport type SwirlOverlayType =\n | \"image\"\n | \"text\"\n | \"scroller\"\n | \"shape\"\n | \"score_bug\"\n | \"lower_third\"\n | \"qr_code\"\n | \"coming_up_next\"\n | \"contextual_trigger\"\n | \"odds_betting\"\n | \"breaking_news\"\n | \"countdown\";\n\nexport interface SwirlOverlay {\n id: number;\n project_id: number;\n name: string;\n type: SwirlOverlayType | string;\n visible: boolean;\n x: number;\n y: number;\n width: number;\n height: number;\n opacity: number;\n start_time: string;\n duration: string;\n content?: string;\n image_url?: string;\n scroller_config?: SwirlScrollerConfig;\n z_index: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport function timeStringToSeconds(timeStr: string): number {\n if (!timeStr) return 0;\n\n const parts = timeStr.split(\":\");\n\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1000;\n }\n\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1000;\n }\n\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\n\nexport function isOverlayActive(\n overlay: SwirlOverlay,\n currentTime: number\n): boolean {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\n\nexport async function fetchProjectOverlays(\n projectId: number,\n apiBaseUrl: string = OVERLAY_API_BASE\n): Promise<SwirlOverlay[]> {\n const response = await fetch(\n `${apiBaseUrl}/adstorm/swirl/projects/${projectId}/overlays`\n );\n if (!response.ok) {\n throw new Error(\n `Failed to fetch overlays: ${response.status} ${response.statusText}`\n );\n }\n const data = await response.json();\n return Array.isArray(data) ? data : [];\n}\n\nexport function resolveImageUrl(\n imageUrl: string,\n apiBaseUrl: string = OVERLAY_API_BASE\n): string {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n"]}