From b8cf8a62747edb3333b7002bcf25861a261c518a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Van=20Canneyt?= Date: Wed, 7 Sep 2022 21:06:10 +0200 Subject: [PATCH] * WebAssembly thread support --- demo/wasienv/threads/bulma.min.css | 1 + demo/wasienv/threads/demothreads.lpi | 103 +++ demo/wasienv/threads/demothreads.lpr | 73 ++ demo/wasienv/threads/index.html | 53 ++ demo/wasienv/threads/threadapp.lpi | 68 ++ demo/wasienv/threads/threadapp.lpr | 50 ++ demo/wasienv/threads/wasmthreads.pp | 927 +++++++++++++++++++++++++ packages/rtl/rtl.webthreads.pas | 477 +++++++++++++ packages/rtl/webassembly.pas | 10 +- packages/wasi/pas2jsthreadworker.lpi | 77 ++ packages/wasi/pas2jsthreadworker.pas | 22 + packages/wasi/wasienv.pas | 302 ++++++-- packages/wasi/wasihostapp.pas | 35 +- packages/wasi/wasithreadedapp.pas | 613 ++++++++++++++++ packages/wasi/wasiworkerthreadhost.pas | 663 ++++++++++++++++++ 15 files changed, 3423 insertions(+), 51 deletions(-) create mode 100644 demo/wasienv/threads/bulma.min.css create mode 100644 demo/wasienv/threads/demothreads.lpi create mode 100644 demo/wasienv/threads/demothreads.lpr create mode 100644 demo/wasienv/threads/index.html create mode 100644 demo/wasienv/threads/threadapp.lpi create mode 100644 demo/wasienv/threads/threadapp.lpr create mode 100644 demo/wasienv/threads/wasmthreads.pp create mode 100644 packages/rtl/rtl.webthreads.pas create mode 100644 packages/wasi/pas2jsthreadworker.lpi create mode 100644 packages/wasi/pas2jsthreadworker.pas create mode 100644 packages/wasi/wasithreadedapp.pas create mode 100644 packages/wasi/wasiworkerthreadhost.pas diff --git a/demo/wasienv/threads/bulma.min.css b/demo/wasienv/threads/bulma.min.css new file mode 100644 index 0000000..be16f72 --- /dev/null +++ b/demo/wasienv/threads/bulma.min.css @@ -0,0 +1 @@ +/*! bulma.io v0.9.3 | MIT License | github.com/jgthms/bulma */.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.5em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.5em - 1px);padding-left:calc(.75em - 1px);padding-right:calc(.75em - 1px);padding-top:calc(.5em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{outline:0}.button[disabled],.file-cta[disabled],.file-name[disabled],.input[disabled],.pagination-ellipsis[disabled],.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled],.select fieldset[disabled] select,.select select[disabled],.textarea[disabled],fieldset[disabled] .button,fieldset[disabled] .file-cta,fieldset[disabled] .file-name,fieldset[disabled] .input,fieldset[disabled] .pagination-ellipsis,fieldset[disabled] .pagination-link,fieldset[disabled] .pagination-next,fieldset[disabled] .pagination-previous,fieldset[disabled] .select select,fieldset[disabled] .textarea{cursor:not-allowed}.breadcrumb,.button,.file,.is-unselectable,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:not(.is-arrowless)::after,.select:not(.is-multiple):not(.is-loading)::after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.level:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.pagination:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,.2);border:none;border-radius:9999px;cursor:pointer;pointer-events:auto;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:0;position:relative;vertical-align:top;width:20px}.delete::after,.delete::before,.modal-close::after,.modal-close::before{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.delete::before,.modal-close::before{height:2px;width:50%}.delete::after,.modal-close::after{height:50%;width:2px}.delete:focus,.delete:hover,.modal-close:focus,.modal-close:hover{background-color:rgba(10,10,10,.3)}.delete:active,.modal-close:active{background-color:rgba(10,10,10,.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.button.is-loading::after,.control.is-loading::after,.loader,.select.is-loading::after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:9999px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.hero-video,.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img,.is-overlay,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.navbar-burger{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:0 0;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0}/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,::after,::before{box-sizing:inherit}img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:inherit}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,optgroup,select,textarea{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1em;font-weight:400;line-height:1.5}a{color:#485fc7;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:#f5f5f5;color:#da1039;font-size:.875em;font-weight:400;padding:.25em .5em .25em}hr{background-color:#f5f5f5;border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}fieldset{border:none}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{vertical-align:top}table td:not([align]),table th:not([align]){text-align:inherit}table th{color:#363636}@-webkit-keyframes spinAround{from{transform:rotate(0)}to{transform:rotate(359deg)}}@keyframes spinAround{from{transform:rotate(0)}to{transform:rotate(359deg)}}.box{background-color:#fff;border-radius:6px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px #485fc7}a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2),0 0 0 1px #485fc7}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding-bottom:calc(.5em - 1px);padding-left:1em;padding-right:1em;padding-top:calc(.5em - 1px);text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.5em - 1px);margin-right:.25em}.button .icon:last-child:not(:first-child){margin-left:.25em;margin-right:calc(-.5em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.5em - 1px);margin-right:calc(-.5em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#485fc7;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled],fieldset[disabled] .button.is-text{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-ghost{background:0 0;border-color:transparent;color:#485fc7;text-decoration:none}.button.is-ghost.is-hovered,.button.is-ghost:hover{color:#485fc7;text-decoration:underline}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled],fieldset[disabled] .button.is-white{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-hovered,.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled],fieldset[disabled] .button.is-white.is-inverted{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined.is-focused,.button.is-white.is-outlined.is-hovered,.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined.is-loading.is-focused::after,.button.is-white.is-outlined.is-loading.is-hovered::after,.button.is-white.is-outlined.is-loading:focus::after,.button.is-white.is-outlined.is-loading:hover::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined[disabled],fieldset[disabled] .button.is-white.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined.is-focused,.button.is-white.is-inverted.is-outlined.is-hovered,.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-white.is-inverted.is-outlined.is-loading:focus::after,.button.is-white.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled],fieldset[disabled] .button.is-black{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-hovered,.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled],fieldset[disabled] .button.is-black.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined.is-focused,.button.is-black.is-outlined.is-hovered,.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined.is-loading.is-focused::after,.button.is-black.is-outlined.is-loading.is-hovered::after,.button.is-black.is-outlined.is-loading:focus::after,.button.is-black.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined[disabled],fieldset[disabled] .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined.is-focused,.button.is-black.is-inverted.is-outlined.is-hovered,.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-black.is-inverted.is-outlined.is-loading:focus::after,.button.is-black.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light[disabled],fieldset[disabled] .button.is-light{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:rgba(0,0,0,.7);color:#f5f5f5}.button.is-light.is-inverted.is-hovered,.button.is-light.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-light.is-inverted[disabled],fieldset[disabled] .button.is-light.is-inverted{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined.is-focused,.button.is-light.is-outlined.is-hovered,.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,.7)}.button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined.is-loading.is-focused::after,.button.is-light.is-outlined.is-loading.is-hovered::after,.button.is-light.is-outlined.is-loading:focus::after,.button.is-light.is-outlined.is-loading:hover::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-light.is-outlined[disabled],fieldset[disabled] .button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-light.is-inverted.is-outlined.is-focused,.button.is-light.is-inverted.is-outlined.is-hovered,.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#f5f5f5}.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-light.is-inverted.is-outlined.is-loading:focus::after,.button.is-light.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-dark{background-color:#363636;border-color:transparent;color:#fff}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#fff}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#fff}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#fff}.button.is-dark[disabled],fieldset[disabled] .button.is-dark{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#fff;color:#363636}.button.is-dark.is-inverted.is-hovered,.button.is-dark.is-inverted:hover{background-color:#f2f2f2}.button.is-dark.is-inverted[disabled],fieldset[disabled] .button.is-dark.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined.is-focused,.button.is-dark.is-outlined.is-hovered,.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#fff}.button.is-dark.is-outlined.is-loading::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined.is-loading.is-focused::after,.button.is-dark.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-outlined.is-loading:focus::after,.button.is-dark.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-dark.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-dark.is-inverted.is-outlined.is-focused,.button.is-dark.is-inverted.is-outlined.is-hovered,.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#fff;color:#363636}.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-inverted.is-outlined.is-loading:focus::after,.button.is-dark.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled],fieldset[disabled] .button.is-primary{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-hovered,.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled],fieldset[disabled] .button.is-primary.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined.is-focused,.button.is-primary.is-outlined.is-hovered,.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined.is-loading.is-focused::after,.button.is-primary.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-outlined.is-loading:focus::after,.button.is-primary.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined.is-focused,.button.is-primary.is-inverted.is-outlined.is-hovered,.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-inverted.is-outlined.is-loading:focus::after,.button.is-primary.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary.is-light{background-color:#ebfffc;color:#00947e}.button.is-primary.is-light.is-hovered,.button.is-primary.is-light:hover{background-color:#defffa;border-color:transparent;color:#00947e}.button.is-primary.is-light.is-active,.button.is-primary.is-light:active{background-color:#d1fff8;border-color:transparent;color:#00947e}.button.is-link{background-color:#485fc7;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#3e56c4;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#3a51bb;border-color:transparent;color:#fff}.button.is-link[disabled],fieldset[disabled] .button.is-link{background-color:#485fc7;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#485fc7}.button.is-link.is-inverted.is-hovered,.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled],fieldset[disabled] .button.is-link.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#485fc7}.button.is-link.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#485fc7;color:#485fc7}.button.is-link.is-outlined.is-focused,.button.is-link.is-outlined.is-hovered,.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#485fc7;border-color:#485fc7;color:#fff}.button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #485fc7 #485fc7!important}.button.is-link.is-outlined.is-loading.is-focused::after,.button.is-link.is-outlined.is-loading.is-hovered::after,.button.is-link.is-outlined.is-loading:focus::after,.button.is-link.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined[disabled],fieldset[disabled] .button.is-link.is-outlined{background-color:transparent;border-color:#485fc7;box-shadow:none;color:#485fc7}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined.is-focused,.button.is-link.is-inverted.is-outlined.is-hovered,.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#485fc7}.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-link.is-inverted.is-outlined.is-loading:focus::after,.button.is-link.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #485fc7 #485fc7!important}.button.is-link.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link.is-light{background-color:#eff1fa;color:#3850b7}.button.is-link.is-light.is-hovered,.button.is-link.is-light:hover{background-color:#e6e9f7;border-color:transparent;color:#3850b7}.button.is-link.is-light.is-active,.button.is-link.is-light:active{background-color:#dce0f4;border-color:transparent;color:#3850b7}.button.is-info{background-color:#3e8ed0;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#3488ce;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(62,142,208,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#3082c5;border-color:transparent;color:#fff}.button.is-info[disabled],fieldset[disabled] .button.is-info{background-color:#3e8ed0;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#3e8ed0}.button.is-info.is-inverted.is-hovered,.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled],fieldset[disabled] .button.is-info.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#3e8ed0}.button.is-info.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#3e8ed0;color:#3e8ed0}.button.is-info.is-outlined.is-focused,.button.is-info.is-outlined.is-hovered,.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#3e8ed0;border-color:#3e8ed0;color:#fff}.button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #3e8ed0 #3e8ed0!important}.button.is-info.is-outlined.is-loading.is-focused::after,.button.is-info.is-outlined.is-loading.is-hovered::after,.button.is-info.is-outlined.is-loading:focus::after,.button.is-info.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined[disabled],fieldset[disabled] .button.is-info.is-outlined{background-color:transparent;border-color:#3e8ed0;box-shadow:none;color:#3e8ed0}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined.is-focused,.button.is-info.is-inverted.is-outlined.is-hovered,.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#3e8ed0}.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-info.is-inverted.is-outlined.is-loading:focus::after,.button.is-info.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #3e8ed0 #3e8ed0!important}.button.is-info.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info.is-light{background-color:#eff5fb;color:#296fa8}.button.is-info.is-light.is-hovered,.button.is-info.is-light:hover{background-color:#e4eff9;border-color:transparent;color:#296fa8}.button.is-info.is-light.is-active,.button.is-info.is-light:active{background-color:#dae9f6;border-color:transparent;color:#296fa8}.button.is-success{background-color:#48c78e;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#3ec487;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(72,199,142,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#3abb81;border-color:transparent;color:#fff}.button.is-success[disabled],fieldset[disabled] .button.is-success{background-color:#48c78e;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#48c78e}.button.is-success.is-inverted.is-hovered,.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled],fieldset[disabled] .button.is-success.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#48c78e}.button.is-success.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#48c78e;color:#48c78e}.button.is-success.is-outlined.is-focused,.button.is-success.is-outlined.is-hovered,.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#48c78e;border-color:#48c78e;color:#fff}.button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #48c78e #48c78e!important}.button.is-success.is-outlined.is-loading.is-focused::after,.button.is-success.is-outlined.is-loading.is-hovered::after,.button.is-success.is-outlined.is-loading:focus::after,.button.is-success.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined[disabled],fieldset[disabled] .button.is-success.is-outlined{background-color:transparent;border-color:#48c78e;box-shadow:none;color:#48c78e}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined.is-focused,.button.is-success.is-inverted.is-outlined.is-hovered,.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#48c78e}.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-success.is-inverted.is-outlined.is-loading:focus::after,.button.is-success.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #48c78e #48c78e!important}.button.is-success.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success.is-light{background-color:#effaf5;color:#257953}.button.is-success.is-light.is-hovered,.button.is-success.is-light:hover{background-color:#e6f7ef;border-color:transparent;color:#257953}.button.is-success.is-light.is-active,.button.is-success.is-light:active{background-color:#dcf4e9;border-color:transparent;color:#257953}.button.is-warning{background-color:#ffe08a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdc7d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,224,138,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd970;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled],fieldset[disabled] .button.is-warning{background-color:#ffe08a;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);color:#ffe08a}.button.is-warning.is-inverted.is-hovered,.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled],fieldset[disabled] .button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffe08a}.button.is-warning.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffe08a;color:#ffe08a}.button.is-warning.is-outlined.is-focused,.button.is-warning.is-outlined.is-hovered,.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffe08a;border-color:#ffe08a;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ffe08a #ffe08a!important}.button.is-warning.is-outlined.is-loading.is-focused::after,.button.is-warning.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-outlined.is-loading:focus::after,.button.is-warning.is-outlined.is-loading:hover::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-outlined{background-color:transparent;border-color:#ffe08a;box-shadow:none;color:#ffe08a}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined.is-focused,.button.is-warning.is-inverted.is-outlined.is-hovered,.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffe08a}.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-inverted.is-outlined.is-loading:focus::after,.button.is-warning.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #ffe08a #ffe08a!important}.button.is-warning.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-warning.is-light{background-color:#fffaeb;color:#946c00}.button.is-warning.is-light.is-hovered,.button.is-warning.is-light:hover{background-color:#fff6de;border-color:transparent;color:#946c00}.button.is-warning.is-light.is-active,.button.is-warning.is-light:active{background-color:#fff3d1;border-color:transparent;color:#946c00}.button.is-danger{background-color:#f14668;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#f03a5f;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ef2e55;border-color:transparent;color:#fff}.button.is-danger[disabled],fieldset[disabled] .button.is-danger{background-color:#f14668;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#f14668}.button.is-danger.is-inverted.is-hovered,.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled],fieldset[disabled] .button.is-danger.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#f14668}.button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#f14668;color:#f14668}.button.is-danger.is-outlined.is-focused,.button.is-danger.is-outlined.is-hovered,.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#f14668;border-color:#f14668;color:#fff}.button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #f14668 #f14668!important}.button.is-danger.is-outlined.is-loading.is-focused::after,.button.is-danger.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-outlined.is-loading:focus::after,.button.is-danger.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-outlined{background-color:transparent;border-color:#f14668;box-shadow:none;color:#f14668}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined.is-focused,.button.is-danger.is-inverted.is-outlined.is-hovered,.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#f14668}.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-inverted.is-outlined.is-loading:focus::after,.button.is-danger.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #f14668 #f14668!important}.button.is-danger.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-danger.is-light{background-color:#feecf0;color:#cc0f35}.button.is-danger.is-light.is-hovered,.button.is-danger.is-light:hover{background-color:#fde0e6;border-color:transparent;color:#cc0f35}.button.is-danger.is-light.is-active,.button.is-danger.is-light:active{background-color:#fcd4dc;border-color:transparent;color:#cc0f35}.button.is-small{font-size:.75rem}.button.is-small:not(.is-rounded){border-radius:2px}.button.is-normal{font-size:1rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled],fieldset[disabled] .button{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading::after{position:absolute;left:calc(50% - (1em * .5));top:calc(50% - (1em * .5));position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:9999px;padding-left:calc(1em + .25em);padding-right:calc(1em + .25em)}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child):not(.is-fullwidth){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large){font-size:.75rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large):not(.is-rounded){border-radius:2px}.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large){font-size:1.25rem}.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium){font-size:1.5rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1;flex-shrink:1}.buttons.is-centered{justify-content:center}.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth){margin-left:.25rem;margin-right:.25rem}.buttons.is-right{justify-content:flex-end}.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth){margin-left:.25rem;margin-right:.25rem}.container{flex-grow:1;margin:0 auto;position:relative;width:auto}.container.is-fluid{max-width:none!important;padding-left:32px;padding-right:32px;width:100%}@media screen and (min-width:1024px){.container{max-width:960px}}@media screen and (max-width:1215px){.container.is-widescreen:not(.is-max-desktop){max-width:1152px}}@media screen and (max-width:1407px){.container.is-fullhd:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}@media screen and (min-width:1216px){.container:not(.is-max-desktop){max-width:1152px}}@media screen and (min-width:1408px){.container:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style-position:outside;margin-left:2em;margin-top:1em}.content ol:not([type]){list-style-type:decimal}.content ol:not([type]).is-lower-alpha{list-style-type:lower-alpha}.content ol:not([type]).is-lower-roman{list-style-type:lower-roman}.content ol:not([type]).is-upper-alpha{list-style-type:upper-alpha}.content ol:not([type]).is-upper-roman{list-style-type:upper-roman}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636}.content table th:not([align]){text-align:inherit}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content .tabs li+li{margin-top:0}.content.is-small{font-size:.75rem}.content.is-normal{font-size:1rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.icon-text{align-items:flex-start;color:inherit;display:inline-flex;flex-wrap:wrap;line-height:1.5rem;vertical-align:top}.icon-text .icon{flex-grow:0;flex-shrink:0}.icon-text .icon:not(:last-child){margin-right:.25em}.icon-text .icon:not(:first-child){margin-left:.25em}div.icon-text{display:flex}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:9999px}.image.is-fullwidth{width:100%}.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;position:relative;padding:1.25rem 2.5rem 1.25rem 1.5rem}.notification a:not(.button):not(.dropdown-item){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:0 0}.notification>.delete{right:.5rem;position:absolute;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.notification.is-dark{background-color:#363636;color:#fff}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-primary.is-light{background-color:#ebfffc;color:#00947e}.notification.is-link{background-color:#485fc7;color:#fff}.notification.is-link.is-light{background-color:#eff1fa;color:#3850b7}.notification.is-info{background-color:#3e8ed0;color:#fff}.notification.is-info.is-light{background-color:#eff5fb;color:#296fa8}.notification.is-success{background-color:#48c78e;color:#fff}.notification.is-success.is-light{background-color:#effaf5;color:#257953}.notification.is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.notification.is-warning.is-light{background-color:#fffaeb;color:#946c00}.notification.is-danger{background-color:#f14668;color:#fff}.notification.is-danger.is-light{background-color:#feecf0;color:#cc0f35}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:9999px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#ededed}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-white:indeterminate{background-image:linear-gradient(to right,#fff 30%,#ededed 30%)}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-black:indeterminate{background-image:linear-gradient(to right,#0a0a0a 30%,#ededed 30%)}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-light:indeterminate{background-image:linear-gradient(to right,#f5f5f5 30%,#ededed 30%)}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-dark:indeterminate{background-image:linear-gradient(to right,#363636 30%,#ededed 30%)}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-primary:indeterminate{background-image:linear-gradient(to right,#00d1b2 30%,#ededed 30%)}.progress.is-link::-webkit-progress-value{background-color:#485fc7}.progress.is-link::-moz-progress-bar{background-color:#485fc7}.progress.is-link::-ms-fill{background-color:#485fc7}.progress.is-link:indeterminate{background-image:linear-gradient(to right,#485fc7 30%,#ededed 30%)}.progress.is-info::-webkit-progress-value{background-color:#3e8ed0}.progress.is-info::-moz-progress-bar{background-color:#3e8ed0}.progress.is-info::-ms-fill{background-color:#3e8ed0}.progress.is-info:indeterminate{background-image:linear-gradient(to right,#3e8ed0 30%,#ededed 30%)}.progress.is-success::-webkit-progress-value{background-color:#48c78e}.progress.is-success::-moz-progress-bar{background-color:#48c78e}.progress.is-success::-ms-fill{background-color:#48c78e}.progress.is-success:indeterminate{background-image:linear-gradient(to right,#48c78e 30%,#ededed 30%)}.progress.is-warning::-webkit-progress-value{background-color:#ffe08a}.progress.is-warning::-moz-progress-bar{background-color:#ffe08a}.progress.is-warning::-ms-fill{background-color:#ffe08a}.progress.is-warning:indeterminate{background-image:linear-gradient(to right,#ffe08a 30%,#ededed 30%)}.progress.is-danger::-webkit-progress-value{background-color:#f14668}.progress.is-danger::-moz-progress-bar{background-color:#f14668}.progress.is-danger::-ms-fill{background-color:#f14668}.progress.is-danger:indeterminate{background-image:linear-gradient(to right,#f14668 30%,#ededed 30%)}.progress:indeterminate{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:moveIndeterminate;animation-name:moveIndeterminate;-webkit-animation-timing-function:linear;animation-timing-function:linear;background-color:#ededed;background-image:linear-gradient(to right,#4a4a4a 30%,#ededed 30%);background-position:top left;background-repeat:no-repeat;background-size:150% 150%}.progress:indeterminate::-webkit-progress-bar{background-color:transparent}.progress:indeterminate::-moz-progress-bar{background-color:transparent}.progress:indeterminate::-ms-fill{animation-name:none}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}@-webkit-keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}@keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,.7)}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#fff}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#485fc7;border-color:#485fc7;color:#fff}.table td.is-info,.table th.is-info{background-color:#3e8ed0;border-color:#3e8ed0;color:#fff}.table td.is-success,.table th.is-success{background-color:#48c78e;border-color:#48c78e;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffe08a;border-color:#ffe08a;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#f14668;border-color:#f14668;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table td.is-vcentered,.table th.is-vcentered{vertical-align:middle}.table th{color:#363636}.table th:not([align]){text-align:inherit}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead{background-color:transparent}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot{background-color:transparent}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody{background-color:transparent}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even){background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.are-medium .tag:not(.is-normal):not(.is-large){font-size:1rem}.tags.are-large .tag:not(.is-normal):not(.is-medium){font-size:1.25rem}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.tags.has-addons .tag:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.tag:not(body).is-dark{background-color:#363636;color:#fff}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-primary.is-light{background-color:#ebfffc;color:#00947e}.tag:not(body).is-link{background-color:#485fc7;color:#fff}.tag:not(body).is-link.is-light{background-color:#eff1fa;color:#3850b7}.tag:not(body).is-info{background-color:#3e8ed0;color:#fff}.tag:not(body).is-info.is-light{background-color:#eff5fb;color:#296fa8}.tag:not(body).is-success{background-color:#48c78e;color:#fff}.tag:not(body).is-success.is-light{background-color:#effaf5;color:#257953}.tag:not(body).is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.tag:not(body).is-warning.is-light{background-color:#fffaeb;color:#946c00}.tag:not(body).is-danger{background-color:#f14668;color:#fff}.tag:not(body).is-danger.is-light{background-color:#feecf0;color:#cc0f35}.tag:not(body).is-normal{font-size:.75rem}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete::after,.tag:not(body).is-delete::before{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.tag:not(body).is-delete::before{height:1px;width:50%}.tag:not(body).is-delete::after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:9999px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.title sub{font-size:.75em}.subtitle sup,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.number{align-items:center;background-color:#f5f5f5;border-radius:9999px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.input,.select select,.textarea{background-color:#fff;border-color:#dbdbdb;border-radius:4px;color:#363636}.input::-moz-placeholder,.select select::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.select select::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.select select:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.select select:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input:hover,.is-hovered.input,.is-hovered.textarea,.select select.is-hovered,.select select:hover,.textarea:hover{border-color:#b5b5b5}.input:active,.input:focus,.is-active.input,.is-active.textarea,.is-focused.input,.is-focused.textarea,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{border-color:#485fc7;box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.input[disabled],.select fieldset[disabled] select,.select select[disabled],.textarea[disabled],fieldset[disabled] .input,fieldset[disabled] .select select,fieldset[disabled] .textarea{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.select fieldset[disabled] select::-moz-placeholder,.select select[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder,fieldset[disabled] .input::-moz-placeholder,fieldset[disabled] .select select::-moz-placeholder,fieldset[disabled] .textarea::-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]::-webkit-input-placeholder,.select fieldset[disabled] select::-webkit-input-placeholder,.select select[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder,fieldset[disabled] .input::-webkit-input-placeholder,fieldset[disabled] .select select::-webkit-input-placeholder,fieldset[disabled] .textarea::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-moz-placeholder,.select fieldset[disabled] select:-moz-placeholder,.select select[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder,fieldset[disabled] .input:-moz-placeholder,fieldset[disabled] .select select:-moz-placeholder,fieldset[disabled] .textarea:-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-ms-input-placeholder,.select fieldset[disabled] select:-ms-input-placeholder,.select select[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder,fieldset[disabled] .input:-ms-input-placeholder,fieldset[disabled] .select select:-ms-input-placeholder,fieldset[disabled] .textarea:-ms-input-placeholder{color:rgba(122,122,122,.3)}.input,.textarea{box-shadow:inset 0 .0625em .125em rgba(10,10,10,.05);max-width:100%;width:100%}.input[readonly],.textarea[readonly]{box-shadow:none}.is-white.input,.is-white.textarea{border-color:#fff}.is-white.input:active,.is-white.input:focus,.is-white.is-active.input,.is-white.is-active.textarea,.is-white.is-focused.input,.is-white.is-focused.textarea,.is-white.textarea:active,.is-white.textarea:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.is-black.input,.is-black.textarea{border-color:#0a0a0a}.is-black.input:active,.is-black.input:focus,.is-black.is-active.input,.is-black.is-active.textarea,.is-black.is-focused.input,.is-black.is-focused.textarea,.is-black.textarea:active,.is-black.textarea:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.is-light.input,.is-light.textarea{border-color:#f5f5f5}.is-light.input:active,.is-light.input:focus,.is-light.is-active.input,.is-light.is-active.textarea,.is-light.is-focused.input,.is-light.is-focused.textarea,.is-light.textarea:active,.is-light.textarea:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.is-dark.input,.is-dark.textarea{border-color:#363636}.is-dark.input:active,.is-dark.input:focus,.is-dark.is-active.input,.is-dark.is-active.textarea,.is-dark.is-focused.input,.is-dark.is-focused.textarea,.is-dark.textarea:active,.is-dark.textarea:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.is-primary.input,.is-primary.textarea{border-color:#00d1b2}.is-primary.input:active,.is-primary.input:focus,.is-primary.is-active.input,.is-primary.is-active.textarea,.is-primary.is-focused.input,.is-primary.is-focused.textarea,.is-primary.textarea:active,.is-primary.textarea:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.is-link.input,.is-link.textarea{border-color:#485fc7}.is-link.input:active,.is-link.input:focus,.is-link.is-active.input,.is-link.is-active.textarea,.is-link.is-focused.input,.is-link.is-focused.textarea,.is-link.textarea:active,.is-link.textarea:focus{box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.is-info.input,.is-info.textarea{border-color:#3e8ed0}.is-info.input:active,.is-info.input:focus,.is-info.is-active.input,.is-info.is-active.textarea,.is-info.is-focused.input,.is-info.is-focused.textarea,.is-info.textarea:active,.is-info.textarea:focus{box-shadow:0 0 0 .125em rgba(62,142,208,.25)}.is-success.input,.is-success.textarea{border-color:#48c78e}.is-success.input:active,.is-success.input:focus,.is-success.is-active.input,.is-success.is-active.textarea,.is-success.is-focused.input,.is-success.is-focused.textarea,.is-success.textarea:active,.is-success.textarea:focus{box-shadow:0 0 0 .125em rgba(72,199,142,.25)}.is-warning.input,.is-warning.textarea{border-color:#ffe08a}.is-warning.input:active,.is-warning.input:focus,.is-warning.is-active.input,.is-warning.is-active.textarea,.is-warning.is-focused.input,.is-warning.is-focused.textarea,.is-warning.textarea:active,.is-warning.textarea:focus{box-shadow:0 0 0 .125em rgba(255,224,138,.25)}.is-danger.input,.is-danger.textarea{border-color:#f14668}.is-danger.input:active,.is-danger.input:focus,.is-danger.is-active.input,.is-danger.is-active.textarea,.is-danger.is-focused.input,.is-danger.is-focused.textarea,.is-danger.textarea:active,.is-danger.textarea:focus{box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.is-small.input,.is-small.textarea{border-radius:2px;font-size:.75rem}.is-medium.input,.is-medium.textarea{font-size:1.25rem}.is-large.input,.is-large.textarea{font-size:1.5rem}.is-fullwidth.input,.is-fullwidth.textarea{display:block;width:100%}.is-inline.input,.is-inline.textarea{display:inline;width:auto}.input.is-rounded{border-radius:9999px;padding-left:calc(calc(.75em - 1px) + .375em);padding-right:calc(calc(.75em - 1px) + .375em)}.input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:calc(.75em - 1px);resize:vertical}.textarea:not([rows]){max-height:40em;min-height:8em}.textarea[rows]{height:initial}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox input[disabled],.checkbox[disabled],.radio input[disabled],.radio[disabled],fieldset[disabled] .checkbox,fieldset[disabled] .radio{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.5em}.select:not(.is-multiple):not(.is-loading)::after{border-color:#485fc7;right:1.125em;z-index:4}.select.is-rounded select{border-radius:9999px;padding-left:1em}.select select{cursor:pointer;display:block;font-size:1em;max-width:100%;outline:0}.select select::-ms-expand{display:none}.select select[disabled]:hover,fieldset[disabled] .select select:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover::after{border-color:#363636}.select.is-white:not(:hover)::after{border-color:#fff}.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.select.is-black:not(:hover)::after{border-color:#0a0a0a}.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.select.is-light:not(:hover)::after{border-color:#f5f5f5}.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.select.is-dark:not(:hover)::after{border-color:#363636}.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover)::after{border-color:#00d1b2}.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover)::after{border-color:#485fc7}.select.is-link select{border-color:#485fc7}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#3a51bb}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.select.is-info:not(:hover)::after{border-color:#3e8ed0}.select.is-info select{border-color:#3e8ed0}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#3082c5}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(62,142,208,.25)}.select.is-success:not(:hover)::after{border-color:#48c78e}.select.is-success select{border-color:#48c78e}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#3abb81}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(72,199,142,.25)}.select.is-warning:not(:hover)::after{border-color:#ffe08a}.select.is-warning select{border-color:#ffe08a}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd970}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,224,138,.25)}.select.is-danger:not(:hover)::after{border-color:#f14668}.select.is-danger select{border-color:#f14668}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ef2e55}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#7a7a7a}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{margin-top:0;position:absolute;right:.625em;top:.625em;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,255,255,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(10,10,10,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(245,245,245,.25);color:rgba(0,0,0,.7)}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#fff}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#fff}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#fff}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#fff}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#485fc7;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#3e56c4;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(72,95,199,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#3a51bb;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#3e8ed0;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#3488ce;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(62,142,208,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#3082c5;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#48c78e;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#3ec487;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(72,199,142,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#3abb81;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffe08a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdc7d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,224,138,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd970;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#f14668;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#f03a5f;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(241,70,104,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ef2e55;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-normal{font-size:1rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:100%;left:0;opacity:0;outline:0;position:absolute;top:0;width:100%}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:inherit;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#485fc7}.help.is-info{color:#3e8ed0}.help.is-success{color:#48c78e}.help.is-warning{color:#ffe08a}.help.is-danger{color:#f14668}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child:not(:only-child) .button,.field.has-addons .control:first-child:not(:only-child) .input,.field.has-addons .control:first-child:not(:only-child) .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child:not(:only-child) .button,.field.has-addons .control:last-child:not(:only-child) .input,.field.has-addons .control:last-child:not(:only-child) .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button:not([disabled]).is-hovered,.field.has-addons .control .button:not([disabled]):hover,.field.has-addons .control .input:not([disabled]).is-hovered,.field.has-addons .control .input:not([disabled]):hover,.field.has-addons .control .select select:not([disabled]).is-hovered,.field.has-addons .control .select select:not([disabled]):hover{z-index:2}.field.has-addons .control .button:not([disabled]).is-active,.field.has-addons .control .button:not([disabled]).is-focused,.field.has-addons .control .button:not([disabled]):active,.field.has-addons .control .button:not([disabled]):focus,.field.has-addons .control .input:not([disabled]).is-active,.field.has-addons .control .input:not([disabled]).is-focused,.field.has-addons .control .input:not([disabled]):active,.field.has-addons .control .input:not([disabled]):focus,.field.has-addons .control .select select:not([disabled]).is-active,.field.has-addons .control .select select:not([disabled]).is-focused,.field.has-addons .control .select select:not([disabled]):active,.field.has-addons .control .select select:not([disabled]):focus{z-index:3}.field.has-addons .control .button:not([disabled]).is-active:hover,.field.has-addons .control .button:not([disabled]).is-focused:hover,.field.has-addons .control .button:not([disabled]):active:hover,.field.has-addons .control .button:not([disabled]):focus:hover,.field.has-addons .control .input:not([disabled]).is-active:hover,.field.has-addons .control .input:not([disabled]).is-focused:hover,.field.has-addons .control .input:not([disabled]):active:hover,.field.has-addons .control .input:not([disabled]):focus:hover,.field.has-addons .control .select select:not([disabled]).is-active:hover,.field.has-addons .control .select select:not([disabled]).is-focused:hover,.field.has-addons .control .select select:not([disabled]):active:hover,.field.has-addons .control .select select:not([disabled]):focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width:769px),print{.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media screen and (min-width:769px),print{.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width:769px),print{.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{box-sizing:border-box;clear:both;font-size:1rem;position:relative;text-align:inherit}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon{color:#4a4a4a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.5em;pointer-events:none;position:absolute;top:0;width:2.5em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select{padding-left:2.5em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select{padding-right:2.5em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#485fc7;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#b5b5b5;content:"\0002f"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;border-radius:.25rem;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);color:#4a4a4a;max-width:100%;position:relative}.card-content:first-child,.card-footer:first-child,.card-header:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-content:last-child,.card-footer:last-child,.card-header:last-child{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-header{background-color:transparent;align-items:stretch;box-shadow:0 .125em .25em rgba(10,10,10,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem 1rem}.card-header-title.is-centered{justify-content:center}.card-header-icon{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:0 0;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0;align-items:center;cursor:pointer;display:flex;justify-content:center;padding:.75rem 1rem}.card-image{display:block;position:relative}.card-image:first-child img{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-image:last-child img{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-content{background-color:transparent;padding:1.5rem}.card-footer{background-color:transparent;border-top:1px solid #ededed;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #ededed}.card .media:not(:last-child){margin-bottom:1.5rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}a.dropdown-item,button.dropdown-item{padding-right:3rem;text-align:inherit;white-space:nowrap;width:100%}a.dropdown-item:hover,button.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active,button.dropdown-item.is-active{background-color:#485fc7;color:#fff}.dropdown-divider{background-color:#ededed;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item:not(:last-child){margin-bottom:0;margin-right:.75rem}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width:769px),print{.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width:769px),print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width:769px),print{.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media screen and (min-width:769px),print{.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:inherit}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid rgba(219,219,219,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid rgba(219,219,219,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:inherit}@media screen and (max-width:768px){.media-content{overflow-x:auto}}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#485fc7;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag):not(.dropdown-item){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.message.is-light .message-body{border-color:#f5f5f5}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#fff}.message.is-dark .message-body{border-color:#363636}.message.is-primary{background-color:#ebfffc}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#00947e}.message.is-link{background-color:#eff1fa}.message.is-link .message-header{background-color:#485fc7;color:#fff}.message.is-link .message-body{border-color:#485fc7;color:#3850b7}.message.is-info{background-color:#eff5fb}.message.is-info .message-header{background-color:#3e8ed0;color:#fff}.message.is-info .message-body{border-color:#3e8ed0;color:#296fa8}.message.is-success{background-color:#effaf5}.message.is-success .message-header{background-color:#48c78e;color:#fff}.message.is-success .message-body{border-color:#48c78e;color:#257953}.message.is-warning{background-color:#fffaeb}.message.is-warning .message-header{background-color:#ffe08a;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffe08a;color:#946c00}.message.is-danger{background-color:#feecf0}.message.is-danger .message-header{background-color:#f14668;color:#fff}.message.is-danger .message-body{border-color:#f14668;color:#cc0f35}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;flex-direction:column;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:rgba(10,10,10,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width:769px){.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:0 0;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden;-ms-overflow-y:visible}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:.5em}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:focus,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:focus,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-burger{color:#0a0a0a}@media screen and (min-width:1024px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:focus,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:focus,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:focus,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:focus,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link::after,.navbar.is-white .navbar-start .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:focus,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:focus,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:focus,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:focus,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:focus,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:focus,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link::after,.navbar.is-black .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:focus,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:focus,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-light .navbar-burger{color:rgba(0,0,0,.7)}@media screen and (min-width:1024px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:focus,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:focus,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:focus,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:focus,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-end .navbar-link::after,.navbar.is-light .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:rgba(0,0,0,.7)}}.navbar.is-dark{background-color:#363636;color:#fff}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#fff}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:focus,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:focus,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#fff}.navbar.is-dark .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#fff}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:focus,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:focus,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:focus,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:focus,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#fff}.navbar.is-dark .navbar-end .navbar-link::after,.navbar.is-dark .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#fff}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#fff}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:focus,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:focus,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:focus,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:focus,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:focus,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:focus,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link::after,.navbar.is-primary .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#485fc7;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:focus,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:focus,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#3a51bb;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:focus,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:focus,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:focus,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:focus,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#3a51bb;color:#fff}.navbar.is-link .navbar-end .navbar-link::after,.navbar.is-link .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#3a51bb;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#485fc7;color:#fff}}.navbar.is-info{background-color:#3e8ed0;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:focus,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:focus,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#3082c5;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:focus,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:focus,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:focus,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:focus,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#3082c5;color:#fff}.navbar.is-info .navbar-end .navbar-link::after,.navbar.is-info .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#3082c5;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#3e8ed0;color:#fff}}.navbar.is-success{background-color:#48c78e;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:focus,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:focus,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#3abb81;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:focus,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:focus,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:focus,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:focus,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#3abb81;color:#fff}.navbar.is-success .navbar-end .navbar-link::after,.navbar.is-success .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#3abb81;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#48c78e;color:#fff}}.navbar.is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:focus,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:focus,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd970;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-burger{color:rgba(0,0,0,.7)}@media screen and (min-width:1024px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:focus,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:focus,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:focus,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:focus,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd970;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link::after,.navbar.is-warning .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd970;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffe08a;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#f14668;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:focus,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:focus,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:focus,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:focus,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:focus,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:focus,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-end .navbar-link::after,.navbar.is-danger .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#f14668;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:focus,.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{color:#4a4a4a;cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:nth-child(1){top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:nth-child(1){transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:focus,.navbar-link:focus-within,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:focus,a.navbar-item:focus-within,a.navbar-item:hover{background-color:#fafafa;color:#485fc7}.navbar-item{flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab:focus,.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#485fc7}.navbar-item.is-tab.is-active{background-color:transparent;border-bottom-color:#485fc7;border-bottom-style:solid;border-bottom-width:3px;color:#485fc7;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link:not(.is-arrowless){padding-right:2.5em}.navbar-link:not(.is-arrowless)::after{border-color:#485fc7;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1023px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link::after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px rgba(10,10,10,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1024px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:focus,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:focus,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:focus,.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#485fc7}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:focus .navbar-dropdown,.navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px rgba(10,10,10,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:focus,.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#485fc7}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-.75rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-.75rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:focus):not(:hover),a.navbar-item.is-active:not(:focus):not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:focus .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.hero.is-fullheight-with-navbar{min-height:calc(100vh - 3.25rem)}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:9999px}.pagination.is-rounded .pagination-link{border-radius:9999px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;justify-content:center;margin:.25rem;padding-left:.5em;padding-right:.5em;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.5em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#485fc7}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#485fc7;border-color:#485fc7;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}.pagination-list li{list-style:none}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}.pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width:769px),print{.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{margin-bottom:0;margin-top:0}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between;margin-bottom:0;margin-top:0}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{border-radius:6px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel.is-white .panel-heading{background-color:#fff;color:#0a0a0a}.panel.is-white .panel-tabs a.is-active{border-bottom-color:#fff}.panel.is-white .panel-block.is-active .panel-icon{color:#fff}.panel.is-black .panel-heading{background-color:#0a0a0a;color:#fff}.panel.is-black .panel-tabs a.is-active{border-bottom-color:#0a0a0a}.panel.is-black .panel-block.is-active .panel-icon{color:#0a0a0a}.panel.is-light .panel-heading{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.panel.is-light .panel-tabs a.is-active{border-bottom-color:#f5f5f5}.panel.is-light .panel-block.is-active .panel-icon{color:#f5f5f5}.panel.is-dark .panel-heading{background-color:#363636;color:#fff}.panel.is-dark .panel-tabs a.is-active{border-bottom-color:#363636}.panel.is-dark .panel-block.is-active .panel-icon{color:#363636}.panel.is-primary .panel-heading{background-color:#00d1b2;color:#fff}.panel.is-primary .panel-tabs a.is-active{border-bottom-color:#00d1b2}.panel.is-primary .panel-block.is-active .panel-icon{color:#00d1b2}.panel.is-link .panel-heading{background-color:#485fc7;color:#fff}.panel.is-link .panel-tabs a.is-active{border-bottom-color:#485fc7}.panel.is-link .panel-block.is-active .panel-icon{color:#485fc7}.panel.is-info .panel-heading{background-color:#3e8ed0;color:#fff}.panel.is-info .panel-tabs a.is-active{border-bottom-color:#3e8ed0}.panel.is-info .panel-block.is-active .panel-icon{color:#3e8ed0}.panel.is-success .panel-heading{background-color:#48c78e;color:#fff}.panel.is-success .panel-tabs a.is-active{border-bottom-color:#48c78e}.panel.is-success .panel-block.is-active .panel-icon{color:#48c78e}.panel.is-warning .panel-heading{background-color:#ffe08a;color:rgba(0,0,0,.7)}.panel.is-warning .panel-tabs a.is-active{border-bottom-color:#ffe08a}.panel.is-warning .panel-block.is-active .panel-icon{color:#ffe08a}.panel.is-danger .panel-heading{background-color:#f14668;color:#fff}.panel.is-danger .panel-tabs a.is-active{border-bottom-color:#f14668}.panel.is-danger .panel-block.is-active .panel-icon{color:#f14668}.panel-block:not(:last-child),.panel-tabs:not(:last-child){border-bottom:1px solid #ededed}.panel-heading{background-color:#ededed;border-radius:6px 6px 0 0;color:#363636;font-size:1.25em;font-weight:700;line-height:1.25;padding:.75em 1em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#485fc7}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#485fc7;color:#363636}.panel-block.is-active .panel-icon{color:#485fc7}.panel-block:last-child{border-bottom-left-radius:6px;border-bottom-right-radius:6px}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#485fc7;color:#485fc7}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em;padding-right:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-top-left-radius:4px;border-bottom-left-radius:4px}.tabs.is-toggle li:last-child a{border-top-right-radius:4px;border-bottom-right-radius:4px}.tabs.is-toggle li.is-active a{background-color:#485fc7;border-color:#485fc7;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:9999px;border-top-left-radius:9999px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:9999px;border-top-right-radius:9999px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none;width:unset}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-0{flex:none;width:0%}.columns.is-mobile>.column.is-offset-0{margin-left:0}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none;width:unset}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-0-mobile{flex:none;width:0%}.column.is-offset-0-mobile{margin-left:0}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width:769px),print{.column.is-narrow,.column.is-narrow-tablet{flex:none;width:unset}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-0,.column.is-0-tablet{flex:none;width:0%}.column.is-offset-0,.column.is-offset-0-tablet{margin-left:0}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1023px){.column.is-narrow-touch{flex:none;width:unset}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-0-touch{flex:none;width:0%}.column.is-offset-0-touch{margin-left:0}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1024px){.column.is-narrow-desktop{flex:none;width:unset}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-0-desktop{flex:none;width:0%}.column.is-offset-0-desktop{margin-left:0}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1216px){.column.is-narrow-widescreen{flex:none;width:unset}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-0-widescreen{flex:none;width:0%}.column.is-offset-0-widescreen{margin-left:0}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1408px){.column.is-narrow-fullhd{flex:none;width:unset}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-0-fullhd{flex:none;width:0%}.column.is-offset-0-fullhd{margin-left:0}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media screen and (min-width:769px),print{.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1024px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable>.column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}@media screen and (max-width:768px){.columns.is-variable.is-0-mobile{--columnGap:0rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-0-tablet{--columnGap:0rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-0-tablet-only{--columnGap:0rem}}@media screen and (max-width:1023px){.columns.is-variable.is-0-touch{--columnGap:0rem}}@media screen and (min-width:1024px){.columns.is-variable.is-0-desktop{--columnGap:0rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-0-desktop-only{--columnGap:0rem}}@media screen and (min-width:1216px){.columns.is-variable.is-0-widescreen{--columnGap:0rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-0-widescreen-only{--columnGap:0rem}}@media screen and (min-width:1408px){.columns.is-variable.is-0-fullhd{--columnGap:0rem}}.columns.is-variable.is-1{--columnGap:0.25rem}@media screen and (max-width:768px){.columns.is-variable.is-1-mobile{--columnGap:0.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-1-tablet{--columnGap:0.25rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-1-tablet-only{--columnGap:0.25rem}}@media screen and (max-width:1023px){.columns.is-variable.is-1-touch{--columnGap:0.25rem}}@media screen and (min-width:1024px){.columns.is-variable.is-1-desktop{--columnGap:0.25rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-1-desktop-only{--columnGap:0.25rem}}@media screen and (min-width:1216px){.columns.is-variable.is-1-widescreen{--columnGap:0.25rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-1-widescreen-only{--columnGap:0.25rem}}@media screen and (min-width:1408px){.columns.is-variable.is-1-fullhd{--columnGap:0.25rem}}.columns.is-variable.is-2{--columnGap:0.5rem}@media screen and (max-width:768px){.columns.is-variable.is-2-mobile{--columnGap:0.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-2-tablet{--columnGap:0.5rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-2-tablet-only{--columnGap:0.5rem}}@media screen and (max-width:1023px){.columns.is-variable.is-2-touch{--columnGap:0.5rem}}@media screen and (min-width:1024px){.columns.is-variable.is-2-desktop{--columnGap:0.5rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-2-desktop-only{--columnGap:0.5rem}}@media screen and (min-width:1216px){.columns.is-variable.is-2-widescreen{--columnGap:0.5rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-2-widescreen-only{--columnGap:0.5rem}}@media screen and (min-width:1408px){.columns.is-variable.is-2-fullhd{--columnGap:0.5rem}}.columns.is-variable.is-3{--columnGap:0.75rem}@media screen and (max-width:768px){.columns.is-variable.is-3-mobile{--columnGap:0.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-3-tablet{--columnGap:0.75rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-3-tablet-only{--columnGap:0.75rem}}@media screen and (max-width:1023px){.columns.is-variable.is-3-touch{--columnGap:0.75rem}}@media screen and (min-width:1024px){.columns.is-variable.is-3-desktop{--columnGap:0.75rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-3-desktop-only{--columnGap:0.75rem}}@media screen and (min-width:1216px){.columns.is-variable.is-3-widescreen{--columnGap:0.75rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-3-widescreen-only{--columnGap:0.75rem}}@media screen and (min-width:1408px){.columns.is-variable.is-3-fullhd{--columnGap:0.75rem}}.columns.is-variable.is-4{--columnGap:1rem}@media screen and (max-width:768px){.columns.is-variable.is-4-mobile{--columnGap:1rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-4-tablet{--columnGap:1rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-4-tablet-only{--columnGap:1rem}}@media screen and (max-width:1023px){.columns.is-variable.is-4-touch{--columnGap:1rem}}@media screen and (min-width:1024px){.columns.is-variable.is-4-desktop{--columnGap:1rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-4-desktop-only{--columnGap:1rem}}@media screen and (min-width:1216px){.columns.is-variable.is-4-widescreen{--columnGap:1rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-4-widescreen-only{--columnGap:1rem}}@media screen and (min-width:1408px){.columns.is-variable.is-4-fullhd{--columnGap:1rem}}.columns.is-variable.is-5{--columnGap:1.25rem}@media screen and (max-width:768px){.columns.is-variable.is-5-mobile{--columnGap:1.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-5-tablet{--columnGap:1.25rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-5-tablet-only{--columnGap:1.25rem}}@media screen and (max-width:1023px){.columns.is-variable.is-5-touch{--columnGap:1.25rem}}@media screen and (min-width:1024px){.columns.is-variable.is-5-desktop{--columnGap:1.25rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-5-desktop-only{--columnGap:1.25rem}}@media screen and (min-width:1216px){.columns.is-variable.is-5-widescreen{--columnGap:1.25rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-5-widescreen-only{--columnGap:1.25rem}}@media screen and (min-width:1408px){.columns.is-variable.is-5-fullhd{--columnGap:1.25rem}}.columns.is-variable.is-6{--columnGap:1.5rem}@media screen and (max-width:768px){.columns.is-variable.is-6-mobile{--columnGap:1.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-6-tablet{--columnGap:1.5rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-6-tablet-only{--columnGap:1.5rem}}@media screen and (max-width:1023px){.columns.is-variable.is-6-touch{--columnGap:1.5rem}}@media screen and (min-width:1024px){.columns.is-variable.is-6-desktop{--columnGap:1.5rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-6-desktop-only{--columnGap:1.5rem}}@media screen and (min-width:1216px){.columns.is-variable.is-6-widescreen{--columnGap:1.5rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-6-widescreen-only{--columnGap:1.5rem}}@media screen and (min-width:1408px){.columns.is-variable.is-6-fullhd{--columnGap:1.5rem}}.columns.is-variable.is-7{--columnGap:1.75rem}@media screen and (max-width:768px){.columns.is-variable.is-7-mobile{--columnGap:1.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-7-tablet{--columnGap:1.75rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-7-tablet-only{--columnGap:1.75rem}}@media screen and (max-width:1023px){.columns.is-variable.is-7-touch{--columnGap:1.75rem}}@media screen and (min-width:1024px){.columns.is-variable.is-7-desktop{--columnGap:1.75rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-7-desktop-only{--columnGap:1.75rem}}@media screen and (min-width:1216px){.columns.is-variable.is-7-widescreen{--columnGap:1.75rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-7-widescreen-only{--columnGap:1.75rem}}@media screen and (min-width:1408px){.columns.is-variable.is-7-fullhd{--columnGap:1.75rem}}.columns.is-variable.is-8{--columnGap:2rem}@media screen and (max-width:768px){.columns.is-variable.is-8-mobile{--columnGap:2rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-8-tablet{--columnGap:2rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-8-tablet-only{--columnGap:2rem}}@media screen and (max-width:1023px){.columns.is-variable.is-8-touch{--columnGap:2rem}}@media screen and (min-width:1024px){.columns.is-variable.is-8-desktop{--columnGap:2rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-8-desktop-only{--columnGap:2rem}}@media screen and (min-width:1216px){.columns.is-variable.is-8-widescreen{--columnGap:2rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-8-widescreen-only{--columnGap:2rem}}@media screen and (min-width:1408px){.columns.is-variable.is-8-fullhd{--columnGap:2rem}}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media screen and (min-width:769px),print{.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-primary-light{color:#ebfffc!important}a.has-text-primary-light:focus,a.has-text-primary-light:hover{color:#b8fff4!important}.has-background-primary-light{background-color:#ebfffc!important}.has-text-primary-dark{color:#00947e!important}a.has-text-primary-dark:focus,a.has-text-primary-dark:hover{color:#00c7a9!important}.has-background-primary-dark{background-color:#00947e!important}.has-text-link{color:#485fc7!important}a.has-text-link:focus,a.has-text-link:hover{color:#3449a8!important}.has-background-link{background-color:#485fc7!important}.has-text-link-light{color:#eff1fa!important}a.has-text-link-light:focus,a.has-text-link-light:hover{color:#c8cfee!important}.has-background-link-light{background-color:#eff1fa!important}.has-text-link-dark{color:#3850b7!important}a.has-text-link-dark:focus,a.has-text-link-dark:hover{color:#576dcb!important}.has-background-link-dark{background-color:#3850b7!important}.has-text-info{color:#3e8ed0!important}a.has-text-info:focus,a.has-text-info:hover{color:#2b74b1!important}.has-background-info{background-color:#3e8ed0!important}.has-text-info-light{color:#eff5fb!important}a.has-text-info-light:focus,a.has-text-info-light:hover{color:#c6ddf1!important}.has-background-info-light{background-color:#eff5fb!important}.has-text-info-dark{color:#296fa8!important}a.has-text-info-dark:focus,a.has-text-info-dark:hover{color:#368ace!important}.has-background-info-dark{background-color:#296fa8!important}.has-text-success{color:#48c78e!important}a.has-text-success:focus,a.has-text-success:hover{color:#34a873!important}.has-background-success{background-color:#48c78e!important}.has-text-success-light{color:#effaf5!important}a.has-text-success-light:focus,a.has-text-success-light:hover{color:#c8eedd!important}.has-background-success-light{background-color:#effaf5!important}.has-text-success-dark{color:#257953!important}a.has-text-success-dark:focus,a.has-text-success-dark:hover{color:#31a06e!important}.has-background-success-dark{background-color:#257953!important}.has-text-warning{color:#ffe08a!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd257!important}.has-background-warning{background-color:#ffe08a!important}.has-text-warning-light{color:#fffaeb!important}a.has-text-warning-light:focus,a.has-text-warning-light:hover{color:#ffecb8!important}.has-background-warning-light{background-color:#fffaeb!important}.has-text-warning-dark{color:#946c00!important}a.has-text-warning-dark:focus,a.has-text-warning-dark:hover{color:#c79200!important}.has-background-warning-dark{background-color:#946c00!important}.has-text-danger{color:#f14668!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ee1742!important}.has-background-danger{background-color:#f14668!important}.has-text-danger-light{color:#feecf0!important}a.has-text-danger-light:focus,a.has-text-danger-light:hover{color:#fabdc9!important}.has-background-danger-light{background-color:#feecf0!important}.has-text-danger-dark{color:#cc0f35!important}a.has-text-danger-dark:focus,a.has-text-danger-dark:hover{color:#ee2049!important}.has-background-danger-dark{background-color:#cc0f35!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.is-flex-direction-row{flex-direction:row!important}.is-flex-direction-row-reverse{flex-direction:row-reverse!important}.is-flex-direction-column{flex-direction:column!important}.is-flex-direction-column-reverse{flex-direction:column-reverse!important}.is-flex-wrap-nowrap{flex-wrap:nowrap!important}.is-flex-wrap-wrap{flex-wrap:wrap!important}.is-flex-wrap-wrap-reverse{flex-wrap:wrap-reverse!important}.is-justify-content-flex-start{justify-content:flex-start!important}.is-justify-content-flex-end{justify-content:flex-end!important}.is-justify-content-center{justify-content:center!important}.is-justify-content-space-between{justify-content:space-between!important}.is-justify-content-space-around{justify-content:space-around!important}.is-justify-content-space-evenly{justify-content:space-evenly!important}.is-justify-content-start{justify-content:start!important}.is-justify-content-end{justify-content:end!important}.is-justify-content-left{justify-content:left!important}.is-justify-content-right{justify-content:right!important}.is-align-content-flex-start{align-content:flex-start!important}.is-align-content-flex-end{align-content:flex-end!important}.is-align-content-center{align-content:center!important}.is-align-content-space-between{align-content:space-between!important}.is-align-content-space-around{align-content:space-around!important}.is-align-content-space-evenly{align-content:space-evenly!important}.is-align-content-stretch{align-content:stretch!important}.is-align-content-start{align-content:start!important}.is-align-content-end{align-content:end!important}.is-align-content-baseline{align-content:baseline!important}.is-align-items-stretch{align-items:stretch!important}.is-align-items-flex-start{align-items:flex-start!important}.is-align-items-flex-end{align-items:flex-end!important}.is-align-items-center{align-items:center!important}.is-align-items-baseline{align-items:baseline!important}.is-align-items-start{align-items:start!important}.is-align-items-end{align-items:end!important}.is-align-items-self-start{align-items:self-start!important}.is-align-items-self-end{align-items:self-end!important}.is-align-self-auto{align-self:auto!important}.is-align-self-flex-start{align-self:flex-start!important}.is-align-self-flex-end{align-self:flex-end!important}.is-align-self-center{align-self:center!important}.is-align-self-baseline{align-self:baseline!important}.is-align-self-stretch{align-self:stretch!important}.is-flex-grow-0{flex-grow:0!important}.is-flex-grow-1{flex-grow:1!important}.is-flex-grow-2{flex-grow:2!important}.is-flex-grow-3{flex-grow:3!important}.is-flex-grow-4{flex-grow:4!important}.is-flex-grow-5{flex-grow:5!important}.is-flex-shrink-0{flex-shrink:0!important}.is-flex-shrink-1{flex-shrink:1!important}.is-flex-shrink-2{flex-shrink:2!important}.is-flex-shrink-3{flex-shrink:3!important}.is-flex-shrink-4{flex-shrink:4!important}.is-flex-shrink-5{flex-shrink:5!important}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.is-clickable{cursor:pointer!important;pointer-events:all!important}.is-clipped{overflow:hidden!important}.is-relative{position:relative!important}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.m-0{margin:0!important}.mt-0{margin-top:0!important}.mr-0{margin-right:0!important}.mb-0{margin-bottom:0!important}.ml-0{margin-left:0!important}.mx-0{margin-left:0!important;margin-right:0!important}.my-0{margin-top:0!important;margin-bottom:0!important}.m-1{margin:.25rem!important}.mt-1{margin-top:.25rem!important}.mr-1{margin-right:.25rem!important}.mb-1{margin-bottom:.25rem!important}.ml-1{margin-left:.25rem!important}.mx-1{margin-left:.25rem!important;margin-right:.25rem!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.m-2{margin:.5rem!important}.mt-2{margin-top:.5rem!important}.mr-2{margin-right:.5rem!important}.mb-2{margin-bottom:.5rem!important}.ml-2{margin-left:.5rem!important}.mx-2{margin-left:.5rem!important;margin-right:.5rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.m-3{margin:.75rem!important}.mt-3{margin-top:.75rem!important}.mr-3{margin-right:.75rem!important}.mb-3{margin-bottom:.75rem!important}.ml-3{margin-left:.75rem!important}.mx-3{margin-left:.75rem!important;margin-right:.75rem!important}.my-3{margin-top:.75rem!important;margin-bottom:.75rem!important}.m-4{margin:1rem!important}.mt-4{margin-top:1rem!important}.mr-4{margin-right:1rem!important}.mb-4{margin-bottom:1rem!important}.ml-4{margin-left:1rem!important}.mx-4{margin-left:1rem!important;margin-right:1rem!important}.my-4{margin-top:1rem!important;margin-bottom:1rem!important}.m-5{margin:1.5rem!important}.mt-5{margin-top:1.5rem!important}.mr-5{margin-right:1.5rem!important}.mb-5{margin-bottom:1.5rem!important}.ml-5{margin-left:1.5rem!important}.mx-5{margin-left:1.5rem!important;margin-right:1.5rem!important}.my-5{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.m-6{margin:3rem!important}.mt-6{margin-top:3rem!important}.mr-6{margin-right:3rem!important}.mb-6{margin-bottom:3rem!important}.ml-6{margin-left:3rem!important}.mx-6{margin-left:3rem!important;margin-right:3rem!important}.my-6{margin-top:3rem!important;margin-bottom:3rem!important}.m-auto{margin:auto!important}.mt-auto{margin-top:auto!important}.mr-auto{margin-right:auto!important}.mb-auto{margin-bottom:auto!important}.ml-auto{margin-left:auto!important}.mx-auto{margin-left:auto!important;margin-right:auto!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.p-0{padding:0!important}.pt-0{padding-top:0!important}.pr-0{padding-right:0!important}.pb-0{padding-bottom:0!important}.pl-0{padding-left:0!important}.px-0{padding-left:0!important;padding-right:0!important}.py-0{padding-top:0!important;padding-bottom:0!important}.p-1{padding:.25rem!important}.pt-1{padding-top:.25rem!important}.pr-1{padding-right:.25rem!important}.pb-1{padding-bottom:.25rem!important}.pl-1{padding-left:.25rem!important}.px-1{padding-left:.25rem!important;padding-right:.25rem!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.p-2{padding:.5rem!important}.pt-2{padding-top:.5rem!important}.pr-2{padding-right:.5rem!important}.pb-2{padding-bottom:.5rem!important}.pl-2{padding-left:.5rem!important}.px-2{padding-left:.5rem!important;padding-right:.5rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.p-3{padding:.75rem!important}.pt-3{padding-top:.75rem!important}.pr-3{padding-right:.75rem!important}.pb-3{padding-bottom:.75rem!important}.pl-3{padding-left:.75rem!important}.px-3{padding-left:.75rem!important;padding-right:.75rem!important}.py-3{padding-top:.75rem!important;padding-bottom:.75rem!important}.p-4{padding:1rem!important}.pt-4{padding-top:1rem!important}.pr-4{padding-right:1rem!important}.pb-4{padding-bottom:1rem!important}.pl-4{padding-left:1rem!important}.px-4{padding-left:1rem!important;padding-right:1rem!important}.py-4{padding-top:1rem!important;padding-bottom:1rem!important}.p-5{padding:1.5rem!important}.pt-5{padding-top:1.5rem!important}.pr-5{padding-right:1.5rem!important}.pb-5{padding-bottom:1.5rem!important}.pl-5{padding-left:1.5rem!important}.px-5{padding-left:1.5rem!important;padding-right:1.5rem!important}.py-5{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.p-6{padding:3rem!important}.pt-6{padding-top:3rem!important}.pr-6{padding-right:3rem!important}.pb-6{padding-bottom:3rem!important}.pl-6{padding-left:3rem!important}.px-6{padding-left:3rem!important;padding-right:3rem!important}.py-6{padding-top:3rem!important;padding-bottom:3rem!important}.p-auto{padding:auto!important}.pt-auto{padding-top:auto!important}.pr-auto{padding-right:auto!important}.pb-auto{padding-bottom:auto!important}.pl-auto{padding-left:auto!important}.px-auto{padding-left:auto!important;padding-right:auto!important}.py-auto{padding-top:auto!important;padding-bottom:auto!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media screen and (min-width:769px),print{.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1023px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1024px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1216px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1408px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media screen and (min-width:769px),print{.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1023px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1024px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1216px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1408px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media screen and (min-width:769px),print{.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1023px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1024px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1216px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1408px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media screen and (min-width:769px),print{.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1023px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1024px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1216px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1408px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media screen and (min-width:769px),print{.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1023px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1024px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1216px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1408px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.is-underlined{text-decoration:underline!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-medium{font-weight:500!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-family-primary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-secondary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-sans-serif{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-monospace{font-family:monospace!important}.is-family-code{font-family:monospace!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media screen and (min-width:769px),print{.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1023px){.is-block-touch{display:block!important}}@media screen and (min-width:1024px){.is-block-desktop{display:block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1216px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1408px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media screen and (min-width:769px),print{.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1023px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1024px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1216px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1408px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media screen and (min-width:769px),print{.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1023px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1024px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1216px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1408px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media screen and (min-width:769px),print{.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1023px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1024px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1216px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1408px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media screen and (min-width:769px),print{.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1023px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1024px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1216px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1408px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}.is-sr-only{border:none!important;clip:rect(0,0,0,0)!important;height:.01em!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:.01em!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media screen and (min-width:769px),print{.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1023px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1024px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1216px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1408px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media screen and (min-width:769px),print{.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1023px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1024px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1216px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1408px){.is-invisible-fullhd{visibility:hidden!important}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:0 0}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1023px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{color:#fff!important;opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{color:#0a0a0a!important;opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-light strong{color:inherit}.hero.is-light .title{color:rgba(0,0,0,.7)}.hero.is-light .subtitle{color:rgba(0,0,0,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1023px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(0,0,0,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.hero.is-light .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{color:#f5f5f5!important;opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}}.hero.is-dark{background-color:#363636;color:#fff}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#fff}.hero.is-dark .subtitle{color:rgba(255,255,255,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:rgba(255,255,255,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#fff}.hero.is-dark .tabs a{color:#fff;opacity:.9}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{color:#363636!important;opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#fff}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:rgba(255,255,255,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:rgba(255,255,255,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{color:#00d1b2!important;opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}}.hero.is-link{background-color:#485fc7;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-link .navbar-menu{background-color:#485fc7}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#3a51bb;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{color:#485fc7!important;opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#485fc7}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#2959b3 0,#485fc7 71%,#5658d2 100%)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#2959b3 0,#485fc7 71%,#5658d2 100%)}}.hero.is-info{background-color:#3e8ed0;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-info .navbar-menu{background-color:#3e8ed0}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#3082c5;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{color:#3e8ed0!important;opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3e8ed0}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#208fbc 0,#3e8ed0 71%,#4d83db 100%)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#208fbc 0,#3e8ed0 71%,#4d83db 100%)}}.hero.is-success{background-color:#48c78e;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-success .navbar-menu{background-color:#48c78e}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#3abb81;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{color:#48c78e!important;opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#48c78e}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#29b35e 0,#48c78e 71%,#56d2af 100%)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#29b35e 0,#48c78e 71%,#56d2af 100%)}}.hero.is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1023px){.hero.is-warning .navbar-menu{background-color:#ffe08a}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd970;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{color:#ffe08a!important;opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffe08a}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffb657 0,#ffe08a 71%,#fff6a3 100%)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffb657 0,#ffe08a 71%,#fff6a3 100%)}}.hero.is-danger{background-color:#f14668;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-danger .navbar-menu{background-color:#f14668}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ef2e55;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{color:#f14668!important;opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#f14668}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#fa0a62 0,#f14668 71%,#f7595f 100%)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#fa0a62 0,#f14668 71%,#f7595f 100%)}}.hero.is-small .hero-body{padding:1.5rem}@media screen and (min-width:769px),print{.hero.is-medium .hero-body{padding:9rem 4.5rem}}@media screen and (min-width:769px),print{.hero.is-large .hero-body{padding:18rem 6rem}}.hero.is-fullheight .hero-body,.hero.is-fullheight-with-navbar .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-fullheight-with-navbar .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media screen and (min-width:769px),print{.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}@media screen and (min-width:769px),print{.hero-body{padding:3rem 3rem}}.section{padding:3rem 1.5rem}@media screen and (min-width:1024px){.section{padding:3rem 3rem}.section.is-medium{padding:9rem 4.5rem}.section.is-large{padding:18rem 6rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem} \ No newline at end of file diff --git a/demo/wasienv/threads/demothreads.lpi b/demo/wasienv/threads/demothreads.lpi new file mode 100644 index 0000000..0bf4489 --- /dev/null +++ b/demo/wasienv/threads/demothreads.lpi @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <CustomData Count="3"> + <Item0 Name="MaintainHTML" Value="1"/> + <Item1 Name="Pas2JSProject" Value="1"/> + <Item2 Name="PasJSWebBrowserProject" Value="1"/> + </CustomData> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <Units> + <Unit> + <Filename Value="demothreads.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="demowasithreads"/> + </Unit> + <Unit> + <Filename Value="index.html"/> + <IsPartOfProject Value="True"/> + <CustomData Count="1"> + <Item0 Name="PasJSIsProjectHTMLFile" Value="1"/> + </CustomData> + </Unit> + <Unit> + <Filename Value="../../../packages/rtl/rtl.webthreads.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="Rtl.WebThreads"/> + </Unit> + <Unit> + <Filename Value="../../../packages/wasi/wasiworkerthreadhost.pas"/> + <IsPartOfProject Value="True"/> + </Unit> + <Unit> + <Filename Value="../../../packages/wasi/wasithreadedapp.pas"/> + <IsPartOfProject Value="True"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <Target FileExt=".js"> + <Filename Value="demothreads"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="js"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <AllowLabel Value="False"/> + <CPPInline Value="False"/> + <UseAnsiStrings Value="False"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <TargetOS Value="browser"/> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + <UseLineInfoUnit Value="False"/> + </Debugging> + </Linking> + <Other> + <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/> + <CompilerPath Value="$(pas2js)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/demo/wasienv/threads/demothreads.lpr b/demo/wasienv/threads/demothreads.lpr new file mode 100644 index 0000000..c847c6d --- /dev/null +++ b/demo/wasienv/threads/demothreads.lpr @@ -0,0 +1,73 @@ +program demowasithreads; + +{$mode objfpc} + +uses + browserconsole, browserapp, JS, Classes, SysUtils, Web, WebAssembly, types, + wasienv, Rtl.WebThreads, wasihostapp, wasithreadedapp ; + +Type + + { TMyApplication } + + TMyApplication = class(TBrowserWASIThreadedHostApplication) + Private + BtnStart : TJSHTMLButtonElement; + procedure DoBeforeWasmInstantiate(Sender: TObject); + function DoStartClick(aEvent: TJSMouseEvent): boolean; + procedure DoWasmLoaded(Sender: TObject); + procedure DoWrite(Sender: TObject; aOutput: String); + Public + procedure doRun; override; + end; + +procedure TMyApplication.DoWrite(Sender: TObject; aOutput: String); + +begin + Writeln('Wasm: '+aOutput); +end; + +function TMyApplication.DoStartClick(aEvent: TJSMouseEvent): boolean; +begin + Result:=false; + Writeln('Host: Starting program'); + Host.Exported.start; +end; + +procedure TMyApplication.DoBeforeWasmInstantiate(Sender: TObject); +begin + Writeln('Host: Webassembly downloaded, instantiating VM'); +end; + +procedure TMyApplication.DoWasmLoaded(Sender: TObject); +begin + Writeln('Host: wasm loaded, ready to run'); + BtnStart.Disabled:=False; +end; + +procedure TMyApplication.doRun; + +begin + // Your code here + Terminate; + btnStart:=TJSHTMLButtonElement(GetHTMLElement('btnStart')); + btnStart.onclick:=@DoStartClick; + BtnStart.Disabled:=True; + Host.MemoryDescriptor.initial:=256; + Host.MemoryDescriptor.maximum:=512; + Host.OnConsoleWrite:=@DoWrite; + Host.AfterInstantation:=@DoWasmLoaded; + Host.BeforeInstantation:=@DoBeforeWasmInstantiate; + Writeln('Host: Loading wasm...'); + StartWebAssembly('threadapp.wasm',False); +end; + +var + Application : TMyApplication; + +begin + MaxConsoleLines:=250; + Application:=TMyApplication.Create(nil); + Application.Initialize; + Application.Run; +end. diff --git a/demo/wasienv/threads/index.html b/demo/wasienv/threads/index.html new file mode 100644 index 0000000..98711e7 --- /dev/null +++ b/demo/wasienv/threads/index.html @@ -0,0 +1,53 @@ + +<!doctype html> +<html lang="en"> +<head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>FPC-Webassembly and Pas2JS Demo + + + + + + + +
+

FPC compiled Browser Host/Webassembly programs console output:

+
+
+
+ +
+ +
+
+
+
+

Created using   pas2js.

+

Pas2JS Sources:   Pas2JS Program

+

Webassembly Sources:   FPC Program

+
+
+
+
+ + + diff --git a/demo/wasienv/threads/threadapp.lpi b/demo/wasienv/threads/threadapp.lpi new file mode 100644 index 0000000..8b368dc --- /dev/null +++ b/demo/wasienv/threads/threadapp.lpi @@ -0,0 +1,68 @@ + + + + + + + + + + + + + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <Units> + <Unit> + <Filename Value="threadapp.lpr"/> + <IsPartOfProject Value="True"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <Target> + <Filename Value="threadapp"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <CodeGeneration> + <TargetCPU Value="wasm32"/> + <TargetOS Value="wasi"/> + </CodeGeneration> + <Linking> + <Debugging> + <UseLineInfoUnit Value="False"/> + </Debugging> + </Linking> + <Other> + <CustomOptions Value="-CTwasmthreads"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/demo/wasienv/threads/threadapp.lpr b/demo/wasienv/threads/threadapp.lpr new file mode 100644 index 0000000..2a29e36 --- /dev/null +++ b/demo/wasienv/threads/threadapp.lpr @@ -0,0 +1,50 @@ +program threadapp; +{$mode objfpc} +{$h+} +{$i-} + +uses SysUtils, Classes; + +Function Fibonacci(N : Integer) : Int64; + +Var + Next,Last : Int64; + I : Integer; + +begin + if N=0 then + exit(0); + Result:=1; + Last:=0; + for I:=1 to N-1 do + begin + Next:=Result+last; + Last:=Result; + Result:=Next; + end; +end; + +Type + { TCalcThread } + TCalcThread = Class(TThread) + Procedure Execute; override; + end; + +{ TCalcThread } + +procedure TCalcThread.Execute; +begin + FreeOnTerminate:=True; + DebugWriteln('Fibonacci(10) = '+IntToStr(Fibonacci(10))); +end; + +begin + DebugWriteln('Starting thread'); + With TCalcThread.Create(False) do + begin + DebugWriteln('Thread created'); + WaitFor; + DebugWriteln('thread ended'); + end; +end. + diff --git a/demo/wasienv/threads/wasmthreads.pp b/demo/wasienv/threads/wasmthreads.pp new file mode 100644 index 0000000..d2008ba --- /dev/null +++ b/demo/wasienv/threads/wasmthreads.pp @@ -0,0 +1,927 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2022 by Michael Van Canneyt, + member of the Free Pascal development team. + + wasm threading support implementation + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} +{$mode objfpc} +{$modeswitch advancedrecords} + +{$DEFINE DEBUG_MT} + +unit wasmthreads; + +interface + +Procedure SetWasmThreadManager; + +implementation + +Uses + WebAssembly, wasiapi; + +{***************************************************************************** + System unit import +*****************************************************************************} + +procedure fpc_threaderror; [external name 'FPC_THREADERROR']; + +Type + TTimeLockResult = (tlrOK,tlrTimeout,tlrError); + + TFPWasmMutex = record + _lock : Longint; + _owner : Pointer; + function TryLock : Boolean; + function Lock : Boolean; + function TimedLock(aTimeOut : Longint) : TTimeLockResult; + function Unlock : Boolean; + end; + + TFPWasmEvent = record + _mutex : TFPWasmMutex; + _isset : Boolean; + end; + + PFPWasmThread = ^TFPWasmThread; + TFPWasmThread = record + ThreadID : Integer; + Next : PFPWasmThread; + Previous : PFPWasmThread; + end; + +Var + MainThread : TFPWasmThread; + threadvarblocksize : dword = 0; + TLSInitialized : Integer = 0; + +{$IFDEF DEBUG_MT} +Type + TSmallString = string[100]; + + +Procedure SetTLSMemory(aValue : Pointer); + +begin + fpc_wasm32_init_tls(aValue); +end; + +Function GetTLSMemory : Pointer; + +begin + Result:=fpc_wasm32_tls_base; +end; + + +Procedure RawWrite(var S : TSmallString); + +begin + // ToDo +end; +{$ENDIF DEBUG_MT} + +procedure WasmInitThreadvar(var offset : dword;size : dword); + +begin + threadvarblocksize:=align(threadvarblocksize, fpc_wasm32_tls_align); + offset:=threadvarblocksize; + inc(threadvarblocksize,size); +end; + + + +procedure WasmAllocateThreadVars; + +var + tlsMemBlock : pointer; + tlsBlockSize : Integer; + +begin + tlsBlockSize:=fpc_wasm32_tls_size; + if threadvarblocksize<>tlsBlocksize then + Writeln('Warning : block sizes differ: ',tlsBlocksize,'<>',threadvarblocksize,'(calculated) !'); +// DataIndex:=Pointer(Fpmmap(nil,threadvarblocksize,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0)); + FillChar(DataIndex^,threadvarblocksize,0); +// pthread_setspecific(tlskey,dataindex); +end; + +procedure WasmThreadCleanup(p: pointer); cdecl; + +{$ifdef DEBUG_MT} +var + s: TSmallString; // not an ansistring +{$endif DEBUG_MT} + +begin +{$ifdef DEBUG_MT} + s := 'finishing externally started thread'#10; + RawWrite(s); +{$endif DEBUG_MT} + { Restore tlskey value as it may already have been set to null, + in which case + a) DoneThread can't release the memory + b) accesses to threadvars from DoneThread or anything it + calls would allocate new threadvar memory + } + { clean up } + DoneThread; +pthread_setspecific(CleanupKey,nil); +end; + + + + +procedure HookThread; +{ Set up externally created thread } +begin + WasmAllocateThreadVars; + InitThread(1000000000); + pthread_setspecific(CleanupKey,getTlsMemory); +end; + + + +function WasmRelocateThreadvar(offset : dword) : pointer; + +var + P : Pointer; + +begin + P:=GetTLSMemory; + if (P=Nil) then + begin + HookThread; + P:=GetTLSMemory; + end; + WasmRelocateThreadvar:=P+Offset; +end; + + +procedure WasmReleaseThreadVars; +begin + Fpmunmap(pointer(pthread_getspecific(tlskey)),threadvarblocksize); +end; + + +function WasmThreadMain(param : pointer) : pointer; + +var +{$ifdef DEBUG_MT} + s: TSmallString; // not an ansistring +{$endif DEBUG_MT} + +begin +{$ifdef DEBUG_MT} + s := 'New thread started, initing threadvars'#10; + RawWrite(s); +{$endif DEBUG_MT} + { Must be first, many system unit things depend on threadvars} + WasmAllocateThreadVars; + { Copy parameter to local data } +{$ifdef DEBUG_MT} + s := 'New thread started, initialising ...'#10; + RawWrite(s); +{$endif DEBUG_MT} + ti:=pthreadinfo(param)^; + + { Initialize thread } + InitThread(ti.stklen); + + dispose(pthreadinfo(param)); + { Start thread function } +{$ifdef DEBUG_MT} + writeln('Jumping to thread function'); +{$endif DEBUG_MT} + WasmThreadMain:=pointer(ti.f(ti.p)); + DoneThread; + pthread_exit(WasmThreadMain); +end; + + + +Procedure InitWasmTLS; + +begin + if (InterLockedExchange(longint(TLSInitialized),1) = 0) then + begin + { We're still running in single thread mode, setup the TLS } + pthread_key_create(@TLSKey,nil); + InitThreadVars(@WasmRelocateThreadvar); + { used to clean up threads that we did not create ourselves: + a) the default value for a key (and hence also this one) in + new threads is NULL, and if it's still like that when the + thread terminates, nothing will happen + b) if it's non-NULL, the destructor routine will be called + when the thread terminates + -> we will set it to 1 if the threadvar relocation routine is + called from a thread we did not create, so that we can + clean up everything at the end } + pthread_key_create(@CleanupKey,@WasmthreadCleanup); + end +end; + +function WasmBeginThread(sa : Pointer;stacksize : PtrUInt; + ThreadFunction : tthreadfunc;p : pointer; + creationFlags : dword; var ThreadId : TThreadId) : TThreadID; +var + ti : pthreadinfo; + thread_attr : pthread_attr_t; +{$ifdef DEBUG_MT} + S : TSmallString; +{$endif DEBUG_MT} +begin +{$ifdef DEBUG_MT} + S:='Creating new thread'; + RawWrite(S); +{$endif DEBUG_MT} + { Initialize multithreading if not done } + if not TLSInitialized then + InitWasmTLS; + if not IsMultiThread then + begin + { We're still running in single thread mode, lazy initialize thread support } + LazyInitThreading; + IsMultiThread:=true; + end; + + { the only way to pass data to the newly created thread + in a MT safe way, is to use the heap } + new(ti); + ti^.f:=ThreadFunction; + ti^.p:=p; + ti^.stklen:=stacksize; + { call pthread_create } +{$ifdef DEBUG_MT} + S:='Starting new thread'; + RawWrite(S); +{$endif DEBUG_MT} + pthread_attr_init(@thread_attr); + {$if not defined(HAIKU)and not defined(BEOS) and not defined(ANDROID)} + {$if defined (solaris) or defined (netbsd) } + pthread_attr_setinheritsched(@thread_attr, PTHREAD_INHERIT_SCHED); + {$else not solaris} + pthread_attr_setinheritsched(@thread_attr, PTHREAD_EXPLICIT_SCHED); + {$endif not solaris} + {$ifend} + + // will fail under linux -- apparently unimplemented + pthread_attr_setscope(@thread_attr, PTHREAD_SCOPE_PROCESS); + + // don't create detached, we need to be able to join (waitfor) on + // the newly created thread! + //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED); + + // set the stack size + if (pthread_attr_setstacksize(@thread_attr, stacksize)<>0) or + // and create the thread + (pthread_create(ppthread_t(@threadid), @thread_attr, @ThreadMain,ti) <> 0) then + + begin + dispose(ti); + threadid := TThreadID(0); + end; + CBeginThread:=threadid; + pthread_attr_destroy(@thread_attr); +{$ifdef DEBUG_MT} + Str(ptrint(CBeginThread),S); + S:= 'BeginThread returning '+S; + RawWrite(S); +{$endif DEBUG_MT} +end; + + +procedure WasmEndThread(ExitCode : DWord); + +begin + DoneThread; + pthread_detach(pthread_t(pthread_self())); + pthread_exit(pointer(ptrint(ExitCode))); +end; + + + +function WasmSuspendThread (threadHandle : TThreadID) : dword; +// Not supported +begin + result:=dword(-1); +end; + + +function WasmResumeThread (threadHandle : TThreadID) : dword; +// Not supported +begin + result:=dword(-1); +end; + + + +procedure WasmThreadSwitch; {give time to other threads} + +begin + // Not supported +end; + + +function WasmKillThread (threadHandle : TThreadID) : dword; +begin + pthread_detach(pthread_t(threadHandle)); + WasmKillThread := pthread_cancel(pthread_t(threadHandle)); +end; + +function WasmCloseThread (threadHandle : TThreadID) : dword; + +begin + result:=0; +end; + +function WasmWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout} + +var + LResultP: Pointer; + +begin + pthread_join(pthread_t(threadHandle), @LResultP); + WasmWaitForThreadTerminate := dword(LResultP); +end; + +function WasmThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal} +begin + result:=false; +end; + +function WasmThreadGetPriority (threadHandle : TThreadID): Integer; +begin + result:=0; +end; + + + function CGetCurrentThreadId : TThreadID; + begin + CGetCurrentThreadId := TThreadID (pthread_self()); + end; + + + procedure CSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString); +{$if defined(Linux) or defined(Android)} + var + CuttedName: AnsiString; +{$endif} + begin +{$if defined(Linux) or defined(Android)} + if ThreadName = '' then + Exit; + {$ifdef dynpthreads} + if Assigned(pthread_setname_np) then + {$endif dynpthreads} + begin + // length restricted to 16 characters including terminating null byte + CuttedName:=Copy(ThreadName, 1, 15); + if threadHandle=TThreadID(-1) then + begin + pthread_setname_np(pthread_self(), @CuttedName[1]); + end + else + begin + pthread_setname_np(pthread_t(threadHandle), @CuttedName[1]); + end; + end; +{$elseif defined(Darwin) or defined(iphonesim)} + {$ifdef dynpthreads} + if Assigned(pthread_setname_np) then + {$endif dynpthreads} + begin + // only allowed to set from within the thread + if threadHandle=TThreadID(-1) then + pthread_setname_np(@ThreadName[1]); + end; +{$else} + {$Warning SetThreadDebugName needs to be implemented} +{$endif} + end; + + + procedure CSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString); + begin +{$if defined(Linux) or defined(Android)} + {$ifdef dynpthreads} + if Assigned(pthread_setname_np) then + {$endif dynpthreads} + begin + CSetThreadDebugNameA(threadHandle, AnsiString(ThreadName)); + end; +{$elseif defined(Darwin) or defined(iphonesim)} + {$ifdef dynpthreads} + if Assigned(pthread_setname_np) then + {$endif dynpthreads} + begin + CSetThreadDebugNameA(threadHandle, AnsiString(ThreadName)); + end; +{$else} + {$Warning SetThreadDebugName needs to be implemented} +{$endif} + end; + + +{***************************************************************************** + Delphi/Win32 compatibility +*****************************************************************************} + + procedure CInitCriticalSection(var CS); + + var + MAttr : pthread_mutexattr_t; + res: longint; + begin + res:=pthread_mutexattr_init(@MAttr); + if res=0 then + begin + res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE)); + if res=0 then + res := pthread_mutex_init(@CS,@MAttr) + else + { No recursive mutex support :/ } + fpc_threaderror + end + else + res:= pthread_mutex_init(@CS,NIL); + pthread_mutexattr_destroy(@MAttr); + if res <> 0 then + fpc_threaderror; + end; + + procedure CEnterCriticalSection(var CS); + begin + if pthread_mutex_lock(@CS) <> 0 then + fpc_threaderror + end; + + function CTryEnterCriticalSection(var CS):longint; + begin + if pthread_mutex_Trylock(@CS)=0 then + result:=1 // succes + else + result:=0; // failure + end; + + procedure CLeaveCriticalSection(var CS); + begin + if pthread_mutex_unlock(@CS) <> 0 then + fpc_threaderror + end; + + procedure CDoneCriticalSection(var CS); + begin + { unlock as long as unlocking works to unlock it if it is recursive + some Delphi code might call this function with a locked mutex } + while pthread_mutex_unlock(@CS)=0 do + ; + + if pthread_mutex_destroy(@CS) <> 0 then + fpc_threaderror; + end; + + +{***************************************************************************** + Semaphore routines +*****************************************************************************} + + +type + TPthreadCondition = pthread_cond_t; + TPthreadMutex = pthread_mutex_t; + Tbasiceventstate=record + FCondVar: TPthreadCondition; +{$if defined(Linux) and not defined(Android)} + FAttr: pthread_condattr_t; + FClockID: longint; +{$ifend} + FEventSection: TPthreadMutex; + FWaiters: longint; + FIsSet, + FManualReset, + FDestroying : Boolean; + end; + plocaleventstate = ^tbasiceventstate; +// peventstate=pointer; + +Const + wrSignaled = 0; + wrTimeout = 1; + wrAbandoned= 2; + wrError = 3; + +function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState; +var + MAttr : pthread_mutexattr_t; + res : cint; +{$if defined(Linux) and not defined(Android)} + timespec: ttimespec; +{$ifend} +begin + new(plocaleventstate(result)); + plocaleventstate(result)^.FManualReset:=AManualReset; + plocaleventstate(result)^.FWaiters:=0; + plocaleventstate(result)^.FDestroying:=False; + plocaleventstate(result)^.FIsSet:=InitialState; +{$if defined(Linux) and not defined(Android)} + res := pthread_condattr_init(@plocaleventstate(result)^.FAttr); + if (res <> 0) then + begin + FreeMem(result); + fpc_threaderror; + end; + + if clock_gettime(CLOCK_MONOTONIC_RAW, @timespec) = 0 then + begin + res := pthread_condattr_setclock(@plocaleventstate(result)^.FAttr, CLOCK_MONOTONIC_RAW); + end + else + begin + res := -1; // No support for CLOCK_MONOTONIC_RAW + end; + + if (res = 0) then + begin + plocaleventstate(result)^.FClockID := CLOCK_MONOTONIC_RAW; + end + else + begin + res := pthread_condattr_setclock(@plocaleventstate(result)^.FAttr, CLOCK_MONOTONIC); + if (res = 0) then + begin + plocaleventstate(result)^.FClockID := CLOCK_MONOTONIC; + end + else + begin + pthread_condattr_destroy(@plocaleventstate(result)^.FAttr); + FreeMem(result); + fpc_threaderror; + end; + end; + + res := pthread_cond_init(@plocaleventstate(result)^.FCondVar, @plocaleventstate(result)^.FAttr); + if (res <> 0) then + begin + pthread_condattr_destroy(@plocaleventstate(result)^.FAttr); + FreeMem(result); + fpc_threaderror; + end; +{$else} + res := pthread_cond_init(@plocaleventstate(result)^.FCondVar, nil); + if (res <> 0) then + begin + FreeMem(result); + fpc_threaderror; + end; +{$ifend} + + res:=pthread_mutexattr_init(@MAttr); + if res=0 then + begin + res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE)); + if Res=0 then + Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr) + else + res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil); + end + else + res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil); + + pthread_mutexattr_destroy(@MAttr); + if res <> 0 then + begin + pthread_cond_destroy(@plocaleventstate(result)^.FCondVar); +{$if defined(Linux) and not defined(Android)} + pthread_condattr_destroy(@plocaleventstate(result)^.FAttr); +{$ifend} + FreeMem(result); + fpc_threaderror; + end; +end; + +procedure Intbasiceventdestroy(state:peventstate); +begin + { safely mark that we are destroying this event } + pthread_mutex_lock(@plocaleventstate(state)^.feventsection); + plocaleventstate(state)^.FDestroying:=true; + + { send a signal to all threads that are waiting } + pthread_cond_broadcast(@plocaleventstate(state)^.FCondVar); + pthread_mutex_unlock(@plocaleventstate(state)^.feventsection); + + { now wait until they've finished their business } + while (plocaleventstate(state)^.FWaiters <> 0) do + cThreadSwitch; + + { and clean up } + pthread_cond_destroy(@plocaleventstate(state)^.Fcondvar); +{$if defined(Linux) and not defined(Android)} + pthread_condattr_destroy(@plocaleventstate(state)^.FAttr); +{$ifend} + pthread_mutex_destroy(@plocaleventstate(state)^.FEventSection); + dispose(plocaleventstate(state)); +end; + + +procedure IntbasiceventResetEvent(state:peventstate); +begin + pthread_mutex_lock(@plocaleventstate(state)^.feventsection); + plocaleventstate(state)^.fisset:=false; + pthread_mutex_unlock(@plocaleventstate(state)^.feventsection); +end; + +procedure IntbasiceventSetEvent(state:peventstate); +begin + pthread_mutex_lock(@plocaleventstate(state)^.feventsection); + plocaleventstate(state)^.Fisset:=true; + if not(plocaleventstate(state)^.FManualReset) then + pthread_cond_signal(@plocaleventstate(state)^.Fcondvar) + else + pthread_cond_broadcast(@plocaleventstate(state)^.Fcondvar); + pthread_mutex_unlock(@plocaleventstate(state)^.feventsection); +end; + +function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint; +var + timespec: ttimespec; + errres: cint; + isset: boolean; + tnow : timeval; +begin + + { safely check whether we are being destroyed, if so immediately return. } + { otherwise (under the same mutex) increase the number of waiters } + pthread_mutex_lock(@plocaleventstate(state)^.feventsection); + if (plocaleventstate(state)^.FDestroying) then + begin + pthread_mutex_unlock(@plocaleventstate(state)^.feventsection); + result := wrAbandoned; + exit; + end; + { not a regular inc() because it may happen simulatneously with the } + { interlockeddecrement() at the end } + interlockedincrement(plocaleventstate(state)^.FWaiters); + + //Wait without timeout using pthread_cond_wait + if Timeout = $FFFFFFFF then + begin + while (not plocaleventstate(state)^.FIsSet) and (not plocaleventstate(state)^.FDestroying) do + pthread_cond_wait(@plocaleventstate(state)^.Fcondvar, @plocaleventstate(state)^.feventsection); + end + else + begin + //Wait with timeout using pthread_cond_timedwait +{$if defined(Linux) and not defined(Android)} + if clock_gettime(plocaleventstate(state)^.FClockID, @timespec) <> 0 then + begin + Result := Ord(wrError); + Exit; + end; + timespec.tv_sec := timespec.tv_sec + (clong(timeout) div 1000); + timespec.tv_nsec := ((clong(timeout) mod 1000) * 1000000) + (timespec.tv_nsec); +{$else} + // TODO: FIX-ME: Also use monotonic clock for other *nix targets + fpgettimeofday(@tnow, nil); + timespec.tv_sec := tnow.tv_sec + (clong(timeout) div 1000); + timespec.tv_nsec := ((clong(timeout) mod 1000) * 1000000) + (tnow.tv_usec * 1000); +{$ifend} + if timespec.tv_nsec >= 1000000000 then + begin + inc(timespec.tv_sec); + dec(timespec.tv_nsec, 1000000000); + end; + errres := 0; + while (not plocaleventstate(state)^.FDestroying) and + (not plocaleventstate(state)^.FIsSet) and + (errres<>ESysETIMEDOUT) do + errres := pthread_cond_timedwait(@plocaleventstate(state)^.Fcondvar, + @plocaleventstate(state)^.feventsection, + @timespec); + end; + + isset := plocaleventstate(state)^.FIsSet; + + { if ManualReset=false, reset the event immediately. } + if (plocaleventstate(state)^.FManualReset=false) then + plocaleventstate(state)^.FIsSet := false; + + //check the results... + if plocaleventstate(state)^.FDestroying then + Result := wrAbandoned + else + if IsSet then + Result := wrSignaled + else + begin + if errres=ESysETIMEDOUT then + Result := wrTimeout + else + Result := wrError; + end; + + pthread_mutex_unlock(@plocaleventstate(state)^.feventsection); + + { don't put this above the previous pthread_mutex_unlock, because } + { otherwise we can get errors in case an object is destroyed between } + { end of the wait/sleep loop and the signalling above. } + { The pthread_mutex_unlock above takes care of the memory barrier } + interlockeddecrement(plocaleventstate(state)^.FWaiters); +end; + +function intRTLEventCreate: PRTLEvent; + +var p:pintrtlevent; + +begin + new(p); + if not assigned(p) then + fpc_threaderror; + if pthread_cond_init(@p^.condvar, nil)<>0 then + begin + dispose(p); + fpc_threaderror; + end; + if pthread_mutex_init(@p^.mutex, nil)<>0 then + begin + pthread_cond_destroy(@p^.condvar); + dispose(p); + fpc_threaderror; + end; + p^.isset:=false; + result:=PRTLEVENT(p); +end; + +procedure intRTLEventDestroy(AEvent: PRTLEvent); + +var p:pintrtlevent; + +begin + p:=pintrtlevent(aevent); + pthread_cond_destroy(@p^.condvar); + pthread_mutex_destroy(@p^.mutex); + dispose(p); +end; + +procedure intRTLEventSetEvent(AEvent: PRTLEvent); +var p:pintrtlevent; + +begin + p:=pintrtlevent(aevent); + pthread_mutex_lock(@p^.mutex); + p^.isset:=true; + pthread_cond_signal(@p^.condvar); + pthread_mutex_unlock(@p^.mutex); +end; + + +procedure intRTLEventResetEvent(AEvent: PRTLEvent); +var p:pintrtlevent; + +begin + p:=pintrtlevent(aevent); + pthread_mutex_lock(@p^.mutex); + p^.isset:=false; + pthread_mutex_unlock(@p^.mutex); +end; + + +procedure intRTLEventWaitFor(AEvent: PRTLEvent); +var p:pintrtlevent; + +begin + p:=pintrtlevent(aevent); + pthread_mutex_lock(@p^.mutex); + while not p^.isset do pthread_cond_wait(@p^.condvar, @p^.mutex); + p^.isset:=false; + pthread_mutex_unlock(@p^.mutex); +end; + +procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint); + var + p : pintrtlevent; + errres : cint; + timespec : ttimespec; + tnow : timeval; + + begin + p:=pintrtlevent(aevent); + fpgettimeofday(@tnow,nil); + timespec.tv_sec:=tnow.tv_sec+(timeout div 1000); + timespec.tv_nsec:=(timeout mod 1000)*1000000 + tnow.tv_usec*1000; + if timespec.tv_nsec >= 1000000000 then + begin + inc(timespec.tv_sec); + dec(timespec.tv_nsec, 1000000000); + end; + errres:=0; + pthread_mutex_lock(@p^.mutex); + while (not p^.isset) and + (errres <> ESysETIMEDOUT) do + begin + errres:=pthread_cond_timedwait(@p^.condvar, @p^.mutex, @timespec); + end; + p^.isset:=false; + pthread_mutex_unlock(@p^.mutex); + end; + + +type + threadmethod = procedure of object; + + +Function CInitThreads : Boolean; + +begin +{$ifdef DEBUG_MT} + Writeln('Entering InitThreads.'); +{$endif} +{$ifndef dynpthreads} + Result:=True; +{$else} + Result:=LoadPthreads; +{$endif} + ThreadID := TThreadID (pthread_self()); +{$ifdef DEBUG_MT} + Writeln('InitThreads : ',Result); +{$endif DEBUG_MT} + // We assume that if you set the thread manager, the application is multithreading. + InitCTLS; +end; + +Function CDoneThreads : Boolean; + +begin +{$ifndef dynpthreads} + Result:=True; +{$else} + Result:=UnloadPthreads; +{$endif} +end; + + +Var + CThreadManager : TThreadManager; + +Procedure SetCThreadManager; + +begin + With CThreadManager do begin + InitManager :=@WasmInitThreads; + DoneManager :=@WasmDoneThreads; + BeginThread :=@WasmBeginThread; + EndThread :=@WasmEndThread; + SuspendThread :=@WasmSuspendThread; + ResumeThread :=@WasmResumeThread; + KillThread :=@WasmKillThread; + ThreadSwitch :=@WasmThreadSwitch; + CloseThread :=@WasmCloseThread; + WaitForThreadTerminate :=@WasmWaitForThreadTerminate; + ThreadSetPriority :=@WasmThreadSetPriority; + ThreadGetPriority :=@WasmThreadGetPriority; + GetCurrentThreadId :=@WasmGetCurrentThreadId; + SetThreadDebugNameA :=@WasmSetThreadDebugNameA; + SetThreadDebugNameU :=@WasmSetThreadDebugNameU; + InitCriticalSection :=@WasmInitCriticalSection; + DoneCriticalSection :=@WasmDoneCriticalSection; + EnterCriticalSection :=@WasmEnterCriticalSection; + TryEnterCriticalSection:=@WasmTryEnterCriticalSection; + LeaveCriticalSection :=@WasmLeaveCriticalSection; + InitThreadVar :=@WasmInitThreadVar; + RelocateThreadVar :=@WasmRelocateThreadVar; + AllocateThreadVars :=@WasmAllocateThreadVars; + ReleaseThreadVars :=@WasmReleaseThreadVars; + BasicEventCreate :=@intBasicEventCreate; + BasicEventDestroy :=@intBasicEventDestroy; + BasicEventResetEvent :=@intBasicEventResetEvent; + BasicEventSetEvent :=@intBasicEventSetEvent; + BasiceventWaitFor :=@intBasiceventWaitFor; + rtlEventCreate :=@intrtlEventCreate; + rtlEventDestroy :=@intrtlEventDestroy; + rtlEventSetEvent :=@intrtlEventSetEvent; + rtlEventResetEvent :=@intrtlEventResetEvent; + rtleventWaitForTimeout :=@intrtleventWaitForTimeout; + rtleventWaitFor :=@intrtleventWaitFor; + end; + SetThreadManager(CThreadManager); +end; + + +initialization + if ThreadingAlreadyUsed then + begin + writeln('Threading has been used before cthreads was initialized.'); + writeln('Make wasmthreads one of the first units in your uses clause.'); + runerror(211); + end; + SetWasmThreadManager; +finalization +end. diff --git a/packages/rtl/rtl.webthreads.pas b/packages/rtl/rtl.webthreads.pas new file mode 100644 index 0000000..b41aa42 --- /dev/null +++ b/packages/rtl/rtl.webthreads.pas @@ -0,0 +1,477 @@ +unit Rtl.WebThreads; + +{$mode ObjFPC} +{$modeswitch externalclass} + +interface + +uses + JS, SysUtils, wasienv, webassembly; + +Const + // Each thread starts spawning at 1000*IndexOfWorker + ThreadIDInterval = 1000; + // When the thread ID reaches this limit, then it requests a new block + ThreadIDMargin = 2; + + // lowercase !! + cmdConsole = 'console'; + cmdException = 'exception'; + cmdCleanup = 'cleanup'; + cmdCancel = 'cancel'; + cmdLoaded = 'loaded'; + cmdKill = 'kill'; + cmdNeedIdBlock = 'needidblock'; + cmdThreadIdRange = 'threadidrange'; + cmdSpawn = 'spawn'; + cmdLoad = 'load'; + cmdRun = 'run'; + + DefaultThreadWorker = 'pas2jsthreadworker.js'; + DefaultThreadCount = 2; + DefaultMaxWorkerCount = 100; + + // Default exported thread entry point. Must have signature TThreadEntryPointFunction + DefaultThreadEntryPoint = 'FPC_WASM_THREAD_ENTRY'; + // Default exported thread instance point. Must have signature TThreadInitInstanceFunction + DefaultThreadInstanceInitPoint = 'FPC_WASM_THREAD_INIT'; + + // Imports to wasi env. + sThreadSpawn = 'thread_spawn'; + sThreadDetach = 'thread_detach'; + sThreadCancel = 'thread_cancel'; + sThreadSelf = 'thread_self'; + + + +Type + // aRunProc and aArgs are pointers inside wasm. + TThreadEntryPointFunction = Function(ThreadId: Integer; aRunProc : Integer; aArgs: Integer) : Integer; + TThreadInitInstanceFunction = Function(IsWorkerThread : Longint; IsMainThread : Integer; CanBlock : Integer) : Integer; + + EWasmThreads = class(Exception); + + // Commands sent between thread workers and main program. + + { Basic TWorkerCommand. Command is the actual command } + + { we do not use Pascal classes for this, to avoid transferring unnecessary metadata present in the pascal class } + + TWorkerCommand = Class external name 'Object' (TJSObject) + Command : String; + ThreadID : Integer; // Meaning depends on actual command. + TargetID : Integer; // Forward to thread ID + end; + TCommandNotifyEvent = Procedure (Sender : TObject; aCommand : TWorkerCommand) of object; + + { TWorkerCommandHelper } + + TWorkerCommandHelper = class helper for TWorkerCommand + Class function NewWorker(const aCommand : string; aThreadID : Integer = -1) : TWorkerCommand; static; + end; + + { TWorkerExceptionCommand } + + // When an unexpected error occurred. + TWorkerExceptionCommand = class external name 'Object' (TWorkerCommand) + public + ExceptionClass: String; + ExceptionMessage: String; + end; + + { TWorkerExceptionCommandHelper } + + TWorkerExceptionCommandHelper = class helper for TWorkerExceptionCommand + Class function CommandName : string; static; + Class function CreateNew(const aExceptionClass,aExceptionMessage : string; aThreadID : Integer = -1) : TWorkerExceptionCommand; static; + end; + + { TWorkerConsoleCommand } + + // Sent by worker to main: write message to console + // Thread ID : sending console ID + TWorkerConsoleCommand = class external name 'Object' (TWorkerCommand) + public + ConsoleMessage : String; + end; + + { TWorkerConsoleCommandHelper } + + TWorkerConsoleCommandHelper = class helper for TWorkerConsoleCommand + Class function CommandName : string; static; + Class function Create(const aMessage : string; aThreadID : Integer = -1) : TWorkerConsoleCommand; static; reintroduce; + Class function Create(const aMessage : array of JSValue; aThreadID : Integer = -1) : TWorkerConsoleCommand; static; reintroduce; + end; + + // Cleanup thread info: put this worker into unusued workers + TWorkerCleanupCommand = class external name 'Object' (TWorkerCommand) + end; + + { TWorkerCleanupCommandHelper } + + TWorkerCleanupCommandHelper = class helper for TWorkerCleanupCommand + Class function CommandName : string; static; + Class function Create(aThreadID : Integer): TWorkerCleanupCommand; static; reintroduce; + end; + + + { TWorkerKillCommand } + // Kill thread (thread ID in ThreadID) + TWorkerKillCommand = class external name 'Object' (TWorkerCommand) + end; + + { TWorkerCleanupCommandHelper } + + TWorkerKillCommandHelper = class helper for TWorkerKillCommand + Class function CommandName : string; static; + Class function Create(aThreadID : Integer): TWorkerKillCommand; static;reintroduce; + end; + + // Cancel thread (thread ID in ThreadID) + TWorkerCancelCommand = class external name 'Object' (TWorkerCommand) + end; + + { TWorkerCancelCommandHelper } + + TWorkerCancelCommandHelper = class helper for TWorkerCancelCommand + Class function CommandName : string; static; + Class function Create(aThreadID : Integer): TWorkerCancelCommand; static; reintroduce; + end; + + // sent to notify main thread that the wasm module is loaded. + TWorkerLoadedCommand = class external name 'Object' (TWorkerCommand) + end; + + { TWorkerLoadedCommandHelper } + + TWorkerLoadedCommandHelper = class helper for TWorkerLoadedCommand + Class function CommandName : string; static; + Class function Create: TWorkerLoadedCommand; static; reintroduce; + end; + + // Sent to notify main thread that a new range of IDs is needed. + TWorkerNeedIdBlockCommand = class external name 'Object' (TWorkerCommand) + Current : NativeInt; + end; + + { TWorkerNeedIdBlockCommandHelper } + + TWorkerNeedIdBlockCommandHelper = class helper for TWorkerNeedIdBlockCommand + Class function CommandName : string; static; + Class function Create(aCurrent : NativeInt): TWorkerNeedIdBlockCommand; static; reintroduce; + end; + + + // Sent to notify main thread that a new thread must be started. + // Worker cannot start new thread. It allocates the ID (threadId) + // It sends RunFunction, Attributes and Arguments received by thread_spawn call. + TWorkerSpawnThreadCommand = class external name 'Object' (TWorkerCommand) + Attributes : Integer; + Arguments : Integer; + RunFunction : Integer; + ThreadInfo : integer; + end; + + { TWorkerSpawnThreadCommandHelper } + + TWorkerSpawnThreadCommandHelper = class helper for TWorkerSpawnThreadCommand + Class function CommandName : string; static; + Class function Create(aThreadID : integer; aAttrs,aArgs,aRun,aThreadInfo : Integer): TWorkerSpawnThreadCommand; static;reintroduce; + end; + + + + // Sent by main to worker: load wasm module + TWorkerLoadCommand = class external name 'Object' (TWorkerCommand) + public + Memory : TJSWebAssemblyMemory; + Module : TJSWebAssemblyModule; + ThreadRangeStart : NativeInt; + end; + + { TWorkerLoadCommandHelper } + + TWorkerLoadCommandHelper = class helper for TWorkerLoadCommand + Class function CommandName : string; static; + Class function Create(aStartThreadIdRange : integer; aModule : TJSWebAssemblyModule; aMemory : TJSWebAssemblyMemory): TWorkerLoadCommand; static;reintroduce; + end; + + + // Sent by main to worker: run thread procedure + TWorkerRunCommand = class external name 'Object' (TWorkerCommand) + public + ThreadInfo : Integer; + RunThreadProc : Integer; + Attrs : Integer; + Args : Integer; + end; + + { TWorkerRunCommandHelper } + + TWorkerRunCommandHelper = class helper for TWorkerRunCommand + Class function CommandName : string; static; + Class function Create(aThreadID, aRunProc, aAttrs, aArgs, aThreadInfoLocation : integer): TWorkerRunCommand; static; reintroduce; + end; + + + // Sent to worker with new range of thread IDs. + TWorkerThreadIDRangeCommand = class external name 'Object' (TWorkerCommand) + RangeStart : NativeInt; + end; + + { TWorkerThreadIDRangeCommandHelper } + + TWorkerThreadIDRangeCommandHelper = class helper for TWorkerThreadIDRangeCommand + Class function CommandName : string; static; + class function Create(aRangeStart: NativeInt): TWorkerThreadIDRangeCommand; static; reintroduce; + end; + + + + TThreadinfo = record + OriginThreadID : Integer; // Numerical thread ID + ThreadID : Integer; // Numerical thread ID + ThreadInfoLocation : Integer; // Location of thread block (pointer) + RunFunction : Integer; // Location of thread function (pointer) + Attributes : Integer; // Unused for the moment + Arguments : Integer; // Arguments (pointer) + end; + + // This basis object has the thread support that is needed by the WASM module. + // It relies on descendents to implement the actual calls. + + { TWasmThreadSupport } + + TWasmThreadSupport = Class (TImportExtension) + private + FOnSendCommand: TCommandNotifyEvent; + Protected + // Proposed WASI standard, modeled after POSIX pthreads. + Function thread_spawn(thread_id : Integer; attrs: Integer; thread_start_func : Integer; args : Integer) : Integer; virtual; abstract; + Function thread_detach(thread_id : Integer) : Integer; virtual; abstract; + Function thread_cancel(thread_id : Integer) : Integer; virtual; abstract; + Function thread_self() : Integer; virtual; abstract; + Public + Function ImportName : String; override; + procedure FillImportObject(aObject: TJSObject); override; + Procedure HandleCommand(aCommand : TWorkerCommand); virtual; + Procedure SendCommand(aCommand : TWorkerCommand); virtual; + // Set this to actually send commands. Normally set by TWorkerWASIHostApplication + Property OnSendCommand : TCommandNotifyEvent Read FOnSendCommand Write FOnSendCommand; + end; + + +implementation + +{ TWorkerRunCommandHelper } + +class function TWorkerRunCommandHelper.CommandName: string; +begin + Result:=cmdRun; +end; + +class function TWorkerRunCommandHelper.Create(aThreadID, aRunProc, aAttrs, + aArgs, aThreadInfoLocation: integer): TWorkerRunCommand; +begin + Result:=TWorkerRunCommand(TWorkerCommand.NewWorker(CommandName)); + Result.ThreadID:=aThreadID; + Result.ThreadInfo:=aThreadInfoLocation; + Result.RunThreadProc:=aRunProc; + Result.Attrs:=aAttrs; + Result.Args:=aArgs; +end; + +{ TWorkerLoadCommandHelper } + +class function TWorkerLoadCommandHelper.CommandName: string; +begin + Result:=cmdLoad; +end; + +class function TWorkerLoadCommandHelper.Create(aStartThreadIdRange: integer; + aModule: TJSWebAssemblyModule; aMemory: TJSWebAssemblyMemory + ): TWorkerLoadCommand; +begin + Result:=TWorkerLoadCommand(TWorkerCommand.NewWorker(CommandName)); + Result.ThreadRangeStart:=aStartThreadIdRange; + Result.Memory:=aMemory; + Result.Module:=aModule; +end; + +{ TWorkerSpawnThreadCommandHelper } + +class function TWorkerSpawnThreadCommandHelper.CommandName: string; +begin + Result:=cmdSpawn +end; + +class function TWorkerSpawnThreadCommandHelper.Create(aThreadID: integer; + aAttrs, aArgs, aRun, aThreadInfo: Integer): TWorkerSpawnThreadCommand; +begin + Result:=TWorkerSpawnThreadCommand(TWorkerCommand.NewWorker(CommandName,aThreadID)); + Result.Arguments:=aArgs; + Result.Attributes:=aAttrs; + Result.RunFunction:=aRun; + Result.ThreadInfo:=aThreadInfo; +end; + +{ TWorkerThreadIDRangeCommandHelper } + +class function TWorkerThreadIDRangeCommandHelper.CommandName: string; +begin + Result:=cmdThreadIdRange; +end; + +class function TWorkerThreadIDRangeCommandHelper.Create(aRangeStart: NativeInt + ): TWorkerThreadIDRangeCommand; +begin + Result:=TWorkerThreadIDRangeCommand(TWorkerCommand.NewWorker(CommandName)); + Result.RangeStart:=aRangeStart; +end; + +{ TWorkerNeedIdBlockCommandHelper } + +class function TWorkerNeedIdBlockCommandHelper.CommandName: string; +begin + Result:=cmdNeedIdBlock; +end; + +class function TWorkerNeedIdBlockCommandHelper.Create(aCurrent: NativeInt + ): TWorkerNeedIdBlockCommand; +begin + Result:=TWorkerNeedIdBlockCommand(TWorkerCommand.NewWorker(CommandName)); + Result.Current:=aCurrent; +end; + + +{ TWorkerLoadedCommandHelper } + +class function TWorkerLoadedCommandHelper.CommandName: string; +begin + Result:=cmdLoaded; +end; + +class function TWorkerLoadedCommandHelper.Create: TWorkerLoadedCommand; +begin + Result:=TWorkerLoadedCommand(TWorkerCommand.NewWorker(CommandName)); +end; + +{ TWorkerCancelCommandHelper } + +class function TWorkerCancelCommandHelper.CommandName: string; +begin + result:=cmdCancel; +end; + +class function TWorkerCancelCommandHelper.Create(aThreadID: Integer + ): TWorkerCancelCommand; +begin + Result:=TWorkerCancelCommand(TWorkerCommand.NewWorker(CommandName,aThreadID)); +end; + +{ TWorkerKillCommandHelper } + +class function TWorkerKillCommandHelper.CommandName: string; +begin + Result:=cmdKill +end; + +class function TWorkerKillCommandHelper.Create(aThreadID : Integer): TWorkerKillCommand; +begin + Result:=TWorkerKillCommand(TWorkerCommand.NewWorker(CommandName,aThreadID)); +end; + +{ TWorkerCleanupCommandHelper } + +class function TWorkerCleanupCommandHelper.CommandName: string; +begin + Result:=cmdCleanup +end; + +class function TWorkerCleanupCommandHelper.Create(aThreadID: Integer): TWorkerCleanupCommand; +begin + Result:=TWorkerCleanupCommand(TWorkerCommand.NewWorker(CommandName,aThreadID)); +end; + +{ TWorkerConsoleCommandHelper } + +class function TWorkerConsoleCommandHelper.CommandName: string; +begin + Result:=cmdConsole; +end; + +class function TWorkerConsoleCommandHelper.Create( + const aMessage: string; aThreadID : Integer = -1): TWorkerConsoleCommand; +begin + Result:=TWorkerConsoleCommand(TWorkerCommand.NewWorker(CommandName,aThreadID)); + Result.ConsoleMessage:=aMessage; +end; + +class function TWorkerConsoleCommandHelper.Create( + const aMessage: array of JSValue; aThreadID : Integer = -1): TWorkerConsoleCommand; +begin + Result:=Create(TJSArray(aMessage).join(' '),aThreadID); +end; + +{ TWorkerExceptionCommandHelper } + +class function TWorkerExceptionCommandHelper.CommandName: string; +begin + Result:=cmdException; +end; + +class function TWorkerExceptionCommandHelper.CreateNew(const aExceptionClass,aExceptionMessage: string; aThreadID : Integer = -1 ): TWorkerExceptionCommand; +begin + Result:=TWorkerExceptionCommand(TWorkerCommand.NewWorker(CommandName,aThreadID)); + Result.ExceptionClass:=aExceptionClass; + Result.ExceptionMessage:=aExceptionMessage; +end; + +{ TWorkerCommandHelper } + +class function TWorkerCommandHelper.NewWorker(const aCommand : string; aThreadID : Integer = -1): TWorkerCommand; +begin + Result:=TWorkerCommand.New; + Result.Command:=LowerCase(aCommand); + if aThreadID<>-1 then + Result.ThreadID:=aThreadID; +end; + + +{ TWasmThreadSupport } + +function TWasmThreadSupport.ImportName: String; +begin + Result:='FPCThreading'; +end; + +procedure TWasmThreadSupport.FillImportObject(aObject: TJSObject); +begin + aObject[sThreadSpawn]:=@Thread_Spawn; + aObject[sThreadDetach]:=@Thread_Detach; + aObject[sThreadCancel]:=@Thread_Cancel; + aObject[sThreadSelf]:=@Thread_Self; +end; + + +procedure TWasmThreadSupport.HandleCommand(aCommand: TWorkerCommand); + +Var + P : TWorkerExceptionCommand; + +begin + P:=TWorkerExceptionCommand.New; + P.ExceptionClass:='ENotSupportedException'; + P.ExceptionMessage:='Unsupported command : '+TJSJSON.Stringify(aCommand); + SendCommand(aCommand); +end; + +procedure TWasmThreadSupport.SendCommand(aCommand: TWorkerCommand); +begin + if Assigned(FOnSendCommand) then + FOnSendCommand(Self,aCommand); +end; + + +end. + diff --git a/packages/rtl/webassembly.pas b/packages/rtl/webassembly.pas index e166e73..31edd3b 100644 --- a/packages/rtl/webassembly.pas +++ b/packages/rtl/webassembly.pas @@ -6,7 +6,7 @@ unit webassembly; interface uses - js, Weborworker; + js; Type { TJSWebAssemblyMemory } @@ -36,7 +36,7 @@ Type FMemory : TJSWebAssemblyMemory; external name 'memory'; function GetFun(aName : String): TJSFunction; external name '[]'; public - Property Memory : TJSWebAssemblyMemory Read FMemory; + Property Memory : TJSWebAssemblyMemory Read FMemory Write fMemory; Property functions [aName : String] : TJSFunction read GetFun; default; end; @@ -80,9 +80,9 @@ Type Class Function instantiate(Buffer : TJSWebAssemblyModule; ImportObject : TJSObject) : TJSPromise; overload; Class Function instantiate(Buffer : TJSWebAssemblyModule) : TJSPromise; overload; Class Function compile(Buffer : TJSArrayBuffer): TJSPromise; - Class Function compileStreaming(source : TJSResponse): TJSPromise; - Class Function instantiateStreaming(source : TJSResponse; ImportObject : TJSObject) : TJSPromise; overload; - Class Function instantiateStreaming(source : TJSResponse) : TJSPromise; overload; + Class Function compileStreaming(source : TJSObject): TJSPromise; + Class Function instantiateStreaming(source : TJSObject; ImportObject : TJSObject) : TJSPromise; overload; + Class Function instantiateStreaming(source : TJSObject) : TJSPromise; overload; Class Function validate(Buffer : TJSArrayBuffer): Boolean; end; diff --git a/packages/wasi/pas2jsthreadworker.lpi b/packages/wasi/pas2jsthreadworker.lpi new file mode 100644 index 0000000..2be9175 --- /dev/null +++ b/packages/wasi/pas2jsthreadworker.lpi @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="12"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + <MainUnitHasScaledStatement Value="False"/> + <Runnable Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <Title Value="pas2jsthreadworker"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <Units> + <Unit> + <Filename Value="pas2jsthreadworker.pas"/> + <IsPartOfProject Value="True"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <Target FileExt=".js"> + <Filename Value="pas2jsthreadworker"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="js"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <AllowLabel Value="False"/> + <CPPInline Value="False"/> + <UseAnsiStrings Value="False"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <TargetOS Value="nodejs"/> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + <UseLineInfoUnit Value="False"/> + </Debugging> + </Linking> + <Other> + <CustomOptions Value="-Jeutf-8 -Jminclude -Jirtl.js"/> + <CompilerPath Value="$(pas2js)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/packages/wasi/pas2jsthreadworker.pas b/packages/wasi/pas2jsthreadworker.pas new file mode 100644 index 0000000..1f762f6 --- /dev/null +++ b/packages/wasi/pas2jsthreadworker.pas @@ -0,0 +1,22 @@ +program pas2jsthreadworker; + +{$mode objfpc} + +uses + Classes, WasiWorkerThreadHost; + +type + { TApplication } + + TApplication = class(TWorkerWASIHostApplication) + end; + +{ TApplication } + +var + App: TApplication; + +begin + App:=TApplication.Create(nil); + App.Run; +end. diff --git a/packages/wasi/wasienv.pas b/packages/wasi/wasienv.pas index 7fdedaf..316cb3a 100644 --- a/packages/wasi/wasienv.pas +++ b/packages/wasi/wasienv.pas @@ -4,7 +4,8 @@ unit wasienv; {$mode ObjFPC} {$modeswitch externalclass} {$INTERFACES CORBA} - +{$WARN 5024 off} +{$WARN 4501 off} interface uses @@ -327,10 +328,12 @@ type FOnStdOutputWrite: TWASIWriteEvent; FImportExtensions : TFPList; FWASIImportName : string; + FMemory : TJSWebAssemblyMemory; function GetConsoleInputBuffer: TJSUint8Array; function GetFileBuffer(FD: NativeInt): TJSUint8Array; function GetImportObject: TJSObject; function getiovs(view: TJSDataView; iovs, iovsLen: NativeInt): TJSArray; + function GetMemory: TJSWebassemblyMemory; procedure SetInstance(AValue: TJSWebAssemblyInstance); Protected Class Var UTF8TextDecoder: TJSTextDecoder; @@ -392,6 +395,8 @@ type function sock_recv() : NativeInt; virtual; function sock_send() : NativeInt; virtual; function sock_shutdown() : NativeInt; virtual; + Protected + Procedure SetMemory(aMemory : TJSWebAssemblyMemory); Public class constructor init; Constructor Create; @@ -414,6 +419,7 @@ type Property OnGetConsoleInputBuffer : TGetConsoleInputBufferEvent Read FOnGetConsoleInputBuffer Write FOnGetConsoleInputBuffer; Property OnGetConsoleInputString : TGetConsoleInputStringEvent Read FOnGetConsoleInputString Write FOnGetConsoleInputString; Property Instance : TJSWebAssemblyInstance Read Finstance Write SetInstance; + Property Memory : TJSWebassemblyMemory Read GetMemory; Property Exitcode : Nativeint Read FExitCode; // Default is set to the one expected by FPC runtime: wasi_snapshot_preview1 Property WASIImportName : String Read FWASIImportName Write FWASIImportName; @@ -434,11 +440,25 @@ type Property Env : TPas2JSWASIEnvironment Read FEnv; end; + TRunWebassemblyProc = reference to Procedure(aExports : TWASIExports); TWebAssemblyStartDescriptor = record + // Module + Module : TJSWebAssemblyModule; + // memory to use Memory : TJSWebAssemblyMemory; + // Table to use Table : TJSWebAssemblyTable; + // Exports of module Exported : TWASIExports; + // Imports of module + Imports : TJSOBject; + // Instance Instance : TJSWebAssemblyInstance; + // Procedure to actually run a function. + CallRun : TRunWebassemblyProc; + // After run, if an exception occurred, this is filled with error class/message. + RunExceptionClass : String; + RunExceptionMessage : String; end; @@ -448,6 +468,8 @@ type TBeforeStartEvent = Procedure (Sender : TObject; aDescriptor : TWebAssemblyStartDescriptor; var aAllowRun : Boolean) of object; TAfterStartEvent = Procedure (Sender : TObject; aDescriptor : TWebAssemblyStartDescriptor) of object; + TFailEvent = Procedure (Sender : TObject; aFail : JSValue) of object; + TConsoleReadEvent = Procedure(Sender : TObject; Var AInput : String) of object; TConsoleWriteEvent = Procedure (Sender : TObject; aOutput : string) of object; @@ -455,10 +477,15 @@ type TWASIHost = Class(TComponent) Private + FAfterInstantation: TNotifyEvent; FAfterStart: TAfterStartEvent; + FBeforeInstantation: TNotifyEvent; FBeforeStart: TBeforeStartEvent; FEnv: TPas2JSWASIEnvironment; FExported: TWASIExports; + FOnInstantiateFail: TFailEvent; + FOnLoadFail: TFailEvent; + FPreparedStartDescriptor: TWebAssemblyStartDescriptor; FMemoryDescriptor : TJSWebAssemblyMemoryDescriptor; FOnConsoleRead: TConsoleReadEvent; FOnConsoleWrite: TConsoleWriteEvent; @@ -466,21 +493,51 @@ type FReadLineCount : Integer; FRunEntryFunction: String; FTableDescriptor : TJSWebAssemblyTableDescriptor; + function GetStartDescriptorReady: Boolean; + function GetUseSharedMemory: Boolean; procedure SetPredefinedConsoleInput(AValue: TStrings); + procedure SetUseSharedMemory(AValue: Boolean); protected + // Called after instantiation was OK. + Procedure DoAfterInstantiate; virtual; + // Called before instantiation starts. + Procedure DoBeforeInstantiate; virtual; + // Called when loading fails + Procedure DoLoadFail(aError : JSValue); virtual; + // Called when instantiating fails + Procedure DoInstantiateFail(aError : JSValue); virtual; + // Prepare start descriptor + Procedure PrepareWebAssemblyInstance(aDescr: TWebAssemblyStartDescriptor); virtual; + // Call the run function on an instantiated webassembly + function RunWebAssemblyInstance(aBeforeStart: TBeforeStartCallback; aAfterStart: TAfterStartCallback; aRun : TRunWebassemblyProc): Boolean; virtual; overload; + // Prepare and run web assembly instance. + function RunWebAssemblyInstance(aDescr: TWebAssemblyStartDescriptor; aBeforeStart: TBeforeStartCallback; aAfterStart: TAfterStartCallback): Boolean; overload; + // Standard Input/Output reads procedure DoStdRead(Sender: TObject; var AInput: string); virtual; procedure DoStdWrite(Sender: TObject; const aOutput: String); virtual; + // Load file from path ans instantiate a webassembly from it. function CreateWebAssembly(aPath: string; aImportObject: TJSObject): TJSPromise; virtual; + // Create a WASI environment. Called during constructor, override to customize. Function CreateWasiEnvironment : TPas2JSWASIEnvironment; virtual; + // Create Standard webassembly table description function GetTable: TJSWebAssemblyTable; virtual; + // Create tandard webassembly memory. function GetMemory: TJSWebAssemblyMemory; virtual; public Constructor Create(aOwner : TComponent); override; Destructor Destroy; override; + // Will call OnConsoleWrite or write to console + procedure WriteOutput(const aOutput: String); virtual; + // Get prepared descriptor + Property PreparedStartDescriptor : TWebAssemblyStartDescriptor Read FPreparedStartDescriptor; + // Initialize a start descriptor. + function InitStartDescriptor(aMemory: TJSWebAssemblyMemory; aTable: TJSWebAssemblyTable; aImportObj: TJSObject): TWebAssemblyStartDescriptor; // Load and start webassembly. If DoRun is true, then Webassembly entry point is called. // If aBeforeStart is specified, then it is called prior to calling run, and can disable running. // If aAfterStart is specified, then it is called after calling run. It is not called if running was disabled. - Procedure StartWebAssembly(aPath: string; DoRun : Boolean = True; aBeforeStart : TBeforeStartCallback = Nil; aAfterStart : TAfterStartCallback = Nil); + Procedure StartWebAssembly(aPath: string; DoRun: Boolean; aBeforeStart: TBeforeStartCallback; aAfterStart: TAfterStartCallback); + // Run the prepared descriptor + Procedure RunPreparedDescriptor; // Initial memory descriptor Property MemoryDescriptor : TJSWebAssemblyMemoryDescriptor Read FMemoryDescriptor Write FMemoryDescriptor; // Import/export table descriptor @@ -489,8 +546,11 @@ type Property WasiEnvironment : TPas2JSWASIEnvironment Read FEnv; // Exported functions. Also available in start descriptor. Property Exported : TWASIExports Read FExported; + // Is the descriptor prepared ? + Property StartDescriptorReady : Boolean Read GetStartDescriptorReady; // Default console input Property PredefinedConsoleInput : TStrings Read FPredefinedConsoleInput Write SetPredefinedConsoleInput; + // Name of function to run. If empty, the FPC default _start is used. Property RunEntryFunction : String Read FRunEntryFunction Write FRunEntryFunction; // Called after webassembly start was run. Not called if webassembly was not run. @@ -501,6 +561,17 @@ type property OnConsoleRead : TConsoleReadEvent Read FOnConsoleRead Write FOnConsoleRead; // Called when writing to console (stdout). If not set, console.log is used. property OnConsoleWrite : TConsoleWriteEvent Read FOnConsoleWrite Write FOnConsoleWrite; + // Called when fetch of the wasm module fails. + Property OnLoadFail : TFailEvent Read FOnLoadFail Write FOnLoadFail; + // Called when instantiation of the wasm module fails. + Property OnInstantiateFail : TFailEvent Read FOnInstantiateFail Write FOnInstantiateFail; + // Use Shared memory for webassembly instances ? + Property UseSharedMemory : Boolean Read GetUseSharedMemory Write SetUseSharedMemory; + // Executed after instantiation + Property AfterInstantation : TNotifyEvent Read FAfterInstantation Write FAfterInstantation; + // Executed before instantiation + Property BeforeInstantation : TNotifyEvent Read FBeforeInstantation Write FBeforeInstantation; + end; implementation @@ -534,30 +605,139 @@ begin FPredefinedConsoleInput.Assign(AValue); end; +function TWASIHost.GetUseSharedMemory: Boolean; +begin + Result:=FMemoryDescriptor.shared; + if isUndefined(Result) then + Result:=False; +end; + +function TWASIHost.GetStartDescriptorReady: Boolean; +begin + With FPreparedStartDescriptor do + Result:=Assigned(Memory) and Assigned(Module); +end; + +procedure TWASIHost.SetUseSharedMemory(AValue: Boolean); +begin + FMemoryDescriptor.shared:=aValue; +end; + +procedure TWASIHost.DoAfterInstantiate; +begin + If Assigned(FAfterInstantation) then + FAfterInstantation(Self); +end; + +procedure TWASIHost.DoBeforeInstantiate; +begin + If Assigned(FBeforeInstantation) then + FBeforeInstantation(Self); +end; + +procedure TWASIHost.DoLoadFail(aError: JSValue); +begin + If Assigned(FOnLoadFail) then + FOnLoadFail(Self,aError); +end; + +procedure TWASIHost.DoInstantiateFail(aError: JSValue); +begin + If Assigned(FOnInstantiateFail) then + FOnInstantiateFail(Self,aError); +end; + +procedure TWASIHost.PrepareWebAssemblyInstance( + aDescr: TWebAssemblyStartDescriptor); +begin + FPreparedStartDescriptor:=aDescr; + FExported:=aDescr.Exported; + WasiEnvironment.Instance:=aDescr.Instance; + WasiEnvironment.SetMemory(aDescr.Memory); + // We do this here, so in the event, the FPreparedStartDescriptor Is ready. + DoAfterInstantiate; +end; + +function TWASIHost.RunWebAssemblyInstance(aBeforeStart: TBeforeStartCallback; aAfterStart: TAfterStartCallback; aRun : TRunWebassemblyProc): Boolean; + +begin + Result:=True; + // Writeln('Entering RunWebAssemblyInstance'); + if Assigned(aBeforeStart) then + Result:=aBeforeStart(Self,FPreparedStartDescriptor); + if Assigned(FBeforeStart) then + FBeforeStart(Self,FPreparedStartDescriptor,Result); + if not Result then + exit; + try + if aRun=Nil then + aRun:=FPreparedStartDescriptor.CallRun; + aRun(FPreparedStartDescriptor.Exported); + if Assigned(aAfterStart) then + aAfterStart(Self,FPreparedStartDescriptor); + if Assigned(FAfterStart) then + FAfterStart(Self,FPreparedStartDescriptor) + except + On E : exception do + begin + FPreparedStartDescriptor.RunExceptionClass:=E.ClassName; + FPreparedStartDescriptor.RunExceptionMessage:=E.Message; + end; + On JE : TJSError do + begin + FPreparedStartDescriptor.RunExceptionClass:=jsTypeOf(JE); + FPreparedStartDescriptor.RunExceptionMessage:=JE.Message; + end; + On OE : TJSObject do + begin + FPreparedStartDescriptor.RunExceptionClass:=jsTypeOf(OE); + FPreparedStartDescriptor.RunExceptionMessage:=TJSJSON.Stringify(OE); + end; + end; +end; + procedure TWASIHost.DoStdWrite(Sender: TObject; const aOutput: String); begin - if assigned(FOnConsoleWrite) then - FOnConsoleWrite(Self,aOutput) - else - Console.log('Webassembly output: ', aOutput); + WriteOutput(aOutput); end; function TWASIHost.CreateWebAssembly(aPath: string; aImportObject: TJSObject ): TJSPromise; + Function InstantiateOK(Res : JSValue) : JSValue; + + begin + Result:=res; + end; + + Function InstantiateFail(Res : JSValue) : JSValue; + + begin + Result:=False; + DoInstantiateFail(res); + end; + + Function ArrayOK(res2 : jsValue) : JSValue; begin - Result:=TJSWebAssembly.instantiate(TJSArrayBuffer(res2),aImportObject); + DoBeforeInstantiate; + Result:=TJSWebAssembly.instantiate(TJSArrayBuffer(res2),aImportObject)._then(@InstantiateOK,@InstantiateFail); end; function fetchOK(res : jsValue) : JSValue; begin - Result:=TJSResponse(Res).arrayBuffer._then(@ArrayOK,Nil) + Result:=TJSResponse(Res).arrayBuffer._then(@ArrayOK,Nil); + end; + + function DoFail(res : jsValue) : JSValue; + begin + Result:=False; + DoLoadFail(res); end; begin - Result:=fetch(aPath)._then(@fetchOK); + Result:=fetch(aPath)._then(@fetchOK,@DoFail).Catch(@DoFail); end; function TWASIHost.CreateWasiEnvironment: TPas2JSWASIEnvironment; @@ -598,39 +778,48 @@ begin inherited Destroy; end; -procedure TWASIHost.StartWebAssembly(aPath: string; DoRun: Boolean; - aBeforeStart: TBeforeStartCallback; aAfterStart: TAfterStartCallback); +procedure TWASIHost.WriteOutput(const aOutput: String); +begin + if assigned(FOnConsoleWrite) then + FOnConsoleWrite(Self,aOutput) + else + Writeln(aOutput); +end; + + +function TWASIHost.RunWebAssemblyInstance(aDescr: TWebAssemblyStartDescriptor; + aBeforeStart: TBeforeStartCallback; + aAfterStart: TAfterStartCallback): Boolean; + +begin + Result:=RunWebAssemblyInstance(aBeforeStart,aAfterStart,Nil); +end; + +procedure TWASIHost.StartWebAssembly(aPath: string; DoRun: Boolean; aBeforeStart: TBeforeStartCallback; aAfterStart: TAfterStartCallback); + Var - ImportObj : TJSObject; - Res : TWebAssemblyStartDescriptor; + WASD : TWebAssemblyStartDescriptor; function InitEnv(aValue: JSValue): JSValue; Var - Module : TJSInstantiateResult absolute aValue; + InstResult : TJSInstantiateResult absolute aValue; begin Result:=True; - Res.Instance:=Module.Instance; - Res.Exported:=TWASIExports(TJSObject(Module.Instance.exports_)); - // These 2 prevent running different instances simultaneously. - FExported:=Res.Exported; - WasiEnvironment.Instance:=Module.Instance; - if Assigned(aBeforeStart) then - DoRun:=aBeforeStart(Self,Res) and DoRun; - if Assigned(FBeforeStart) then - FBeforeStart(Self,Res,DoRun); - if DoRun then + WASD.Instance:=InstResult.Instance; + WASD.Module:=InstResult.Module; + WASD.Exported:=TWASIExports(TJSObject(WASD.Instance.exports_)); + WASD.CallRun:=Procedure(aExports : TWASIExports) begin if FRunEntryFunction='' then - Res.Exported.Start + aExports.Start else - TProcedure(Res.Exported[RunEntryFunction])(); - if Assigned(aAfterStart) then - aAfterStart(Self,Res); - if Assigned(FAfterStart) then - FAfterStart(Self,Res) + TProcedure(aExports[RunEntryFunction])(); end; + PrepareWebAssemblyInstance(WASD); + if DoRun then + RunWebAssemblyInstance(aBeforeStart,aAfterStart,Nil); end; function DoFail(aValue: JSValue): JSValue; @@ -643,16 +832,32 @@ Var begin FReadLineCount:=0; - Res.Memory:=GetMemory; - Res.Table:=GetTable; - ImportObj:=new([ - 'js', new([ - 'mem', Res.Memory, - 'tbl', Res.Table - ]) + // Clear current descriptor. + FPreparedStartDescriptor:=Default(TWebAssemblyStartDescriptor); + WASD:=InitStartDescriptor(GetMemory,GetTable,Nil); + CreateWebAssembly(aPath,WASD.Imports)._then(@initEnv,@DoFail).catch(@DoFail); +end; + +procedure TWASIHost.RunPreparedDescriptor; +begin + RunWebAssemblyInstance(Nil,Nil,Nil) +end; + +function TWASIHost.InitStartDescriptor(aMemory: TJSWebAssemblyMemory; + aTable: TJSWebAssemblyTable; aImportObj: TJSObject + ): TWebAssemblyStartDescriptor; + +begin + Result.Memory:=aMemory; + Result.Table:=aTable; + if Not assigned(aImportObj) then + aImportObj:=TJSObject.New; + aImportObj['env']:=new([ + 'memory', Result.Memory, + 'tbl', Result.Table ]); - FEnv.AddImports(ImportObj); - CreateWebAssembly(aPath,ImportObj)._then(@initEnv,@DoFail) + FEnv.AddImports(aImportObj); + Result.Imports:=aImportObj; end; function TImportExtension.getModuleMemoryDataView : TJSDataView; @@ -711,7 +916,7 @@ end; function TPas2JSWASIEnvironment.getModuleMemoryDataView: TJSDataView; begin - Result:=TJSDataView.New(FModuleInstanceExports.memory.buffer); + Result:=TJSDataView.New(Memory.buffer); end; function TPas2JSWASIEnvironment.fd_prestat_get(fd, bufPtr: NativeInt @@ -813,6 +1018,8 @@ begin if Finstance=AValue then Exit; Finstance:=AValue; FModuleInstanceExports:=Finstance.exports_; + if Not Assigned(FMemory) and Assigned(FModuleInstanceExports.Memory) then + FMemory:=FModuleInstanceExports.Memory; end; function TPas2JSWASIEnvironment.GetTime(aClockID: NativeInt): NativeInt; @@ -873,11 +1080,19 @@ begin ptr:=iovs + i * 8; buf:=view.getUint32(ptr, IsLittleEndian); bufLen:=view.getUint32(ptr + 4, IsLittleEndian); - ArrayBuf:=TJSUint8Array.New(FModuleInstanceExports.memory.buffer, buf, bufLen); + ArrayBuf:=TJSUint8Array.New(Memory.buffer, buf, bufLen); Result.Push(ArrayBuf); end; end; +function TPas2JSWASIEnvironment.GetMemory: TJSWebassemblyMemory; +begin + if Assigned(FMemory) then + Result:=FMemory + else + Result:= FModuleInstanceExports.Memory; +end; + function TPas2JSWASIEnvironment.fd_write(fd, iovs, iovsLen, nwritten: NativeInt): NativeInt; var @@ -1089,6 +1304,11 @@ begin Result:=WASI_ENOSYS; end; +procedure TPas2JSWASIEnvironment.SetMemory(aMemory: TJSWebAssemblyMemory); +begin + FMemory:=aMemory; +end; + class constructor TPas2JSWASIEnvironment.init; Var Opts : TJSTextDecoderOptions; diff --git a/packages/wasi/wasihostapp.pas b/packages/wasi/wasihostapp.pas index ca16ff8..da027a8 100644 --- a/packages/wasi/wasihostapp.pas +++ b/packages/wasi/wasihostapp.pas @@ -14,24 +14,27 @@ Type TBrowserWASIHostApplication = class(TBrowserApplication) private FHost : TWASIHost; - FOnConsoleRead: TConsoleReadEvent; - FOnConsoleWrite: TConsoleWriteEvent; FPredefinedConsoleInput: TStrings; function GetAfterStart: TAfterStartEvent; function GetBeforeStart: TBeforeStartEvent; function GetEnv: TPas2JSWASIEnvironment; function GetExported: TWASIExports; function GetMemoryDescriptor: TJSWebAssemblyMemoryDescriptor; + function GetOnConsoleRead: TConsoleReadEvent; + function GetOnConsoleWrite: TConsoleWriteEvent; function GetRunEntryFunction: String; function GetTableDescriptor: TJSWebAssemblyTableDescriptor; procedure SetAfterStart(AValue: TAfterStartEvent); procedure SetBeforeStart(AValue: TBeforeStartEvent); procedure SetMemoryDescriptor(AValue: TJSWebAssemblyMemoryDescriptor); + procedure SetOnConsoleRead(AValue: TConsoleReadEvent); + procedure SetOnConsoleWrite(AValue: TConsoleWriteEvent); procedure SetPredefinedConsoleInput(AValue: TStrings); procedure SetRunEntryFunction(AValue: String); procedure SetTableDescriptor(AValue: TJSWebAssemblyTableDescriptor); protected function CreateHost: TWASIHost; virtual; + Property Host : TWASIHost Read FHost; public Constructor Create(aOwner : TComponent); override; Destructor Destroy; override; @@ -56,9 +59,9 @@ Type // Default console input Property PredefinedConsoleInput : TStrings Read FPredefinedConsoleInput Write SetPredefinedConsoleInput; // Called when reading from console (stdin). If not set, PredefinedConsoleinput is used. - property OnConsoleRead : TConsoleReadEvent Read FOnConsoleRead Write FOnConsoleRead; + property OnConsoleRead : TConsoleReadEvent Read GetOnConsoleRead Write SetOnConsoleRead; // Called when writing to console (stdout). If not set, console.log is used. - property OnConsoleWrite : TConsoleWriteEvent Read FOnConsoleWrite Write FOnConsoleWrite; + property OnConsoleWrite : TConsoleWriteEvent Read GetOnConsoleWrite Write SetOnConsoleWrite; end; // For backwards compatibility @@ -94,6 +97,16 @@ begin Result:=FHost.MemoryDescriptor; end; +function TBrowserWASIHostApplication.GetOnConsoleRead: TConsoleReadEvent; +begin + Result:=FHost.OnConsoleRead; +end; + +function TBrowserWASIHostApplication.GetOnConsoleWrite: TConsoleWriteEvent; +begin + Result:=FHost.OnConsoleWrite; +end; + function TBrowserWASIHostApplication.GetRunEntryFunction: String; begin Result:=FHost.RunEntryFunction; @@ -120,6 +133,18 @@ begin FHost.MemoryDescriptor:=aValue; end; +procedure TBrowserWASIHostApplication.SetOnConsoleRead(AValue: TConsoleReadEvent + ); +begin + FHost.OnConsoleRead:=aValue +end; + +procedure TBrowserWASIHostApplication.SetOnConsoleWrite( + AValue: TConsoleWriteEvent); +begin + FHost.OnConsoleWrite:=aValue; +end; + procedure TBrowserWASIHostApplication.SetPredefinedConsoleInput(AValue: TStrings); begin FHost.PredefinedConsoleInput:=aValue; @@ -139,7 +164,7 @@ end; function TBrowserWASIHostApplication.CreateHost : TWASIHost; begin - Result:=TWASIHost.Create(Nil); + Result:=TWASIHost.Create(Self); end; constructor TBrowserWASIHostApplication.Create(aOwner: TComponent); diff --git a/packages/wasi/wasithreadedapp.pas b/packages/wasi/wasithreadedapp.pas new file mode 100644 index 0000000..2f13521 --- /dev/null +++ b/packages/wasi/wasithreadedapp.pas @@ -0,0 +1,613 @@ +unit wasithreadedapp; + +{$mode ObjFPC} +{$modeswitch externalclass} +{$modeswitch typehelpers} + +interface + +uses + JS, Classes, SysUtils, Rtl.WebThreads, wasienv, wasihostapp, weborworker; + +Type + { TWasmThread } + TWasmThread = TJSWorker; + + { TWasmThreadHelper } + + TWasmThreadHelper = Class helper for TWasmThread + private + function GetLoaded: Boolean; + function GetLoadSent: Boolean; + function GetThreadID: Integer; + function GetThreadIDRange: Integer; + function GetThreadInfo: TThreadinfo; + function GetThreadLocation: Integer; + procedure SetLoaded(AValue: Boolean); + procedure SetLoadSent(AValue: Boolean); + procedure SetThreadID(AValue: Integer); + procedure SetThreadIDRange(AValue: Integer); + procedure SetThreadInfo(AValue: TThreadinfo); + procedure SetThreadLocation(AValue: Integer); + Public + Class function Create(aScript : String) : TWasmThread; reintroduce; static; + Procedure SendCommand(aCommand : TWorkerCommand); + Property LoadSent : Boolean Read GetLoadSent Write SetLoadSent; + Property Loaded : Boolean Read GetLoaded Write SetLoaded; + Property ThreadInfo : TThreadinfo Read GetThreadInfo Write SetThreadInfo; + Property ThreadID : Integer Read GetThreadID Write SetThreadID; + Property ThreadIDRange : Integer Read GetThreadIDRange Write SetThreadIDRange; + Property ThreadLocation : Integer Read GetThreadLocation Write SetThreadLocation; + end; + + + + TThreadHash = class external name 'Object' (TJSObject) + Private + function GetThreadData(aIndex: NativeInt): TWasmThread; external name '[]'; + procedure SetThreadData(aIndex: NativeInt; const AValue: TWasmThread); external name '[]'; + Public + Property ThreadData[aIndex : NativeInt] : TWasmThread Read GetThreadData Write SetThreadData; default; + end; + + + // This object has the thread support that is needed by the 'main' program + + { TMainThreadSupport } + + TMainThreadSupport = class(TWasmThreadSupport) + private + FInitialWorkerCount: Integer; + FMaxWorkerCount: Integer; + FOnUnknownMessage: TJSRawEventHandler; + FHost: TWASIHost; + FWorkerScript: String; + FNextIDRange : Integer; + FNextThreadID : Integer; + procedure SetWasiHost(AValue: TWASIHost); + Protected + Function thread_spawn(thread_id : Integer; attrs: Integer; thread_start_func : Integer; args : Integer) : Integer; override; + Function thread_detach(thread_id : Integer) : Integer; override; + Function thread_cancel(thread_id : Integer) : Integer; override; + Function thread_self() : Integer; override; + function AllocateThreadID : Integer; + Protected + FIdleWorkers : Array of TWasmThread; + FBusyWorkers : Array of TWasmThread; + FThreads : TThreadHash; // ThreadID is key, + // Send load commands to all workers that still need it. + procedure SendLoadCommands; + // Allocate new thread ID range + function GetNextThreadIDRange: Integer; + // Handle worker messages. If it is a command, it is set to handlecommand. + procedure DoWorkerMessage(aEvent: TJSEvent); + // Create & set up new worker + Function AllocateNewWorker(Const aWorkerScript : string) : TWasmThread; + // Send a load command + procedure SendLoadCommand(aThreadWorker: TWasmThread); virtual; + // Get new worker from pool, create new if needed. + Function GetNewWorker : TWasmThread; + // Spawn & prepare to run a new thread. + Function SpawnThread(aInfo : TThreadInfo) : Integer; + // Actually send run command. + Procedure SendRunCommand(aThreadWorker: TWasmThread); + // + // Handle Various commands sent from worker threads. + // + // Allocate a new worker for a thread and run the thread if the worker is loaded. + procedure HandleSpawnCommand(aWorker: TWasmThread; aCommand: TWorkerSpawnThreadCommand); virtual; + // Cancel command: stop the thread + procedure HandleCancelCommand(aWorker: TWasmThread; aCommand: TWorkerCancelCommand); virtual; + // Cleanup thread : after join (or stopped if detached), free worker. + procedure HandleCleanupCommand(aWorker: TWasmThread; aCommand: TWorkerCleanupCommand); virtual; + // forward KILL signal to thread. + procedure HandleKillCommand(aWorker: TWasmThread; aCommand: TWorkerKillCommand); virtual; + // Worker script is loaded, has loaded webassembly and is ready to run. + procedure HandleLoadedCommand(aWorker: TWasmThread; aCommand: TWorkerLoadedCommand); overload; + // Console output from worker. + procedure HandleConsoleCommand(aWorker: TWasmThread; aCommand: TWorkerConsoleCommand); + Public + Constructor Create(aEnv : TPas2JSWASIEnvironment); override; + Constructor Create(aEnv : TPas2JSWASIEnvironment; aWorkerScript : String; aSpawnWorkerCount : integer); virtual; overload; + Procedure HandleCommand(aWorker : TWasmThread; aCommand : TWorkerCommand); overload; virtual; + Property WorkerScript : String Read FWorkerScript; + // Initial number of threads, set by constructor + Property InitialWorkerCount : Integer Read FInitialWorkerCount; + // Maximum number of workers. If more workers are requested, the GetNewWorker will return Nil. + Property MaxWorkerCount : Integer Read FMaxWorkerCount Write FMaxWorkerCount; + Property OnUnknownMessage : TJSRawEventHandler Read FOnUnknownMessage Write FOnUnknownMessage; + // The WASI host, used to run routines. + Property Host : TWASIHost Read FHost Write SetWasiHost; + end; + + { TBrowserWASIThreadedHostApplication } + + TBrowserWASIThreadedHostApplication = class(TBrowserWASIHostApplication) + private + FThreadSupport: TMainThreadSupport; + protected + Function CreateThreadSupport(aEnv : TPas2JSWASIEnvironment) : TMainThreadSupport; virtual; + Function CreateHost: TWASIHost; override; + Public + Destructor Destroy; override; + Property ThreadSupport : TMainThreadSupport Read FThreadSupport; + end; + + { ThreadAppWASIHost } + + ThreadAppWASIHost = class(TWASIHost) + private + FThreadInitInstanceEntry: String; + FThreadSupport: TMainThreadSupport; + + procedure SetThreadSupport(AValue: TMainThreadSupport); + Protected + Procedure PrepareWebAssemblyInstance(aDescr: TWebAssemblyStartDescriptor); override; + Procedure DoAfterInstantiate; override; + Public + constructor Create(aOwner: TComponent); override; + + Property ThreadSupport : TMainThreadSupport Read FThreadSupport Write SetThreadSupport; + // Thread instance Init point name for the WASI Host. + Property ThreadInitInstanceEntry : String Read FThreadInitInstanceEntry Write FThreadInitInstanceEntry; + end; + + +implementation + +Resourcestring + SErrMaxWorkersReached = 'Cannot create thread worker, Maximum number of workers (%d) reached.'; + +{ ThreadAppWASIHost } + +procedure ThreadAppWASIHost.SetThreadSupport(AValue: TMainThreadSupport); +begin + if FThreadSupport=AValue then Exit; + FThreadSupport:=AValue; + FThreadSupport.Host:=Self; +end; + +procedure ThreadAppWASIHost.PrepareWebAssemblyInstance( + aDescr: TWebAssemblyStartDescriptor); +Var + func : JSValue; + InitFunc : TThreadInitInstanceFunction absolute func; + Res : Integer; + +begin + inherited; + Writeln('PrepareWebAssemblyInstance: check init thread'); + func:=aDescr.Exported[ThreadInitInstanceEntry]; + if Assigned(func) then + begin + Writeln('Initializing main thread instance'); + res:=InitFunc(0,1,0); + if Res<>0 then + Writeln('Failed to initialize thread'); + end; +end; + +procedure ThreadAppWASIHost.DoAfterInstantiate; +begin + inherited DoAfterInstantiate; + If Assigned(FThreadSupport) then + FThreadSupport.SendLoadCommands; +end; + +constructor ThreadAppWASIHost.Create(aOwner: TComponent); +begin + inherited Create(aOwner); + ThreadInitInstanceEntry:=DefaultThreadInstanceInitPoint; +end; + + +{ TBrowserWASIThreadedHostApplication } + +function TBrowserWASIThreadedHostApplication.CreateThreadSupport( + aEnv: TPas2JSWASIEnvironment): TMainThreadSupport; +begin + Result:=TMainThreadSupport.Create(aEnv); +end; + +function TBrowserWASIThreadedHostApplication.CreateHost: TWASIHost; + +Var + Res : ThreadAppWASIHost; + +begin + Res:=ThreadAppWASIHost.Create(Self); + Res.UseSharedMemory:=True; + Res.ThreadSupport:=CreateThreadSupport(Res.WasiEnvironment); + Result:=Res; +end; + + +destructor TBrowserWASIThreadedHostApplication.Destroy; +begin + FreeAndNil(FThreadSupport); + inherited Destroy; +end; + + +{ TWasmThread } + + +class function TWasmThreadHelper.Create(aScript: String): TWasmThread; +begin + Result:=TJSWorker.new(aScript); + Result.ThreadID:=-1; + Result.Loaded:=False; + Result.LoadSent:=False; + Result.ThreadIDRange:=-1; + Result.ThreadInfo:=Default(TThreadInfo); +end; + +function TWasmThreadHelper.GetLoaded: Boolean; +Var + S : JSValue; +begin + S:=Properties['FLoaded']; + if isBoolean(S) then + Result:=Boolean(S) + else + Result:=False; +end; + +function TWasmThreadHelper.GetLoadSent: Boolean; + +Var + S : JSValue; +begin + S:=Properties['FLoadSent']; + if isBoolean(S) then + Result:=Boolean(S) + else + Result:=False; +end; + +function TWasmThreadHelper.GetThreadID: Integer; +begin + Result:=ThreadInfo.ThreadID; +end; + +function TWasmThreadHelper.GetThreadIDRange: Integer; +Var + S : JSValue; +begin + S:=Properties['FThreadIDRange']; + if isNumber(S) then + Result:=Integer(S) + else + Result:=0; +end; + +function TWasmThreadHelper.GetThreadInfo: TThreadinfo; +Var + S : JSValue; +begin + S:=Properties['FThreadInfo']; + if isObject(S) then + Result:=TThreadinfo(S) + else + Result:=Default(TThreadInfo); +end; + +function TWasmThreadHelper.GetThreadLocation: Integer; +begin + Result:=ThreadInfo.ThreadInfoLocation; +end; + +procedure TWasmThreadHelper.SetLoaded(AValue: Boolean); +begin + Properties['FLoaded']:=aValue +end; + +procedure TWasmThreadHelper.SetLoadSent(AValue: Boolean); +begin + Properties['FLoadSent']:=aValue; +end; + + + +procedure TWasmThreadHelper.SetThreadID(AValue: Integer); +begin + ThreadInfo.ThreadID:=aValue; +end; + +procedure TWasmThreadHelper.SetThreadIDRange(AValue: Integer); +begin + Properties['FThreadIDRange']:=aValue +end; + +procedure TWasmThreadHelper.SetThreadInfo(AValue: TThreadinfo); +begin + Properties['FThreadInfo']:=aValue +end; + +procedure TWasmThreadHelper.SetThreadLocation(AValue: Integer); +begin + ThreadInfo.ThreadInfoLocation:=aValue +end; + + +procedure TWasmThreadHelper.SendCommand(aCommand: TWorkerCommand); +begin + // Writeln('Sending command '+TJSJSON.Stringify(aCommand)); + PostMessage(aCommand); +end; + +procedure TMainThreadSupport.DoWorkerMessage(aEvent: TJSEvent); + +Var + aMessageEvent : TJSMessageEvent absolute aEvent; + aData : TWorkerCommand; + aWorker : TWasmThread; + +begin + // Writeln('Received worker message '+TJSJSON.Stringify(aMessageEvent.Data)); + if IsObject(aMessageEvent.Data) and TJSObject(aMessageEvent.Data).hasOwnProperty('Command') then + begin + aData:=TWorkerCommand(aMessageEvent.Data); + aWorker:=TWasmThread(aMessageEvent.Target); + HandleCommand(aWorker,aData); + end + else if Assigned(FOnUnknownMessage) then + FOnUnknownMessage(aEvent) + else + Writeln('Unknown worker message : ',TJSJSON.stringify(aEvent)); +end; + +function TMainThreadSupport.GetNextThreadIDRange : Integer; + +begin + Inc(FNextIDRange,ThreadIDInterval); + Result:=FNextIDRange; +end; + +function TMainThreadSupport.AllocateNewWorker(const aWorkerScript: string): TWasmThread; + +begin + // Writeln('Allocating new worker for: '+aWorkerScript); + Result:=TWasmThread.Create(aWorkerScript); + Result.ThreadIDRange:=GetNextThreadIDRange; + Result.addEventListener('message',@DoWorkerMessage); + if Assigned(Host) and Host.StartDescriptorReady then + SendLoadCommand(Result) + else + Writeln('Host not set, delaying sending load command.'+aWorkerScript); +end; + +procedure TMainThreadSupport.SendLoadCommand(aThreadWorker: TWasmThread); + +Var + WLC: TWorkerLoadCommand; + +begin + WLC:=TWorkerLoadCommand.Create(aThreadWorker.ThreadIDRange, Host.PreparedStartDescriptor.Module, Host.PreparedStartDescriptor.Memory); + aThreadWorker.SendCommand(WLC); + aThreadWorker.LoadSent:=True; +end; + +function TMainThreadSupport.GetNewWorker: TWasmThread; + +Var + WT : TWasmThread; + +begin + if Length(FIdleWorkers)=0 then + begin + // Writeln('No idle workers, creating new one'); + if Length(FBusyWorkers)<MaxWorkerCount then + WT:=AllocateNewWorker(FWorkerScript) + else + Raise EWasmThreads.Create(SErrMaxWorkersReached); + end + else + begin + WT:=TWasmThread(TJSArray(FIdleWorkers).pop); + end; + TJSArray(FBusyWorkers).Push(WT); + Result:=WT; +end; + + +procedure TMainThreadSupport.SendRunCommand(aThreadWorker: TWasmThread); + +Var + WRC : TWorkerRunCommand; + +begin + With aThreadWorker.ThreadInfo do + WRC:=TWorkerRunCommand.Create(ThreadID,RunFunction,Attributes,Arguments,ThreadInfoLocation); + aThreadWorker.SendCommand(Wrc); +end; + +procedure TMainThreadSupport.SetWasiHost(AValue: TWASIHost); + + +begin + // Writeln('Setting wasi host'); + if FHost=AValue then + Exit; + FHost:=AValue; + If Assigned(FHost) and Host.StartDescriptorReady then + SendLoadCommands; +end; + +function TMainThreadSupport.thread_spawn(thread_id: Integer; attrs: Integer; + thread_start_func: Integer; args: Integer): Integer; + +var + aInfo : TThreadInfo; + +begin + // Writeln('In host thread_spawn'); + aInfo.ThreadID:=AllocateThreadID; + aInfo.RunFunction:=thread_start_func; + aInfo.Arguments:=Args; + aInfo.Attributes:=Attrs; + aInfo.OriginThreadID:=0; + aInfo.ThreadInfoLocation:=thread_id; + Env.SetMemInfoInt32(thread_id,aInfo.ThreadID); + Result:=SpawnThread(aInfo); +end; + +function TMainThreadSupport.thread_detach(thread_id: Integer): Integer; +begin + Result:=0; +end; + +function TMainThreadSupport.thread_cancel(thread_id: Integer): Integer; +begin + Result:=0; +end; + +function TMainThreadSupport.thread_self: Integer; +begin + Result:=0; +end; + +function TMainThreadSupport.AllocateThreadID: Integer; +begin + Inc(FNextThreadID); + Result:=FNextThreadID; +end; + +procedure TMainThreadSupport.SendLoadCommands; + +Var + WT : TWasmThread; + +begin + // Writeln('Sending load command to all workers'); + For WT in FIdleWorkers do + if not WT.LoadSent then + SendLoadCommand(WT); +end; + +function TMainThreadSupport.SpawnThread(aInfo: TThreadInfo): Integer; + +Var + WT : TWasmThread; + +begin + // Writeln('Enter TMainThreadSupport.SpawnThread for ID ',aInfo.ThreadID); + WT:=GetNewWorker; + if WT=nil then + begin + Writeln('Error: no worker !'); + exit(-1) + end; + WT.ThreadInfo:=aInfo; + FThreads[aInfo.ThreadID]:=WT; + if WT.Loaded then + begin + // Writeln('Worker is loaded. Sending run command to worker'); + SendRunCommand(WT); + end; + // Writeln('Exit: TMainThreadSupport.SpawnThread for ID ',WT.ThreadID); +end; + + +constructor TMainThreadSupport.Create(aEnv: TPas2JSWASIEnvironment); +begin + Create(aEnv,DefaultThreadWorker,DefaultThreadCount) +end; + +constructor TMainThreadSupport.Create(aEnv: TPas2JSWASIEnvironment; + aWorkerScript: String; aSpawnWorkerCount: integer); + +Var + I : Integer; + +begin + Inherited Create(aEnv); + FThreads:=TThreadHash.new; + FWorkerScript:=aWorkerScript; + FInitialWorkerCount:=aSpawnWorkerCount; + FMaxWorkerCount:=DefaultMaxWorkerCount; + For I:=1 to aSpawnWorkerCount do + TJSArray(FIdleWorkers).Push(AllocateNewWorker(aWorkerScript)); +end; + +procedure TMainThreadSupport.HandleSpawnCommand(aWorker : TWasmThread; aCommand: TWorkerSpawnThreadCommand); + +Var + aInfo: TThreadInfo; + +begin + aInfo.OriginThreadID:=aWorker.ThreadID; + aInfo.RunFunction:=aCommand.RunFunction; + aInfo.ThreadID:=aCommand.ThreadID; + aInfo.Arguments:=aCommand.Arguments; + aInfo.Attributes:=aCommand.Attributes; + SpawnThread(aInfo); +end; + +procedure TMainThreadSupport.HandleKillCommand(aWorker : TWasmThread; aCommand: TWorkerKillCommand); + +begin + +end; + +procedure TMainThreadSupport.HandleCancelCommand(aWorker : TWasmThread; aCommand: TWorkerCancelCommand); + +begin + +end; + +procedure TMainThreadSupport.HandleLoadedCommand(aWorker : TWasmThread; aCommand: TWorkerLoadedCommand); + +begin + // Writeln('Host: Entering TMainThreadSupport.HandleLoadedCommand'); + aWorker.Loaded:=True; + // if a thread is scheduled to run in this thread, run it. + if aWorker.ThreadID>0 then + SendRunCommand(aWorker); + // Writeln('Host: exiting TMainThreadSupport.HandleLoadedCommand'); +end; + +procedure TMainThreadSupport.HandleCleanupCommand(aWorker : TWasmThread; aCommand: TWorkerCleanupCommand); + +Var + Idx : Integer; + +begin + aWorker.ThreadInfo:=Default(TThreadInfo); + Idx:=TJSarray(FBusyWorkers).indexOf(aWorker); + if Idx<>-1 then + Delete(FBusyWorkers,Idx,1); + Idx:=TJSarray(FIdleWorkers).indexOf(aWorker); + if Idx=-1 then + FIdleWorkers:=Concat(FIdleWorkers,[aWorker]); +end; + +procedure TMainThreadSupport.HandleConsoleCommand(aWorker : TWasmThread; aCommand: TWorkerConsoleCommand); + +Var + Prefix : string; + +begin + Prefix:=Format('Wasm thread %d: ',[aWorker.ThreadID]); + if Assigned(Host.OnConsoleWrite) then + Host.OnConsoleWrite(Host,Prefix+aCommand.ConsoleMessage) + else + Writeln(Prefix+aCommand.ConsoleMessage); +end; + +procedure TMainThreadSupport.HandleCommand(aWorker : TWasmThread; aCommand: TWorkerCommand); +begin + Case aCommand.Command of + cmdSpawn : HandleSpawnCommand(aWorker, TWorkerSpawnThreadCommand(aCommand)); + cmdCleanup : HandleCleanupCommand(aWorker, TWorkerCleanupCommand(aCommand)); + cmdKill : HandleKillCommand(aWorker, TWorkerKillCommand(aCommand)); + cmdCancel : HandleCancelCommand(aWorker, TWorkerCancelCommand(aCommand)); + cmdLoaded : HandleLoadedCommand(aWorker, TWorkerLoadedCommand(aCommand)); + cmdConsole : HandleConsoleCommand(aWorker, TWorkerConsoleCommand(aCommand)); + else + HandleCommand(aCommand); + end; +end; + +end. + diff --git a/packages/wasi/wasiworkerthreadhost.pas b/packages/wasi/wasiworkerthreadhost.pas new file mode 100644 index 0000000..30029b3 --- /dev/null +++ b/packages/wasi/wasiworkerthreadhost.pas @@ -0,0 +1,663 @@ +unit wasiworkerthreadhost; + +{$mode ObjFPC} +{$modeswitch externalclass} + +interface + +uses + Classes, SysUtils, JS, custapp, weborworker, webworker, webassembly, wasienv, Rtl.WebThreads; + +Type + TWorkerThreadSupport = Class; + + { TWASIThreadHost } + + TWASIThreadHost = class(TWASIHost) + private + FSendOutputToBrowserWindow: Boolean; + FThreadEntryPoint: String; + FThreadInitInstanceEntry : String; + FThreadSupport: TWorkerThreadSupport; + procedure SetThreadSupport(AValue: TWorkerThreadSupport); + Protected + Procedure RunWebAssemblyThread(aProc : TRunWebassemblyProc); virtual; + Procedure PrepareWebAssemblyThread(aDescr : TWebAssemblyStartDescriptor); virtual; + procedure DoStdWrite(Sender: TObject; const aOutput: String); override; + Public + constructor Create(aOwner: TComponent); override; + // Thread entry point name for the WASI Host. + Property ThreadEntryPoint : String Read FThreadEntryPoint Write FThreadEntryPoint; + // Thread instance Init point name for the WASI Host. + Property ThreadInitInstanceEntry : String Read FThreadInitInstanceEntry Write FThreadInitInstanceEntry; + // Send output to main window + Property SendOutputToBrowserWindow : Boolean Read FSendOutputToBrowserWindow Write FSendOutputToBrowserWindow; + // our thread + Property ThreadSupport : TWorkerThreadSupport Read FThreadSupport Write SetThreadSupport; + end; + + + // This object has the thread support that is needed by the worker that runs a thread. + + { TWorkerThreadSupport } + + TWorkerThreadSupport = class(TWasmThreadSupport) + Private + FStartThreadID : Integer; + FNextThreadID : Integer; + FCurrentThreadInfo : TThreadinfo; + FModule : TJSWebAssemblyModule; + FMemory : TJSWebAssemblyMemory; + FWasiHost: TWASIThreadHost; + Protected + // Set new thread range + procedure InitThreadRange(aRange: Integer); + // allocate new thread ID. + Function AllocateNewThreadID : NativeInt; + // Incoming messages + procedure LoadWasmModule(aCommand: TWorkerLoadCommand); virtual; + procedure RunWasmModule(aCommand: TWorkerRunCommand); virtual; + procedure CancelWasmModule(aCommand: TWorkerCancelCommand); virtual; + procedure SetThreadRange(aCommand: TWorkerThreadIDRangeCommand); virtual; + // outgoing messages + procedure RequestNewThreadBlock; virtual; + procedure SendLoaded; virtual; + Procedure SendConsoleMessage(aMessage : String); overload; + Procedure SendConsoleMessage(aFmt : String; const aArgs : array of const); overload; + Procedure SendConsoleMessage(const aArgs : array of JSValue); overload; + procedure SendException(aError: Exception); overload; + procedure SendException(aError: TJSError); overload; + Protected + Function thread_spawn(thread_id : Integer; attrs: Integer; thread_start_func : Integer; args : Integer) : Integer; override; + Function thread_detach(thread_id : Integer) : Integer; override; + Function thread_cancel(thread_id : Integer) : Integer; override; + Function thread_self() : Integer; override; + Public + // Handle incoming command + Procedure HandleCommand(aCommand : TWorkerCommand); override; + // Current thread info. + Property CurrentThreadInfo : TThreadInfo Read FCurrentThreadInfo; + // The WASI host, used to run routines. + Property Host : TWASIThreadHost Read FWasiHost Write FWasiHost; + end; + + + { TWorkerWASIHostApplication } + + TWorkerWASIHostApplication = class(TCustomApplication) + private + FHost : TWASIHost; + FThreadSupport : TWorkerThreadSupport; + FSendOutputToBrowser: Boolean; + function GetAfterStart: TAfterStartEvent; + function GetBeforeStart: TBeforeStartEvent; + function GetcPredefinedConsoleInput: TStrings; + function GetEnv: TPas2JSWASIEnvironment; + function GetExported: TWASIExports; + function GetOnConsoleRead: TConsoleReadEvent; + function GetOnConsoleWrite: TConsoleWriteEvent; + function GetRunEntryFunction: String; + procedure SetAfterStart(AValue: TAfterStartEvent); + procedure SetBeforeStart(AValue: TBeforeStartEvent); + procedure SetOnConsoleRead(AValue: TConsoleReadEvent); + procedure SetOnConsoleWrite(AValue: TConsoleWriteEvent); + procedure SetPredefinedConsoleInput(AValue: TStrings); + procedure SetRunEntryFunction(AValue: String); + protected + procedure HandleMessage(aEvent: TJSEvent); virtual; + procedure DoOnSendCommand(Sender : TObject; aCommand : TWorkerCommand); + function CreateHost: TWASIHost; virtual; + procedure DoRun; override; + function GetConsoleApplication: boolean; override; + function GetLocation: String; override; + public + Constructor Create(aOwner : TComponent); override; + Destructor Destroy; override; + procedure SendCommand(aCommand: TWorkerCommand); virtual; + procedure GetEnvironmentList(List: TStrings; NamesOnly: Boolean); override; + procedure ShowException(E: Exception); override; + // Load and start webassembly. If DoRun is true, then Webassembly entry point is called. + // If aBeforeStart is specified, then it is called prior to calling run, and can disable running. + // If aAfterStart is specified, then it is called after calling run. It is not called is running was disabled. + Procedure StartWebAssembly(aPath: string; DoRun : Boolean = True; aBeforeStart : TBeforeStartCallback = Nil; aAfterStart : TAfterStartCallback = Nil); + // Environment to be used + Property WasiEnvironment : TPas2JSWASIEnvironment Read GetEnv; + // Exported functions. Also available in start descriptor. + Property Exported : TWASIExports Read GetExported; + // Name of function to run, if empty default _start symbol is used. + Property RunEntryFunction : String Read GetRunEntryFunction Write SetRunEntryFunction; + // Called after webassembly start was run. Not called if webassembly was not run. + Property AfterStart : TAfterStartEvent Read GetAfterStart Write SetAfterStart; + // Called before running webassembly. If aAllowRun is false, running is disabled + Property BeforeStart : TBeforeStartEvent Read GetBeforeStart Write SetBeforeStart; + // Send output to browser window process? + Property SendOutputToBrowser : Boolean Read FSendOutputToBrowser Write FSendOutputToBrowser; + // Default console input + Property PredefinedConsoleInput : TStrings Read GetcPredefinedConsoleInput Write SetPredefinedConsoleInput; + // Called when reading from console (stdin). If not set, PredefinedConsoleinput is used. + property OnConsoleRead : TConsoleReadEvent Read GetOnConsoleRead Write SetOnConsoleRead; + // Called when writing to console (stdout). If not set, console.log is used. + property OnConsoleWrite : TConsoleWriteEvent Read GetOnConsoleWrite Write SetOnConsoleWrite; + // Our thread support object + Property ThreadSupport : TWorkerThreadSupport Read FThreadSupport Write FThreadSupport; + end; + +implementation + +uses Types; + +var + Self_ : TJSDedicatedWorkerGlobalScope; external name 'self'; + EnvNames: TJSObject; + +procedure ReloadEnvironmentStrings; + +var + I : Integer; + S,N : String; + A,P : TStringDynArray; + +begin + if Assigned(EnvNames) then + FreeAndNil(EnvNames); + EnvNames:=TJSObject.new; + S:=self_.Location.search; + S:=Copy(S,2,Length(S)-1); + A:=TJSString(S).split('&'); + for I:=0 to Length(A)-1 do + begin + P:=TJSString(A[i]).split('='); + N:=LowerCase(decodeURIComponent(P[0])); + if Length(P)=2 then + EnvNames[N]:=decodeURIComponent(P[1]) + else if Length(P)=1 then + EnvNames[N]:='' + end; +end; + +function MyGetEnvironmentVariable(Const EnvVar: String): String; + +Var + aName : String; + +begin + aName:=Lowercase(EnvVar); + if EnvNames.hasOwnProperty(aName) then + Result:=String(EnvNames[aName]) + else + Result:=''; +end; + +function MyGetEnvironmentVariableCount: Integer; +begin + Result:=length(TJSOBject.getOwnPropertyNames(envNames)); +end; + +function MyGetEnvironmentString(Index: Integer): String; +begin + Result:=String(EnvNames[TJSOBject.getOwnPropertyNames(envNames)[Index]]); +end; + + +{ TWASIThreadHost } + +procedure TWASIThreadHost.SetThreadSupport(AValue: TWorkerThreadSupport); +begin + if FThreadSupport=AValue then Exit; + if Assigned(FThreadSupport) then + FThreadSupport.Host:=Nil; + FThreadSupport:=AValue; + if Assigned(FThreadSupport) then + FThreadSupport.Host:=Self; +end; + +procedure TWASIThreadHost.RunWebAssemblyThread(aProc : TRunWebassemblyProc); +begin +// Writeln('TWASIThreadHost.Entering RunWebAssemblyThread '); + RunWebAssemblyInstance(Nil,Nil,aProc); +end; + +procedure TWASIThreadHost.PrepareWebAssemblyThread( aDescr: TWebAssemblyStartDescriptor); + +Var + func : JSValue; + InitFunc : TThreadInitInstanceFunction absolute func; + res : Integer; + +begin + PrepareWebAssemblyInstance(aDescr); + func:=aDescr.Exported[ThreadInitInstanceEntry]; + if Assigned(func) then + begin + res:=InitFunc(1,0,1); + if Res<>0 then + if Assigned(ThreadSupport) then + ThreadSupport.SendConsoleMessage('Could not init assembly thread: %d', [Res]) + else + Writeln('Could not init assembly thread: ',Res); + end; +end; + +procedure TWASIThreadHost.DoStdWrite(Sender: TObject; const aOutput: String); +begin + inherited DoStdWrite(Sender, aOutput); + if FSendOutputToBrowserWindow and assigned(FThreadSupport) then + FThreadSupport.SendConsoleMessage(aOutput); +end; + +constructor TWASIThreadHost.Create(aOwner: TComponent); +begin + inherited Create(aOwner); + FThreadEntryPoint:=DefaultThreadEntryPoint; + FThreadInitInstanceEntry:=DefaultThreadInstanceInitPoint; + FSendOutputToBrowserWindow:=True; +end; + +{ TWorkerThreadSupport } + +function TWorkerThreadSupport.thread_spawn(thread_id: Integer; attrs: Integer; + thread_start_func: Integer; args: Integer): Integer; + +Var + P : TWorkerSpawnThreadCommand; + +begin + P:=TWorkerSpawnThreadCommand.Create(AllocateNewThreadID,Attrs,Args,thread_start_func,Thread_id); + SendCommand(P); + Env.SetMemInfoInt32(thread_id,P.ThreadID); + Result:=0; +end; + +function TWorkerThreadSupport.thread_detach(thread_id: Integer): Integer; +begin + Result:=0; +end; + +function TWorkerThreadSupport.thread_cancel(thread_id: Integer): Integer; +begin + Result:=0; +end; + +function TWorkerThreadSupport.thread_self: Integer; +begin + Result:=0; +end; + +function TWorkerThreadSupport.AllocateNewThreadID: NativeInt; + +begin + if (FNextThreadID-FStartThreadID)>=ThreadIDInterval then + FNextThreadID:=FStartThreadID; + Inc(FNextThreadID); + if (FNextThreadID-FStartThreadID)=ThreadIDInterval-ThreadIDMargin then + RequestNewThreadBlock; + Result:=FNextThreadID; +end; + +procedure TWorkerThreadSupport.SendLoaded; + +Var + L : TWorkerLoadedCommand; + +begin + L:=TWorkerLoadedCommand.Create(); + SendCommand(L); +end; + +procedure TWorkerThreadSupport.SendConsoleMessage(aMessage: String); + +Var + L : TWorkerConsoleCommand; + +begin + L:=TWorkerConsoleCommand.Create(aMessage,FCurrentThreadInfo.ThreadId); + SendCommand(L); +end; + +procedure TWorkerThreadSupport.SendConsoleMessage(aFmt: String; + const aArgs: array of const); +begin + SendConsoleMessage(Format(aFmt,aArgs)); +end; + +procedure TWorkerThreadSupport.SendConsoleMessage(const aArgs: array of JSValue); + +Var + L : TWorkerConsoleCommand; + +begin + L:=TWorkerConsoleCommand.Create(aArgs,FCurrentThreadInfo.ThreadId); + SendCommand(L); +end; + +procedure TWorkerThreadSupport.CancelWasmModule(aCommand : TWorkerCancelCommand); + +begin + // todo +end; + + +procedure TWorkerThreadSupport.SendException(aError : Exception); + +Var + E : TWorkerExceptionCommand; + +begin + E:=TWorkerExceptionCommand.CreateNew(aError.ClassName,aError.Message,FCurrentThreadInfo.ThreadId); + SendCommand(E); +end; + +procedure TWorkerThreadSupport.SendException(aError: TJSError); + +Var + aMessage,aClass : String; + E : TWorkerExceptionCommand; + +begin + aClass:='Error'; + aMessage:=aError.Message; + E:=TWorkerExceptionCommand.CreateNew(aClass,aMessage,FCurrentThreadInfo.ThreadId); + SendCommand(E); +end; + + +procedure TWorkerThreadSupport.RunWasmModule(aCommand : TWorkerRunCommand); + + Procedure DoRun (aExports : TWASIExports); + + Var + aResult : Integer; + + begin + try + // Writeln('About to run webassembly entry point (',Host.ThreadEntryPoint,') for thread ID ',aCommand.ThreadID); + aResult:=TThreadEntryPointFunction(aExports[Host.ThreadEntryPoint])(aCommand.ThreadInfo,aCommand.RunThreadProc, aCommand.args); + if aResult>0 then + Writeln('Thread run function result ',aResult); + except + on E : Exception do + SendException(E); + on JE : TJSError do + SendException(JE); + on JE : TJSError do + SendException(JE) + end; + + end; + +begin + // Writeln('Entering TWorkerThreadSupport.RunWasmModule '+TJSJSON.Stringify(aCommand)); + // initialize current thread info + FCurrentThreadInfo.ThreadID:=aCommand.ThreadID; + FCurrentThreadInfo.Arguments:=aCommand.Args; + FCurrentThreadInfo.ThreadInfoLocation:=aCommand.ThreadInfo; + FCurrentThreadInfo.Attributes:=aCommand.Attrs; + FCurrentThreadInfo.RunFunction:=aCommand.RunThreadProc; + Host.RunWebAssemblyThread(@DoRun); +end; + +procedure TWorkerThreadSupport.LoadWasmModule(aCommand: TWorkerLoadCommand); + + +Var + WASD : TWebAssemblyStartDescriptor; + aTable : TJSWebAssemblyTable; + + function doOK(aValue: JSValue): JSValue; + // We are using the overload that takes a compiled module. + // In that case the promise resolves to a WebAssembly.Instance, not to a InstantiateResult ! + Var + aInstance : TJSWebAssemblyInstance absolute aValue; + + begin + Result:=True; + WASD.Instance:=aInstance; + WASD.Exported:=TWASIExports(TJSObject(aInstance.exports_)); + WASD.CallRun:=Nil; + Host.PrepareWebAssemblyThread(WASD); + SendLoaded; + // These 2 prevent running different instances simultaneously. + end; + + function DoFail(aValue: JSValue): JSValue; + + var + E: Exception; + + begin + Result:=True; + E:=Exception.Create('Failed to create webassembly. Reason: '+TJSJSON.Stringify(aValue)); + SendException(E); + E.Free; + end; + + +begin + FMemory:=aCommand.Memory; + FModule:=aCommand.Module; + InitThreadRange(aCommand.ThreadRangeStart); + try + aTable:=TJSWebAssemblyTable.New(Host.TableDescriptor); + WASD:=Host.InitStartDescriptor(FMemory,aTable,Nil); + TJSWebAssembly.Instantiate(FModule,WASD.Imports)._then(@DoOK,@DoFail).Catch(@DoFail); + except + on E : Exception do + SendException(E); + on JE : TJSError do + SendException(JE); + end; +end; + + +procedure TWorkerThreadSupport.InitThreadRange(aRange: Integer); + +begin + FStartThreadID:=aRange; + FNextThreadID:=FStartThreadID; +end; + +procedure TWorkerThreadSupport.RequestNewThreadBlock; + +begin + SendCommand(TWorkerNeedIdBlockCommand.Create(FNextThreadID)); +end; + +procedure TWorkerThreadSupport.SetThreadRange( + aCommand: TWorkerThreadIDRangeCommand); + +begin + InitThreadRange(aCommand.RangeStart); +end; + +procedure TWorkerThreadSupport.HandleCommand(aCommand: TWorkerCommand); + +begin + case aCommand.Command of + cmdload : LoadWasmModule(TWorkerLoadCommand(aCommand)); + cmdRun : RunWasmModule(TWorkerRunCommand(aCommand)); + cmdCancel : CancelWasmModule(TWorkerCancelCommand(aCommand)); + cmdThreadIdRange : SetThreadRange(TWorkerThreadIDRangeCommand(aCommand)); + end; +end; + + + +{ TWorkerWASIHostApplication } + +function TWorkerWASIHostApplication.GetAfterStart: TAfterStartEvent; +begin + Result:=FHost.AfterStart; +end; + +function TWorkerWASIHostApplication.GetBeforeStart: TBeforeStartEvent; +begin + Result:=FHost.BeforeStart; +end; + +function TWorkerWASIHostApplication.GetcPredefinedConsoleInput: TStrings; +begin + Result:=FHost.PredefinedConsoleInput; +end; + +function TWorkerWASIHostApplication.GetEnv: TPas2JSWASIEnvironment; +begin + Result:=FHost.WasiEnvironment; +end; + +function TWorkerWASIHostApplication.GetExported: TWASIExports; +begin + Result:=FHost.Exported; +end; + + +function TWorkerWASIHostApplication.GetOnConsoleRead: TConsoleReadEvent; +begin + Result:=FHost.OnConsoleRead; +end; + +function TWorkerWASIHostApplication.GetOnConsoleWrite: TConsoleWriteEvent; +begin + Result:=FHost.OnConsoleWrite; +end; + +function TWorkerWASIHostApplication.GetRunEntryFunction: String; +begin + Result:=FHost.RunEntryFunction; +end; + + +procedure TWorkerWASIHostApplication.SetAfterStart(AValue: TAfterStartEvent); +begin + FHost.AfterStart:=aValue; +end; + +procedure TWorkerWASIHostApplication.SetBeforeStart(AValue: TBeforeStartEvent); +begin + FHost.BeforeStart:=aValue; +end; + +procedure TWorkerWASIHostApplication.SetOnConsoleRead(AValue: TConsoleReadEvent + ); +begin + FHost.OnConsoleRead:=aValue; +end; + +procedure TWorkerWASIHostApplication.SetOnConsoleWrite( + AValue: TConsoleWriteEvent); +begin + FHost.OnConsoleWrite:=aValue; +end; + +procedure TWorkerWASIHostApplication.SetPredefinedConsoleInput(AValue: TStrings); +begin + FHost.PredefinedConsoleInput:=aValue; +end; + +procedure TWorkerWASIHostApplication.SetRunEntryFunction(AValue: String); +begin + FHost.RunEntryFunction:=aValue; +end; + +function TWorkerWASIHostApplication.CreateHost : TWASIHost; + +Var + TH : TWasiThreadHost; + +begin + TH:=TWASIThreadHost.Create(Self); + FThreadSupport:=TWorkerThreadSupport.Create(TH.WasiEnvironment); + FThreadSupport.OnSendCommand:=@DoOnSendCommand; + TH.ThreadSupport:=FThreadSupport; // Sets FThreadSupport.host + Result:=TH; +end; + +procedure TWorkerWASIHostApplication.DoRun; +begin + Self_.addEventListener('message',@HandleMessage); +end; + +procedure TWorkerWASIHostApplication.HandleMessage(aEvent: TJSEvent); + +Var + aMessageEvent : TJSMessageEvent absolute aEvent; + aData : TWorkerCommand; + +begin + if IsObject(aMessageEvent.Data) and TJSObject(aMessageEvent.Data).hasOwnProperty('Command') then + begin + aData:=TWorkerCommand(aMessageEvent.Data); + FThreadSupport.HandleCommand(aData); + end + else + FThreadSupport.SendConsoleMessage('Unknown message received: '+TJSJSON.Stringify(aMessageEvent.Data)); +end; + +procedure TWorkerWASIHostApplication.DoOnSendCommand(Sender: TObject; + aCommand: TWorkerCommand); +begin + SendCommand(aCommand); +end; + +procedure TWorkerWASIHostApplication.SendCommand(aCommand: TWorkerCommand); +begin + Self_.PostMessage(aCommand); +end; + +function TWorkerWASIHostApplication.GetConsoleApplication: boolean; +begin + Result:=true; +end; + +function TWorkerWASIHostApplication.GetLocation: String; +begin + Result:=webworker.Location.pathname; +end; + +constructor TWorkerWASIHostApplication.Create(aOwner: TComponent); +begin + inherited Create(aOwner); + FHost:=CreateHost; +end; + +destructor TWorkerWASIHostApplication.Destroy; +begin + FreeAndNil(FHost); + inherited Destroy; +end; + +procedure TWorkerWASIHostApplication.GetEnvironmentList(List: TStrings; + NamesOnly: Boolean); +var + Names: TStringDynArray; + i: Integer; +begin + Names:=TJSObject.getOwnPropertyNames(EnvNames); + for i:=0 to length(Names)-1 do + begin + if NamesOnly then + List.Add(Names[i]) + else + List.Add(Names[i]+'='+String(EnvNames[Names[i]])); + end; +end; + +procedure TWorkerWASIHostApplication.ShowException(E: Exception); + +begin + ThreadSupport.SendException(E); +end; + +procedure TWorkerWASIHostApplication.StartWebAssembly(aPath: string; DoRun: Boolean; + aBeforeStart: TBeforeStartCallback = nil; aAfterStart: TAfterStartCallback = nil); + +begin + FHost.StartWebAssembly(aPath,DoRun,aBeforeStart,aAfterStart); +end; + +Initialization + ReloadEnvironmentStrings; + OnGetEnvironmentVariable:=@MyGetEnvironmentVariable; + OnGetEnvironmentVariableCount:=@MyGetEnvironmentVariableCount; + OnGetEnvironmentString:=@MyGetEnvironmentString; +end. +