{"version":3,"file":"footer-section.min.js","mappings":"yBACA,IAAAA,EAAA,CCAAA,EAAA,SAAAC,GACAC,OAAAC,eAAAF,EAAAG,OAAAC,YAAA,CAAsDC,MAAA,WACtDJ,OAAAC,eAAAF,EAAA,cAAgDK,OAAA,GAChD,oBCMA,MAAMC,wBAAwBC,YAC5B,6BAAkBC,GAChB,MAAO,CAAC,cAAe,aACzB,CAcO,iBAAAC,GACLC,KAAKC,uBAELD,KAAKE,QACP,CAEO,wBAAAC,CAAyBC,EAAeC,EAAkBC,GAC/DN,KAAKC,uBAEDD,KAAKO,aAAeF,IAAaC,GACnCN,KAAKE,QAET,CAEQ,oBAAAD,GACDD,KAAKQ,aACRR,KAAKS,aAAa,CAAEC,KAAM,SAC1BV,KAAKQ,WAAWG,UAAY,iFAEhC,CAEQ,MAAAT,GACN,MAAMU,EAASC,KAAKC,IAAIC,WAAWf,KAAKgB,QAAQJ,QAAS,GACzD,GAAIK,MAAML,GACR,OAGF,MAAMM,EAAQC,SAASnB,KAAKgB,QAAQE,MAAO,IAC3C,IAAKA,EACH,OAMFlB,KAAKoB,QAAS,EAKd,MAAMC,EAAerB,KAAKsB,cAAc,gBACpCD,IACFA,EAAaE,YAAcX,EAAOY,YAGpC,MAAMC,EAAezB,KAAKsB,cAAc,gBACxC,GAAIG,EAAc,CAChB,MAAMC,EAAS,IAAIC,KAAKC,aAAa,SACrCH,EAAaF,YAAcG,EAAOA,OAAOR,EAC3C,CAEA,MAAMW,EAAe7B,KAAKsB,cAAc,2BACpCO,IACFA,EAAab,QAAQJ,OAASA,EAAOY,YAGvC,MAAMM,EAAa9B,KAAKsB,cAAc,iBACtC,GAAIQ,EAAY,CACd,IAAIC,EAAS,GAEXA,EADEnB,GAAU,EACH,YACAA,GAAU,EACV,QACAA,GAAU,EACV,UACAA,GAAU,EACV,OAEA,MAGXkB,EAAWP,YAAcQ,CAC3B,CACF,EASGC,eAAeC,IAAI,qBACtBD,eAAeE,OAAO,mBAAoBtC,iBCvG5C,MAAMuC,mBAAmBtC,YACvB,6BAAkBC,GAChB,MAAO,CACL,aACA,qBAEJ,CAwBO,iBAAAC,GACLC,KAAKC,uBAELD,KAAKE,QACP,CAEO,wBAAAC,CAAyBC,EAAeC,EAAkBC,GAC/DN,KAAKC,uBAEDD,KAAKO,aAAeF,IAAaC,GACnCN,KAAKE,QAET,CAEQ,oBAAAD,GACDD,KAAKQ,aACRR,KAAKS,aAAa,CAAEC,KAAM,SAC1BV,KAAKQ,WAAWG,UAAY,w/CAEhC,CAEQ,MAAAT,GACOF,KAAKQ,WAAWc,cAAc,KACtCc,KAAOpC,KAAKgB,QAAQqB,KAAO,IAEXrC,KAAKQ,WAAWc,cAAc,OACtCgB,IAAMtC,KAAKgB,QAAQuB,cAEhC,MAAMd,EAAezB,KAAKQ,WAAWc,cAAc,UAC7CJ,EAAQC,SAASnB,KAAKgB,QAAQE,MAAO,IAAM,IAE3CsB,EAAYC,OAAOxB,MAAMC,GAAS,GAAKA,EAAMwB,QAAQ,GAE3DjB,EAAaF,YAAciB,EAAYA,EAAY,IAAM,GAEzD,MAAMG,EAAQ3C,KAAKgB,QAAQ4B,aAAe5C,KAAKgB,QAAQ4B,aAAe,GAEhEC,EAAQ7C,KAAKQ,WAAWc,cAAc,UACxCqB,GAASH,EACN,MAALK,GAAAA,EAAOC,aAAa,aAAc,CAChCH,EACA,yBACAH,EACA,4BACA,oCACAO,KAAK,MAEF,MAALF,GAAAA,EAAOC,aAAa,aAAc,sDAEtC,EASGd,eAAeC,IAAI,gBACtBD,eAAeE,OAAO,cAAeC,YCtFvC,MAAMa,8BAA8BnD,YAClC,6BAAkBC,GAChB,MAAO,CAAC,cACV,CAcO,iBAAAC,GACLC,KAAKC,uBAELD,KAAKE,QACP,CAEO,wBAAAC,CAAyB8C,EAAc5C,EAAkBC,GAC9DN,KAAKC,uBAEDD,KAAKO,aAAwB,gBAAT0C,GAA0B5C,IAAaC,GAC7DN,KAAKE,QAET,CAEQ,oBAAAD,GACDD,KAAKQ,aACRR,KAAKS,aAAa,CAAEC,KAAM,SAC1BV,KAAKQ,WAAWG,UAAY,yhFAEhC,CAEQ,MAAAT,GACN,IAAIU,EAASG,WAAWf,KAAKgB,QAAQJ,QAGrC,GAFAA,EAASA,EAAS,EAAI,EAAIA,GAErBA,EACH,OAGa,IAAXA,IACFZ,KAAKgB,QAAQkC,KAAO,QAGtB,MAAMrB,EAAe7B,KAAKQ,WAAW2C,iBAA8B,SACnE,IAAK,IAAIC,EAAI,EAAGA,EAAIvB,EAAawB,OAAQD,IAKvC,GAJIA,EAAI,GAAKxC,GACXiB,EAAauB,GAAGE,UAAUC,IAAI,UAG5BH,EAAI,IAAMxC,EAAQ,CACpBiB,EAAauB,GAAGE,UAAUC,IAAI,eAC9B,KACF,CAEJ,ECbK,SAASC,EAAMC,EACpBC,GACA,QAD2B,IAA3BA,IAAAA,EAA8B,oBACF,aAAxBC,SAASC,YAAuC,qBAATF,GACjB,gBAAxBC,SAASC,WAA+B,CACxC,MAAMC,EAAQC,WAAWL,EAAU,GAEnC,MAD8B,IAAMM,aAAaF,EAEnD,CACEG,iBAAiBN,EAAMD,GAEvB,MAD8B,IAAMQ,oBAAoBP,EAAMD,EAGlE,CCxDA,SAASS,EAAoBC,GAG3B,GAFAA,EAAMC,iBAEFD,EAAME,yBAAyBxE,YAAa,CAC9C,MAAMyE,EAAa,IAAIC,MAAM,6BAA8B,CAAEC,SAAS,EAAMC,UAAU,IACtFN,EAAME,cAAcK,cAAcJ,GAENX,SAASgB,eAAe,0BAC9C3D,QAAQ4D,KAAO,MACvB,CACF,CFwDK5C,eAAeC,IAAI,4BACtBD,eAAeE,OAAO,0BAA2Bc,uBEvDnDQ,EAnBA,WACE,MAAMqB,EAAQlB,SAASR,iBAAoC,gCAC3D,IAAK,MAAM2B,KAAQD,EACjBC,EAAKd,iBAAiB,QAASE,EAEnC,GC+EO,MAAMa,uBAAuBC,MAMlC,WAAAC,CAAYC,EAAkBC,GAC5BC,MAAMF,GAAW,UAAWC,EAC9B,ECjFF,SAASE,EAAeC,EAAyBJ,GAC/C,MAAMK,EAAQD,EAAME,QAAQ,kBAC5BD,EAAMjC,UAAUC,IAAI,SACpBgC,EAAMjE,cAAc,iBAAiBC,YAAc2D,CACrD,CAEA,SAASO,EAAeH,GACtBA,EAAME,QAAQ,kBAAkBlC,UAAUC,IAAI,UAChD,CAEA,SAASmC,EAAWJ,GAClB,GAAIA,EAAO,CACT,MAAMC,EAAQD,EAAME,QAAQ,kBAC5BD,EAAMjC,UAAUqC,OAAO,QAAS,WAChCJ,EAAMjE,cAAc,iBAAiBC,YAAc,EACrD,CACF,CAKA,SAASqE,EAASzB,GAUhB,GATAA,EAAMC,iBASFyB,QAAQC,WACV,OAGF,MAAMC,EAAO5B,EAAME,cACnB,KAAM0B,aAAgBC,iBACpB,OAGF,MAAMC,EAAeF,EAAKzE,cAAc,UACxC2E,EAAa3C,UAAUC,IAAI,cAC3B0C,EAAa1E,YAAc,gBAE3B,MAAM2E,EAAaH,EAAKzE,cAAgC,oBAClD6E,EAAYJ,EAAKzE,cAAgC,mBACjD8E,EAAaL,EAAKzE,cAAgC,2BAElD+E,EAA2B,CAC/BC,MAAOJ,EAAWvG,MAClB4G,WAAYJ,EAAUxG,MACtB6G,MAAiB,MAAVJ,OAAU,EAAVA,EAAYzG,OAGf8G,GA6EC,mBAAmBC,KA7EWL,EAAYE,YACjD,MAAMI,GClEG,2JAA2JD,KDkE5HL,EAAYC,OAEpD,IAAIM,GAAa,EACbP,EAAYG,QACdI,GAlEK,wEAAwEF,KAkElDL,EAAYG,QAGzC,MAAMK,EAAoBd,EAAKzE,cAAgC,oBAE/D,IAAIwF,GAAa,EACbT,EAAYG,OAASK,IACvBC,GAAcD,EAAkBE,SAGlC,MAAMC,EAAajB,EAAKzE,cAAgC,gBAAgB3B,MAEpE8G,EACFpB,EAAec,EAAW,uBAE1BV,EAAeU,GAGbQ,EACFtB,EAAea,EAAY,sCAE3BT,EAAeS,GAGjB,MAAMe,EAAelB,EAAKzE,cAAc,4BAexC,GAdIwF,GACFG,EAAa3D,UAAUC,IAAI,SAC3B0D,EAAa3F,cAAc,iBAAiBC,YAC1C,gDAEF0F,EAAa3D,UAAUC,IAAI,WAGzBqD,EACFvB,EAAee,EAAY,qCAE3BX,EAAeW,GAGbK,GAAaE,GAAcC,GAAcE,EAG3C,OAFAb,EAAa3C,UAAUqC,OAAO,mBAC9BM,EAAa1E,YAAc,UAI7B,IAAI2F,EAAe,MACnB,MAAMC,EAAoBpB,EAAKzE,cAAgC,kBAC3D6F,IACFD,EAAeC,EAAkBxH,OAInC,IADsBoG,EAAKzE,cAAc,uBAEvC,QDtGG8F,eAAyB9B,GAC9B,MAAMjD,EAAM,+DAENgF,EAA2B,CAC/Bf,MAAOhB,EAAMgB,MACbC,WAAYjB,EAAMiB,WAClBC,MAAOlB,EAAMkB,MACbc,YAAa,6BACbC,oBAAqBjC,EAAMiC,oBAC3BC,kBAAmBlC,EAAMkC,mBAG3B,IAAIC,EACJ,IACEA,QAAiBC,MAAMrF,EAAK,CAC1BsF,KAAMC,KAAKC,UAAUR,GACrBS,MAAO,WACPC,QAAS,CACP,eAAgB,oBAElBC,OAAQ,OACRtH,KAAM,QAEV,CAAE,MAAOuH,GACP,MAAMC,EAAK,IAAInD,eAAe,kCAAmC,CAAEoD,MAAOF,IAG1E,MAFAC,EAAGE,aAAef,EAClBa,EAAG7F,IAAMA,EACH6F,CACR,CAMA,GAAwB,MAApBT,EAASY,OAAgB,CAC3B,MAAMJ,EAAQ,IAAIlD,eAAe,mCACjCkD,EAAMG,aAAef,EAKrB,IACE,MAAMiB,QAAqBb,EAASc,OACpCN,EAAMO,cAAgBF,EAiBK,iBAAZ,OADQ3I,EAdC2I,QAeT,EAAL3I,EAAOsI,QAAsBtI,EAAMsI,MAAM5E,OAAS,GAfW,iBAAvBiF,EAAaL,OAC3DK,EAAaL,MAAM5E,OAAS,IAC5B4E,EAAM/C,QAAUoD,EAAaL,MAEjC,CAAE,MAAOA,GACP,CAKF,MAFAA,EAAMI,OAASZ,EAASY,OACxBJ,EAAM5F,IAAMoF,EAASpF,IACf4F,CACR,CAGF,IAA6BtI,CAF7B,ECwDE8I,CARuC,CACrCnC,MAAOD,EAAYC,MACnBC,WAAYF,EAAYE,WACxBC,MAAOH,EAAYG,MACnBe,oBAAqBP,EACrBQ,kBAAmB,WAIlBkB,KAAK,IAUV,SAA4B3C,EAAuBM,EAA0Ba,GAC3E,MAAMyB,EAAgB5C,EAAKzE,cAAc,uBACzCqH,EAAcrF,UAAUqC,OAAO,SAC/BgD,EAAcrF,UAAUC,IAAI,WAC5BoF,EAAcpH,YAAc,6BAE5B,MAAM6E,EAAaL,EAAKzE,cAAgC,2BACpD8E,IACFA,EAAWzG,MAAQ,IAGrB,MAAMiJ,EAAa7C,EAAKzE,cAAgC,oBACpDsH,IACFA,EAAW7B,SAAU,GAGvB,MAAME,EAAelB,EAAKzE,cAA2B,4BACjD2F,IACFA,EAAa7F,QAAS,EACtB6F,EAAa3D,UAAUqC,OAAO,YAGhC,MAAMkD,EAAS9C,EAAK5C,iBAAmC,8BACvD,IAAK,MAAMmC,KAASuD,EAClBvD,EAAM3F,MAAQ,GAGhB,MAAMwG,EAAYJ,EAAKzE,cAAgC,mBACvDoE,EAAWS,GAEX,MAAMD,EAAaH,EAAKzE,cAAgC,oBACxDoE,EAAWQ,GAEXR,EAAWU,GAEX,MAAMH,EAAeF,EAAKzE,cAAc,UACxC2E,EAAa3C,UAAUqC,OAAO,cAC9BM,EAAa3C,UAAUC,IAAI,WAC3B0C,EAAa1E,YAAc,WAmB7B,SAAkCwE,EAAuBM,GACvD,IAAKA,EAAYC,QAAUD,EAAYG,MACrC,OAIF,MAAMsC,EAAiB,CAAC,EASxB,GAAIzC,EAAYE,WAAY,CAC1B,MAAMwC,EAAQ1C,EAAYE,WAAWyC,MAAM,KACtB,IAAjBD,EAAM1F,OACRyF,EAAOvC,WAAawC,EAAM,GACA,IAAjBA,EAAM1F,QACfyF,EAAOvC,WAAawC,EAAM,GAC1BD,EAAOG,UAAYF,EAAM,KAEzBD,EAAOG,UAAYF,EAAMG,MACzBJ,EAAOvC,WAAawC,EAAMhG,KAAK,KAEnC,CAEIsD,EAAYC,QACdwC,EAAOxC,MAAQD,EAAYC,OAGzBD,EAAYG,QACdsC,EAAOtC,MAAQH,EAAYG,OAG7B,MAAMrC,EAAQ,IAAIgF,YAAoB,oBAAqB,CACzD3E,SAAS,EACTsE,WAEF/C,EAAKrB,cAAcP,EACrB,CA1DEiF,CAAyBrD,EAAMM,GAG/B,MAAMgD,EAAc,IAAIF,YAA8B,cAAe,CACnE3E,SAAS,EACTsE,OAAQ,CACNQ,cAAepC,EACfqC,iBAAgBlD,EAAYC,MAC5BkD,UAAW,mBACXC,cAAeC,SAAStH,KACxBuH,UAAW,sBACXC,iBAAgBvD,EAAYG,SAGhCT,EAAKrB,cAAc2E,EACrB,CAjEgBQ,CAAmB9D,EAAMM,EAAaa,IACjD4C,MAAO7B,GA6GZ,SAA0BA,EAAgBlC,GACxC,MAAM4C,EAAgB5C,EAAKzE,cAAc,uBACzCqH,EAAcrF,UAAUqC,OAAO,WAC/BgD,EAAcrF,UAAUC,IAAI,SAE5B,MAAM0C,EAAeF,EAAKzE,cAAc,UACxC2E,EAAa3C,UAAUqC,OAAO,cAC9BM,EAAa1E,YAAc,SAEvB0G,aAAiBlD,eACnB4D,EAAchI,UAAYsH,EAAM/C,SAC9B,uEAEFyD,EAAchI,UACZ,sEACFoJ,QAAQC,KAAK/B,EAAO,qCAGtB,MAAMhB,EAAelB,EAAKzE,cAA2B,4BACrD2F,EAAa7F,QAAS,CACxB,CAjIsB6I,CAAiBhC,EAAOlC,GAC9C,CAkIA,SAASmE,EAAa/F,GACpB,GAAIA,EAAME,yBAAyB8F,iBAAkB,CACnDzE,EAAWvB,EAAME,eAEjB,MAAM+F,EAAQjG,EAAME,cAAc0B,KAAKzE,cAA2B,4BAClE8I,EAAMhJ,QAAS,EACfgJ,EAAM9I,cAAc,iBAAiBC,YAAc,EACrD,CACF,CAEA,SAAS8I,EAAYlG,GACnB,GAAIA,EAAME,yBAAyB8F,iBAAkB,CACrChG,EAAME,cAAc0B,KAAKzE,cAA2B,4BAC5DF,QAAU+C,EAAME,cAAc1E,KACtC,CACF,CAEA,SAAS2K,EAAcnG,GACrB,GAAIA,EAAMoG,kBAAkBJ,kBAAoBhG,EAAMoG,OAAOxD,QAAS,CACpE,MAAMqD,EAAQjG,EAAMoG,OAAOxE,KAAKzE,cAAc,4BAC9C8I,EAAM9G,UAAUqC,OAAO,SACvByE,EAAM9I,cAAc,iBAAiBC,YAAc,EACrD,CACF,CAEA,SAASiJ,EAAYrG,GACfA,EAAME,yBAAyB8F,kBACjCzE,EAAWvB,EAAME,cAErB,CAEA,SAASoG,EAAatG,GAChBA,EAAME,yBAAyB8F,kBACjCzE,EAAWvB,EAAME,cAErB,CE9SM,SAAUqG,EAAOC,EAAqBzF,GAC1C,IAAKyF,EACH,MAAM,IAAIC,eAAe1F,EAE7B,CFmUA1B,EAvBA,WACE,MAAM4G,EAAQzG,SAASrC,cAAgC,oBAClD,MAAL8I,GAAAA,EAAOpG,iBAAiB,SAAUsG,GAElC,MAAM9D,EAAQ7C,SAASrC,cAAgC,2BAClD,MAALkF,GAAAA,EAAOxC,iBAAiB,QAASkG,GAC5B,MAAL1D,GAAAA,EAAOxC,iBAAiB,OAAQqG,GAEhC,MAAMtE,EAAOpC,SAASrC,cAA+B,0BACjD,MAAJyE,GAAAA,EAAM/B,iBAAiB,SAAU4B,GAEjC,MAAMK,EAAmB,MAAJF,OAAI,EAAJA,EAAMzE,cAAc,UACrC2E,IACFA,EAAa4E,UAAW,GAG1B,MAAM5H,EAAOU,SAASrC,cAAgC,mBAClD,MAAJ2B,GAAAA,EAAMe,iBAAiB,QAASwG,GAEhC,MAAMlE,EAAQ3C,SAASrC,cAAgC,oBAClD,MAALgF,GAAAA,EAAOtC,iBAAiB,QAASyG,EACnC,GE/TM,MAAOG,uBAAuB5F,MAClC,WAAAC,CAAYC,QAAO,IAAPA,IAAAA,EAAU,mBACpBE,MAAMF,GACNlF,KAAKiD,KAAOjD,KAAKiF,YAAYhC,IAC/B,ECgBK,SAAS6H,EAAc7C,EAAY8C,EAAapL,GACrD,IACEsI,EAAM8C,GAAOpL,CACf,CAAE,MAAOqL,GACP,IAAIC,EAEFA,EADED,aAA2BhG,MACpBgG,EAAgB9F,QAEhB,GAAG8F,IAGdjB,QAAQmB,IAAI,kCAAmC,CAC7CH,MACA9H,KAAW,MAALgF,OAAK,EAALA,EAAOhF,KACbkI,IAAK5L,OAAO6L,UAAU5J,SAAS6J,KAAKpD,GACpCgD,UAEJ,CACF,CCvCO7D,eAAekE,EAAkBC,EAAgBC,EAAwBC,QAAQ,IAARA,IAAAA,GAAW,GACzFf,EAAOa,EAAQ,mBACfb,EAAOc,EAAgB,4BAEvB,MAAME,EAAOD,EACX,kDACA,0CAA0CD,IAE5C,IAQI/D,EAiBAkE,EASAhE,EAlCAI,EAAuB,CAAC,EACvB0D,IACH1D,EAAU,CACR,eAAgB,mBAChBwD,SAAUA,IAKd,IACE9D,QAAiBC,MAAMgE,EAAM,CAAE3D,WACjC,CAAE,MAAOE,GAEP,MADA6C,EAAc7C,EAAO,MAAOyD,GACtBzD,CACR,CAEA,IAAKR,EAASmE,GAAI,CAChB,MAAM3D,EAAa,IAAIjD,MACrB,wCAAGyC,EAASY,UAAUZ,EAASoE,cAAcpE,EAASpF,OAGxD,MAFA4F,EAAMI,OAASZ,EAASY,OACxBJ,EAAM5F,IAAMoF,EAASpF,IACf4F,CACR,CAIA,IACE0D,QAAqBlE,EAASqE,MAChC,CAAE,MAAO7D,GAGP,MAFA6C,EAAc7C,EAAO,SAAUR,EAASY,QACxCyC,EAAc7C,EAAO,MAAOR,EAASpF,KAC/B4F,CACR,CAGA,IACEN,EAAOC,KAAKmE,MAAMJ,EACpB,CAAE,MAAO1D,GAIP,MAHA6C,EAAc7C,EAAO,OAAQ0D,GAC7Bb,EAAc7C,EAAO,SAAUR,EAASY,QACxCyC,EAAc7C,EAAO,MAAOR,EAASpF,KAC/B4F,CACR,CAEA,GAWsBtI,EAXFgI,GAYblF,OAAOuJ,UAAe,MAALrM,GAAsB,OAAjBsM,EAALtM,EAAOuM,sBAAe,EAAtBD,EAAwB/K,OAZrB,CACzB,MAAM+G,EAAa,IAAIjD,MAAM,4BAI7B,MAHAiD,EAAMN,KAAOA,EACbM,EAAMI,OAASZ,EAASY,OACxBJ,EAAM5F,IAAMoF,EAASpF,IACf4F,CACR,CAKF,IAAwBtI,EAAoEsM,EAH1F,OAAOtE,CACT,CCHAnE,EA7BA,YAWA4D,iBACE,MAAM+E,EAASxI,SAASR,iBAAiB,oBACzC,GAAsB,IAAlBgJ,EAAO9I,OACT,OAIF,MAAMkI,EAAS,mCACTC,EAAiB,2BAGjBY,QAvDRhF,eAAuCmE,EAAgBC,GACrD,MAAMa,EAAa,yBAEnB,IAAIzL,EAEJ,IACE,MAAMjB,EAAQ2M,eAAeC,QAAQF,GACrC,GAAI1M,IACFiB,EAASgH,KAAKmE,MAAMpM,GAChBiB,GACF,OAAOA,CAGb,CAAE,MAAOqH,GACP8B,QAAQyC,MAAMvE,EAChB,CAEA,MAAMN,QAAa2D,EAAkBC,EAAQC,GAAgB,GAE7D5K,EAAS,CACP+B,MAAOgF,EAAKhF,MAAM8J,WAClBvL,MAAOyG,EAAKuE,gBAAgBhL,OAG9B,IACEoL,eAAeI,QAAQL,EAAYzE,KAAKC,UAAUjH,GACpD,CAAE,MAAOqH,GACP8B,QAAQyC,MAAMvE,EAChB,CAEA,OAAOrH,CACT,CAwB6B+L,CAAwBpB,EAAQC,GAC3D,IAAK,MAAM3I,KAASsJ,EAClBtJ,EAAM7B,QAAQJ,OAAS,GAAGwL,EAAazJ,QACvCE,EAAM7B,QAAQE,MAAQ,GAAGkL,EAAalL,OAE1C,EAnBE0L,GAAO9C,MAAMC,QAAQyC,MACvB","sources":["webpack://lange/webpack/bootstrap","webpack://lange/webpack/runtime/make namespace object","webpack://lange/./sections/footer/trustpilot-badge/index.ts","webpack://lange/./sections/footer/yotpo-badge/index.ts","webpack://lange/./sections/footer/trustpilot-rating-stars/index.ts","webpack://lange/./lib/dom-ready.ts","webpack://lange/./sections/footer/country-selector-modal-cta.ts","webpack://lange/./lib/attentive-service.ts","webpack://lange/./sections/footer/marketing-subscribe.ts","webpack://lange/../node_modules/@integrabeauty/person/dist/src/email.js","webpack://lange/../node_modules/@integrabeauty/assert/src/index.ts","webpack://lange/./lib/decorate-error.ts","webpack://lange/./sections/footer/trustpilot-fetch-business-unit.ts","webpack://lange/./sections/footer/trustpilot-badge-init.ts"],"sourcesContent":["// The require scope\nlet __webpack_require__ = {};\n\n","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Renders a Trustpilot badge with the provided rating and total number of votes. This component is\n * designed to be used within the footer of the website, but it should not be used on the product\n * reviews page. The badge displays the overall rating, total votes, and a star rating based on the\n * provided data attributes.\n */\nclass TrustpilotBadge extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return ['data-rating', 'data-total'];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Overall rating\n     */\n    rating: string;\n\n    /**\n     * Total number of votes\n     */\n    total: string;\n  };\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.render();\n  }\n\n  public attributeChangedCallback(_name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (this.isConnected && oldValue !== newValue) {\n      this.render();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    const rating = Math.min(parseFloat(this.dataset.rating), 5);\n    if (isNaN(rating)) {\n      return;\n    }\n\n    const total = parseInt(this.dataset.total, 10);\n    if (!total) {\n      return;\n    }\n\n    // Show the element. The html starts it out hidden, and the global rule for the hidden attribute\n    // lives in an inlined stylesheet that comes down with the html, so there is no flash of the\n    // badge while this module loads.\n    this.hidden = false;\n\n    // we are modifying HTML inside of a slot, so there's no guarantee that any of the elements\n    // below exist\n\n    const scoreElement = this.querySelector('.trust-score');\n    if (scoreElement) {\n      scoreElement.textContent = rating.toString();\n    }\n\n    const totalElement = this.querySelector('.trust-total');\n    if (totalElement) {\n      const format = new Intl.NumberFormat('en-US');\n      totalElement.textContent = format.format(total);\n    }\n\n    const starsElement = this.querySelector('trustpilot-rating-stars');\n    if (starsElement) {\n      starsElement.dataset.rating = rating.toString();\n    }\n\n    const starPrefix = this.querySelector('.stars-prefix');\n    if (starPrefix) {\n      let prefix = '';\n      if (rating >= 5) {\n        prefix = 'Excellent';\n      } else if (rating >= 4) {\n        prefix = 'Great';\n      } else if (rating >= 3) {\n        prefix = 'Average';\n      } else if (rating >= 2) {\n        prefix = 'Poor';\n      } else {\n        prefix = 'Bad';\n      }\n\n      starPrefix.textContent = prefix;\n    }\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'trustpilot-badge': TrustpilotBadge;\n  }\n}\n\nif (!customElements.get('trustpilot-badge')) {\n  customElements.define('trustpilot-badge', TrustpilotBadge);\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Renders Yotpo site rating badge.\n */\nclass YotpoBadge extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return [\n      'data-total',\n      'data-average-score'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Overall rating\n     */\n    averageScore: string;\n\n    /**\n     * URL to badge image in the assets folder\n     */\n    badgeImageSrc: string;\n\n    /**\n     * Total number of votes\n     */\n    total: string;\n\n    /**\n     * URL where the badge click will lead to\n     */\n    url: string;\n  };\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.render();\n  }\n\n  public attributeChangedCallback(_name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (this.isConnected && oldValue !== newValue) {\n      this.render();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    const link = this.shadowRoot.querySelector('a');\n    link.href = this.dataset.url || '#';\n\n    const imageElement = this.shadowRoot.querySelector('img');\n    imageElement.src = this.dataset.badgeImageSrc;\n\n    const totalElement = this.shadowRoot.querySelector('.total');\n    const total = parseInt(this.dataset.total, 10) / 1000000;\n\n    const totalText = Number.isNaN(total) ? '' : total.toFixed(1);\n\n    totalElement.textContent = totalText ? totalText + 'M' : '';\n\n    const score = this.dataset.averageScore ? this.dataset.averageScore : '';\n\n    const badge = this.shadowRoot.querySelector('.badge');\n    if (score && totalText) {\n      badge?.setAttribute('aria-label', [\n        score,\n        'average score based on',\n        totalText,\n        'million certified reviews',\n        'powered by Yotpo and Trustpilot.'\n      ].join(' '));\n    } else {\n      badge?.setAttribute('aria-label', 'Certified reviews, powered by Yotpo and Trustpilot.');\n    }\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'yotpo-badge': YotpoBadge;\n  }\n}\n\nif (!customElements.get('yotpo-badge')) {\n  customElements.define('yotpo-badge', YotpoBadge);\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Renders a row of Trustpilot styled stars filled to a given rating, including a half filled star.\n *\n * It is the shared star visual wherever a Trustpilot rating is shown, such as the footer badge and\n * individual reviews.\n */\nclass TrustpilotRatingStars extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return ['data-rating'];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Present for a perfect rating, switching the stars to the darker fill via CSS.\n     */\n    dark?: string;\n\n    /**\n     * Rating, how many stars will be filled\n     */\n    rating: string;\n  };\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.render();\n  }\n\n  public attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (this.isConnected && name === 'data-rating' && oldValue !== newValue) {\n      this.render();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    let rating = parseFloat(this.dataset.rating);\n    rating = rating > 5 ? 5 : rating;\n\n    if (!rating) {\n      return;\n    }\n\n    if (rating === 5) {\n      this.dataset.dark = 'true';\n    }\n\n    const starsElement = this.shadowRoot.querySelectorAll<SVGGElement>('svg g');\n    for (let i = 0; i < starsElement.length; i++) {\n      if (i + 1 <= rating) {\n        starsElement[i].classList.add('filled');\n      }\n\n      if (i + 1.5 > rating) {\n        starsElement[i].classList.add('filled-half');\n        break;\n      }\n    }\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'trustpilot-rating-stars': TrustpilotRatingStars;\n  }\n}\n\nif (!customElements.get('trustpilot-rating-stars')) {\n  customElements.define('trustpilot-rating-stars', TrustpilotRatingStars);\n}\n","/**\n * Releases the handle returned from the ready() function.\n *\n * This is idempotent. Additional calls are pointless but harmless.\n */\nexport type ReleaseHandle = () => void;\n\n/**\n * A subset of events that pertain to document ready state.\n */\ntype LoadingEventMap = Pick<DocumentEventMap, 'DOMContentLoaded' | 'load'>;\n\n/**\n * Calls the provided callback when the document is ready. This works both when the document is\n * already ready and when it is not, so callers do not have to care. This exists because it ensures\n * that each caller consistently expresses its dependency on document readiness, and to save a bit\n * of boilerplate.\n *\n * A document is considered ready when it has been parsed. This corresponds to the ready state of\n * 'interactive'. When the document has been fully loaded, the ready state is 'complete'. The\n * callback runs either way, so callers do not have to check which one they are in.\n *\n * Ready means the html is parsed and the DOM is there to query. It does not mean the page's other\n * scripts have run. Deferred scripts and module scripts run after the ready state turns\n * 'interactive' and before DOMContentLoaded fires, so a callback here can run before them. When a\n * caller needs some other script to have run already, this is the wrong tool, and it should wait on\n * whatever that script produces instead.\n *\n * That remedy has a catch of its own when the calling bundle is loaded with async. An async bundle\n * can execute after DOMContentLoaded has already fired, so a plain listener for that event never\n * runs and the work silently never happens. Nothing here can detect that, because by the time this\n * file executes the event is long gone. Catching it needs something registered at parse time,\n * before any bundle loads.\n *\n * There is a second trap right next to that one, and it applies only when waiting for\n * DOMContentLoaded. The ready state stays 'interactive' after DOMContentLoaded has fired, all the\n * way until 'complete', so the ready state cannot tell you whether DOMContentLoaded is still coming\n * or has already gone. Waiting for load is not exposed to this, because the ready state turns\n * 'complete' and load fires in the same task, so seeing 'complete' means load has fired too.\n *\n * When the document is already ready, the callback is invoked via setTimeout. This way, if the\n * callback throws, the error surfaces to the global error handler in a way that is similar to what\n * happens when an event listener throws.\n *\n * We might one day want to move this to an NPM library.\n *\n * Beware of a quirk here. setTimeout yields, and the busier the frontend, the longer it takes to\n * fire. Page load is exactly when the frontend is busiest. The timeout is a floor and not a\n * guarantee, so there is no real bound on how long that yield lasts. What this means is that using\n * this library can introduce more delay than you might expect. Compare code that runs on async load\n * and checks ready state and invokes immediately to code that runs after setTimeout. The former\n * runs in the same tick, the latter runs in \"some later tick\". That later tick can be surprisingly\n * much later. That can cause initial paint issues.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event\n * @see https://html.spec.whatwg.org/multipage/parsing.html#the-end\n */\nexport function ready(callback: (event?: Event) => void,\n  type: keyof LoadingEventMap = 'DOMContentLoaded') {\n  if (document.readyState === 'complete' || (type === 'DOMContentLoaded' &&\n    document.readyState === 'interactive')) {\n    const timer = setTimeout(callback, 0);\n    const handle: ReleaseHandle = () => clearTimeout(timer);\n    return handle;\n  } else {\n    addEventListener(type, callback);\n    const handle: ReleaseHandle = () => removeEventListener(type, callback);\n    return handle;\n  }\n}\n","import { ready } from '../../lib/dom-ready.js';\n\n// The modal-element around the country selector handles the close button, the dismiss gestures, and\n// telling the modal manager when it opens and closes. We only open it.\n\ntype ModalElement = HTMLElementTagNameMap['modal-element'];\n\nfunction onReady() {\n  const links = document.querySelectorAll<HTMLAnchorElement>('.open-country-selector-modal');\n  for (const link of links) {\n    link.addEventListener('click', onShowSelectorClick);\n  }\n}\n\nfunction onShowSelectorClick(event: MouseEvent) {\n  event.preventDefault();\n\n  if (event.currentTarget instanceof HTMLElement) {\n    const focusEvent = new Event('focus-stack-push-requested', { bubbles: true, composed: true });\n    event.currentTarget.dispatchEvent(focusEvent);\n\n    const modal = <ModalElement>document.getElementById('country-selector-modal');\n    modal.dataset.open = 'true';\n  }\n}\n\nready(onReady);\n","export interface SubscribeInput {\n  email?: string;\n  first_name?: string;\n  phone?: string;\n  shopify_customer_id?: string;\n  sign_up_source_id: string;\n}\n\nexport interface RequestBody {\n  email?: string;\n  first_name?: string;\n  phone?: string;\n  shop_domain?: string;\n  shopify_customer_id?: string;\n  sign_up_source_id?: string;\n}\n\nexport type ResponseBody = Record<string, any>;\n\ninterface ErrorResponseBody {\n  error: string;\n}\n\n/**\n * This uses the no-store cache property because we never want this request to use the cache.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Request/cache\n */\nexport async function subscribe(input: SubscribeInput) {\n  const url = 'https://langehair.com/apps/attentive-relay/prod/v1/subscribe';\n\n  const requestBody: RequestBody = {\n    email: input.email,\n    first_name: input.first_name,\n    phone: input.phone,\n    shop_domain: 'lange-beauty.myshopify.com',\n    shopify_customer_id: input.shopify_customer_id,\n    sign_up_source_id: input.sign_up_source_id\n  };\n\n  let response;\n  try {\n    response = await fetch(url, {\n      body: JSON.stringify(requestBody),\n      cache: 'no-store',\n      headers: {\n        'Content-Type': 'application/json'\n      },\n      method: 'POST',\n      mode: 'cors'\n    });\n  } catch (error) {\n    const ae = new AttentiveError('There was a problem subscribing', { cause: error });\n    ae.request_body = requestBody;\n    ae.url = url;\n    throw ae;\n  }\n\n  // On success, we have a 204 No Content, so we do not attempt to fetch the response body. Calling\n  // await response.json() can throw for 204s. So we either have a non-204 indicating an error or we\n  // succeeded.\n\n  if (response.status !== 204) {\n    const error = new AttentiveError('There was a problem subscribing');\n    error.request_body = requestBody;\n\n    // If there is an issue in the backend subscribing, then we might have some information about\n    // the error in the response body, so we have to try to extract the message.\n\n    try {\n      const responseBody = await response.json();\n      error.response_body = responseBody;\n\n      if (isErrorResponseBody(responseBody) && typeof responseBody.error === 'string' &&\n        responseBody.error.length > 0) {\n        error.message = responseBody.error;\n      }\n    } catch (error) {\n      // ignore\n    }\n\n    error.status = response.status;\n    error.url = response.url;\n    throw error;\n  }\n}\n\nfunction isErrorResponseBody(value: any): value is ErrorResponseBody {\n  return typeof value?.error === 'string' && value.error.length > 0;\n}\n\nexport class AttentiveError extends Error {\n  public declare request_body: any;\n  public declare response_body: any;\n  public declare status: number;\n  public declare url: string;\n\n  constructor(message?: string, options?: ErrorOptions) {\n    super(message || 'Unknown', options);\n  }\n}\n","import { isValidEmailAddress } from '@integrabeauty/person';\nimport { AttentiveError, type SubscribeInput, subscribe } from '../../lib/attentive-service.js';\nimport { ready } from '../../lib/dom-ready.js';\n\ninterface RequestData {\n  email: string;\n  first_name: string;\n  phone: string | undefined;\n}\n\nfunction isValidPhone(phone: string) {\n  return /^[+]?(1\\s?)?((\\([0-9]{3}\\))|[0-9]{3})[\\s\\-]?[\\0-9]{3}[\\s\\-]?[0-9]{4}$/.test(phone);\n}\n\n// Each input sits in a field group that contains a single message element. These helpers toggle the\n// group's error/success state and message by walking up from the input, so the surrounding markup\n// is reached by structure rather than through per-element handles.\n\nfunction markFieldError(input: HTMLInputElement, message: string) {\n  const field = input.closest('.field-wrapper');\n  field.classList.add('error');\n  field.querySelector('.form-message').textContent = message;\n}\n\nfunction markFieldValid(input: HTMLInputElement) {\n  input.closest('.field-wrapper').classList.add('success');\n}\n\nfunction clearField(input: HTMLInputElement | null) {\n  if (input) {\n    const field = input.closest('.field-wrapper');\n    field.classList.remove('error', 'success');\n    field.querySelector('.form-message').textContent = '';\n  }\n}\n\n/**\n * Validates and submits the newsletter signup form\n */\nfunction onSubmit(event: SubmitEvent) {\n  event.preventDefault();\n\n  // In the theme editor a merchant is not actually subscribing, so do not submit the form. We do\n  // this check here instead of dynamically registering the listener so that we handle all submit\n  // events and ensure that we call preventDefault().\n\n  // This module expects that the Shopify global is correctly declared and initialized. We\n  // intentionally are not being defensive about its presence here.\n\n  if (Shopify.designMode) {\n    return;\n  }\n\n  const form = event.currentTarget;\n  if (!(form instanceof HTMLFormElement)) {\n    return;\n  }\n\n  const submitButton = form.querySelector('button');\n  submitButton.classList.add('submitting');\n  submitButton.textContent = 'Submitting...';\n\n  const emailInput = form.querySelector<HTMLInputElement>('#subscribe-email');\n  const nameInput = form.querySelector<HTMLInputElement>('#subscribe-name');\n  const phoneInput = form.querySelector<HTMLInputElement>('#subscribe-phone-number');\n\n  const requestData: RequestData = {\n    email: emailInput.value,\n    first_name: nameInput.value,\n    phone: phoneInput?.value\n  };\n\n  const nameError = !isValidPersonName(requestData.first_name);\n  const emailError = !isValidEmailAddress(requestData.email);\n\n  let phoneError = false;\n  if (requestData.phone) {\n    phoneError = !isValidPhone(requestData.phone);\n  }\n\n  const termsInputElement = form.querySelector<HTMLInputElement>('#subscribe-terms');\n\n  let termsError = false;\n  if (requestData.phone && termsInputElement) {\n    termsError = !termsInputElement.checked;\n  }\n\n  const customerId = form.querySelector<HTMLInputElement>('#customer-id').value;\n\n  if (nameError) {\n    markFieldError(nameInput, 'Please enter a name');\n  } else {\n    markFieldValid(nameInput);\n  }\n\n  if (emailError) {\n    markFieldError(emailInput, 'Please enter a valid email address');\n  } else {\n    markFieldValid(emailInput);\n  }\n\n  const termsWrapper = form.querySelector('#subscribe-terms-wrapper');\n  if (termsError) {\n    termsWrapper.classList.add('error');\n    termsWrapper.querySelector('.form-message').textContent =\n      'Please accept the Conditions and Terms above';\n  } else {\n    termsWrapper.classList.add('success');\n  }\n\n  if (phoneError) {\n    markFieldError(phoneInput, 'Please enter a valid phone number');\n  } else {\n    markFieldValid(phoneInput);\n  }\n\n  if (nameError || emailError || phoneError || termsError) {\n    submitButton.classList.remove('submitting');\n    submitButton.textContent = 'Submit';\n    return;\n  }\n\n  let currencyCode = 'USD';\n  const currencyCodeInput = form.querySelector<HTMLInputElement>('#currency-code');\n  if (currencyCodeInput) {\n    currencyCode = currencyCodeInput.value;\n  }\n\n  const resultElement = form.querySelector('#subscribe-response');\n  if (!resultElement) {\n    return;\n  }\n\n  const subscribeInput: SubscribeInput = {\n    email: requestData.email,\n    first_name: requestData.first_name,\n    phone: requestData.phone,\n    shopify_customer_id: customerId,\n    sign_up_source_id: '991550'\n  };\n\n  subscribe(subscribeInput)\n    .then(() => onSubscribeSuccess(form, requestData, currencyCode))\n    .catch((error) => onSubscribeError(error, form));\n}\n\nfunction isValidPersonName(name: string) {\n  // Some browsers do not tolerate using an empty space in the character class so we use \\s\n  // explicitly, even though it matches multiple kinds of spaces.\n  return /^[a-z\\s]{2,30}$/i.test(name);\n}\n\nfunction onSubscribeSuccess(form: HTMLFormElement, requestData: RequestData, currencyCode: string) {\n  const resultElement = form.querySelector('#subscribe-response');\n  resultElement.classList.remove('error');\n  resultElement.classList.add('success');\n  resultElement.textContent = 'Thank you for registering!';\n\n  const phoneInput = form.querySelector<HTMLInputElement>('#subscribe-phone-number');\n  if (phoneInput) {\n    phoneInput.value = '';\n  }\n\n  const termsInput = form.querySelector<HTMLInputElement>('#subscribe-terms');\n  if (termsInput) {\n    termsInput.checked = false;\n  }\n\n  const termsWrapper = form.querySelector<HTMLElement>('#subscribe-terms-wrapper');\n  if (termsWrapper) {\n    termsWrapper.hidden = true;\n    termsWrapper.classList.remove('success');\n  }\n\n  const inputs = form.querySelectorAll<HTMLInputElement>('input:not([type=\"hidden\"])');\n  for (const input of inputs) {\n    input.value = '';\n  }\n\n  const nameInput = form.querySelector<HTMLInputElement>('#subscribe-name');\n  clearField(nameInput);\n\n  const emailInput = form.querySelector<HTMLInputElement>('#subscribe-email');\n  clearField(emailInput);\n\n  clearField(phoneInput);\n\n  const submitButton = form.querySelector('button');\n  submitButton.classList.remove('submitting');\n  submitButton.classList.add('success');\n  submitButton.textContent = 'Success!';\n\n  dispatchIdentityCaptured(form, requestData);\n\n  type FormSubmitDetail = WindowEventMap['form-submit']['detail'];\n  const submitEvent = new CustomEvent<FormSubmitDetail>('form-submit', {\n    bubbles: true,\n    detail: {\n      currency_code: currencyCode,\n      email_provided: requestData.email ? true : false,\n      form_name: 'footer-subscribe',\n      form_location: location.href,\n      form_type: 'marketing-subscribe',\n      phone_provided: requestData.phone ? true : false\n    }\n  });\n  form.dispatchEvent(submitEvent);\n}\n\nfunction dispatchIdentityCaptured(form: HTMLFormElement, requestData: RequestData) {\n  if (!requestData.email && !requestData.phone) {\n    return;\n  }\n\n  type Detail = WindowEventMap['identity-captured']['detail'];\n  const detail: Detail = {};\n\n  // Although the input field is labelled first_name, the placeholder text suggests that it can\n  // contain the full name. To accommodate this, we will attempt to parse the first_name field into\n  // first and last name by splitting on space. If there is only one part, we will treat it as the\n  // first name. If there are two parts, we will treat the first part as the first name and the\n  // second part as the last name. If there are more than two parts, we treat the last part as the\n  // last name and the rest as the first name.\n\n  if (requestData.first_name) {\n    const parts = requestData.first_name.split(' ');\n    if (parts.length === 1) {\n      detail.first_name = parts[0];\n    } else if (parts.length === 2) {\n      detail.first_name = parts[0];\n      detail.last_name = parts[1];\n    } else {\n      detail.last_name = parts.pop();\n      detail.first_name = parts.join(' ');\n    }\n  }\n\n  if (requestData.email) {\n    detail.email = requestData.email;\n  }\n\n  if (requestData.phone) {\n    detail.phone = requestData.phone;\n  }\n\n  const event = new CustomEvent<Detail>('identity-captured', {\n    bubbles: true,\n    detail\n  });\n  form.dispatchEvent(event);\n}\n\nfunction onSubscribeError(error: unknown, form: HTMLFormElement) {\n  const resultElement = form.querySelector('#subscribe-response');\n  resultElement.classList.remove('success');\n  resultElement.classList.add('error');\n\n  const submitButton = form.querySelector('button');\n  submitButton.classList.remove('submitting');\n  submitButton.textContent = 'Submit';\n\n  if (error instanceof AttentiveError) {\n    resultElement.innerHTML = error.message ||\n      'There was a problem submitting your request, please try again later';\n  } else {\n    resultElement.innerHTML =\n      'There was a problem submitting your request, please try again later';\n    console.warn(error, 'Failed to subscribe in Attentive');\n  }\n\n  const termsWrapper = form.querySelector<HTMLElement>('#subscribe-terms-wrapper');\n  termsWrapper.hidden = true;\n}\n\nfunction onPhoneFocus(event: FocusEvent) {\n  if (event.currentTarget instanceof HTMLInputElement) {\n    clearField(event.currentTarget);\n\n    const terms = event.currentTarget.form.querySelector<HTMLElement>('#subscribe-terms-wrapper');\n    terms.hidden = false;\n    terms.querySelector('.form-message').textContent = '';\n  }\n}\n\nfunction onPhoneBlur(event: FocusEvent) {\n  if (event.currentTarget instanceof HTMLInputElement) {\n    const terms = event.currentTarget.form.querySelector<HTMLElement>('#subscribe-terms-wrapper');\n    terms.hidden = !event.currentTarget.value;\n  }\n}\n\nfunction onTermsChange(event: Event) {\n  if (event.target instanceof HTMLInputElement && event.target.checked) {\n    const terms = event.target.form.querySelector('#subscribe-terms-wrapper');\n    terms.classList.remove('error');\n    terms.querySelector('.form-message').textContent = '';\n  }\n}\n\nfunction onNameFocus(event: FocusEvent) {\n  if (event.currentTarget instanceof HTMLInputElement) {\n    clearField(event.currentTarget);\n  }\n}\n\nfunction onEmailFocus(event: FocusEvent) {\n  if (event.currentTarget instanceof HTMLInputElement) {\n    clearField(event.currentTarget);\n  }\n}\n\nfunction onReady() {\n  const terms = document.querySelector<HTMLInputElement>('#subscribe-terms');\n  terms?.addEventListener('change', onTermsChange);\n\n  const phone = document.querySelector<HTMLInputElement>('#subscribe-phone-number');\n  phone?.addEventListener('focus', onPhoneFocus);\n  phone?.addEventListener('blur', onPhoneBlur);\n\n  const form = document.querySelector<HTMLFormElement>('form[name=\"subscribe\"]');\n  form?.addEventListener('submit', onSubmit);\n\n  const submitButton = form?.querySelector('button');\n  if (submitButton) {\n    submitButton.disabled = false;\n  }\n\n  const name = document.querySelector<HTMLInputElement>('#subscribe-name');\n  name?.addEventListener('focus', onNameFocus);\n\n  const email = document.querySelector<HTMLInputElement>('#subscribe-email');\n  email?.addEventListener('focus', onEmailFocus);\n}\n\nready(onReady);\n","/**\n * Validates an email address.\n *\n * Based on the regex used by Meta's pixel.\n */\nexport function isValidEmailAddress(email) {\n    // eslint-disable-next-line @stylistic/max-len\n    return /^[\\w!#\\$%&\\'\\*\\+\\/\\=\\?\\^`\\{\\|\\}~\\-]+(:?\\.[\\w!#\\$%&\\'\\*\\+\\/\\=\\?\\^`\\{\\|\\}~\\-]+)*@(?:[a-z0-9](?:[a-z0-9\\-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9\\-]*[a-z0-9])?$/i.test(email);\n}\n","/**\n * Throws an assertion error if the given condition is falsy.\n *\n * @param expression any expression, although one that evaluates to true or false is preferred by\n * convention\n * @param message optional error text\n */\nexport function assert(expression: unknown, message?: string) {\n  if (!expression) {\n    throw new AssertionError(message);\n  }\n}\n\nexport class AssertionError extends Error {\n  constructor(message = 'Assertion error') {\n    super(message);\n    this.name = this.constructor.name;\n  }\n}\n","/**\n * Copies a single piece of supplementary context onto an error before it is thrown or reported.\n *\n * Use this when decorating a value the calling code did not construct, such as a caught value or an\n * error handed in by another module. An error the caller just built with new is always writable, so\n * plain assignment is fine there. A value from elsewhere may be a foreign object this code does not\n * own. Such a value can carry a non-writable property, such as a DOMException whose code is a read-\n * only accessor, or be frozen outright, and assigning to it then throws. Attaching context must\n * never itself fail, so a rejected assignment is swallowed and the remaining context is still\n * attempted. Guarding each property in isolation means a single read-only slot, the common case,\n * costs only that one property rather than all of them.\n *\n * In practice the value that triggers this is almost always a DOMException that reached the caller\n * from a fetch the browser cut short, such as an abort when an in-flight request is interrupted.\n * The platform defines a DOMException's code, name, and message as read-only attributes, so\n * assigning a code onto one throws under the strict mode that all bundled module code runs in. It\n * surfaces from Safari far more than elsewhere for two reasons. The reported wording, attempted to\n * assign to readonly property, is JavaScriptCore's phrasing for this failure, so the same bug in\n * another engine is worded differently and groups as a separate report, which makes this one look\n * Safari specific even though the read-only rule is not. And iOS Safari abandons in-flight fetches\n * aggressively when a tab is backgrounded or a connection drops, which is when such a DOMException\n * tends to be produced and handed to code that assumed an ordinary writable error.\n *\n * A swallowed failure is logged at a level low enough that it is recorded as a breadcrumb for\n * monitoring rather than escalated into its own captured event. That keeps the lost context visible\n * alongside the eventual report without adding fresh noise of its own. The breadcrumb names the\n * property that could not be attached, the kind of value that rejected it, and why, which is enough\n * to recognize a read-only or frozen caught value after the fact.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Read-only\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException\n * @see https://webidl.spec.whatwg.org/#dfn-read-only\n */\nexport function decorateError(error: any, key: string, value: unknown) {\n  try {\n    error[key] = value;\n  } catch (assignmentError) {\n    let reason: string;\n    if (assignmentError instanceof Error) {\n      reason = assignmentError.message;\n    } else {\n      reason = `${assignmentError}`;\n    }\n\n    console.log('Failed to assign error property', {\n      key,\n      name: error?.name,\n      tag: Object.prototype.toString.call(error),\n      reason\n    });\n  }\n}\n","import { assert } from '@integrabeauty/assert';\nimport type * as TrustPilotTypes from '@integrabeauty/trustpilot-types';\nimport { decorateError } from '../../lib/decorate-error.js';\n\n/**\n * Fetches public information about a business unit from TrustPilot's API.\n *\n * @param apiKey api key\n * @param businessUnitId business unit id\n * @param useCache whether to use Integra's own custom backend cache service in order to avoid the\n * unreliable TrustPilot API\n */\nexport async function fetchBusinessUnit(apiKey: string, businessUnitId: string, useCache = false) {\n  assert(apiKey, 'Invalid api key');\n  assert(businessUnitId, 'Invalid business unit id');\n\n  const path = useCache ?\n    '/apps/trustpilot-cache/lange-business-unit.json' :\n    `/apps/trustpilot-api/v1/business-units/${businessUnitId}`;\n\n  let headers: HeadersInit = {};\n  if (!useCache) {\n    headers = {\n      'Content-Type': 'application/json',\n      'apiKey': apiKey\n    };\n  }\n\n  let response;\n  try {\n    response = await fetch(path, { headers });\n  } catch (error) {\n    decorateError(error, 'url', path);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error('Trustpilot Site Rating Service Error ' +\n      `${response.status} ${response.statusText} ${response.url}`);\n    error.status = response.status;\n    error.url = response.url;\n    throw error;\n  }\n\n  // We are intentionally not using response.json() here because it obscures errors.\n  let responseText;\n  try {\n    responseText = await response.text();\n  } catch (error) {\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  let body;\n  try {\n    body = JSON.parse(responseText);\n  } catch (error) {\n    decorateError(error, 'body', responseText);\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  if (!isResponseBody(body)) {\n    const error: any = new Error('Unexpected response body');\n    error.body = body;\n    error.status = response.status;\n    error.url = response.url;\n    throw error;\n  }\n\n  return body;\n}\n\nfunction isResponseBody(value: any): value is TrustPilotTypes.FetchBusinessUnitResponseBody {\n  return Number.isInteger(value?.numberOfReviews?.total);\n}\n","import { ready } from '../../lib/dom-ready.js';\nimport { fetchBusinessUnit } from './trustpilot-fetch-business-unit.js';\n\ninterface CachedRating {\n  score: number;\n  total: number;\n}\n\nasync function fetchBusinessUnitCached(apiKey: string, businessUnitId: string) {\n  const storageKey = 'trustpilot_site_rating';\n\n  let rating: CachedRating;\n\n  try {\n    const value = sessionStorage.getItem(storageKey);\n    if (value) {\n      rating = JSON.parse(value);\n      if (rating) {\n        return rating;\n      }\n    }\n  } catch (error) {\n    console.debug(error);\n  }\n\n  const body = await fetchBusinessUnit(apiKey, businessUnitId, true);\n\n  rating = {\n    score: body.score.trustScore,\n    total: body.numberOfReviews.total\n  };\n\n  try {\n    sessionStorage.setItem(storageKey, JSON.stringify(rating));\n  } catch (error) {\n    console.debug(error);\n  }\n\n  return rating;\n}\n\nfunction onReady() {\n  // init calls out to fetch business unit and otherwise does nothing fallible. the only reason init\n  // fails is because of a failure to fetch. the failure to fetch here is not really indicative of a\n  // programming error. it is in fact routine to experience load failures because of networking\n  // problems (e.g. unreliable cellular data). if something were truly systemic it would be visually\n  // obvious. because the fetch errors are so routine, this does not capture the error at the error\n  // or warning level.\n\n  init().catch(console.debug);\n}\n\nasync function init() {\n  const badges = document.querySelectorAll('trustpilot-badge');\n  if (badges.length === 0) {\n    return;\n  }\n\n  // cspell:disable\n  const apiKey = 'zY69rTwFDPKLgqcVnM2Ai95NZ2V6NeAR';\n  const businessUnitId = '5ccc9833442b4b00014a6eb9';\n  // cspell:enable\n\n  const businessUnit = await fetchBusinessUnitCached(apiKey, businessUnitId);\n  for (const badge of badges) {\n    badge.dataset.rating = `${businessUnit.score}`;\n    badge.dataset.total = `${businessUnit.total}`;\n  }\n}\n\nready(onReady);\n"],"names":["__webpack_require__","exports","Object","defineProperty","Symbol","toStringTag","value","TrustpilotBadge","HTMLElement","observedAttributes","connectedCallback","this","initializeShadowRoot","render","attributeChangedCallback","_name","oldValue","newValue","isConnected","shadowRoot","attachShadow","mode","innerHTML","rating","Math","min","parseFloat","dataset","isNaN","total","parseInt","hidden","scoreElement","querySelector","textContent","toString","totalElement","format","Intl","NumberFormat","starsElement","starPrefix","prefix","customElements","get","define","YotpoBadge","href","url","src","badgeImageSrc","totalText","Number","toFixed","score","averageScore","badge","setAttribute","join","TrustpilotRatingStars","name","dark","querySelectorAll","i","length","classList","add","ready","callback","type","document","readyState","timer","setTimeout","clearTimeout","addEventListener","removeEventListener","onShowSelectorClick","event","preventDefault","currentTarget","focusEvent","Event","bubbles","composed","dispatchEvent","getElementById","open","links","link","AttentiveError","Error","constructor","message","options","super","markFieldError","input","field","closest","markFieldValid","clearField","remove","onSubmit","Shopify","designMode","form","HTMLFormElement","submitButton","emailInput","nameInput","phoneInput","requestData","email","first_name","phone","nameError","test","emailError","phoneError","termsInputElement","termsError","checked","customerId","termsWrapper","currencyCode","currencyCodeInput","async","requestBody","shop_domain","shopify_customer_id","sign_up_source_id","response","fetch","body","JSON","stringify","cache","headers","method","error","ae","cause","request_body","status","responseBody","json","response_body","subscribe","then","resultElement","termsInput","inputs","detail","parts","split","last_name","pop","CustomEvent","dispatchIdentityCaptured","submitEvent","currency_code","email_provided","form_name","form_location","location","form_type","phone_provided","onSubscribeSuccess","catch","console","warn","onSubscribeError","onPhoneFocus","HTMLInputElement","terms","onPhoneBlur","onTermsChange","target","onNameFocus","onEmailFocus","assert","expression","AssertionError","disabled","decorateError","key","assignmentError","reason","log","tag","prototype","call","fetchBusinessUnit","apiKey","businessUnitId","useCache","path","responseText","ok","statusText","text","parse","isInteger","_value$numberOfReview","numberOfReviews","badges","businessUnit","storageKey","sessionStorage","getItem","debug","trustScore","setItem","fetchBusinessUnitCached","init"],"sourceRoot":""}