diff --git a/404.html b/404.html index 788c1879..6b0185f2 100644 --- a/404.html +++ b/404.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/assets/stylesheets/main.6f955dcd.min.css b/assets/stylesheets/main.6f955dcd.min.css deleted file mode 100644 index 80c74b19..00000000 --- a/assets/stylesheets/main.6f955dcd.min.css +++ /dev/null @@ -1,2 +0,0 @@ -@charset "UTF-8";html{box-sizing:border-box;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}*,:after,:before{box-sizing:inherit}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{display:block;box-sizing:initial;height:.05rem;padding:0;overflow:visible;border:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{margin:0;padding:0;font-size:inherit;font-family:inherit;background:transparent;border:0}input{border:0;outline:none}:root{--md-default-fg-color:rgba(0,0,0,0.87);--md-default-fg-color--light:rgba(0,0,0,0.54);--md-default-fg-color--lighter:rgba(0,0,0,0.32);--md-default-fg-color--lightest:rgba(0,0,0,0.07);--md-default-bg-color:#fff;--md-default-bg-color--light:hsla(0,0%,100%,0.7);--md-default-bg-color--lighter:hsla(0,0%,100%,0.3);--md-default-bg-color--lightest:hsla(0,0%,100%,0.12);--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:hsla(0,0%,100%,0.7);--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:rgba(83,108,254,0.1);--md-accent-bg-color:#fff;--md-accent-bg-color--light:hsla(0,0%,100%,0.7)}:root>*{--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:rgba(255,255,0,0.5);--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-mark-color:rgba(255,255,0,0.5);--md-typeset-del-color:rgba(245,80,61,0.15);--md-typeset-ins-color:rgba(11,213,112,0.15);--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-fg-color:#fff;--md-footer-fg-color--light:hsla(0,0%,100%,0.7);--md-footer-fg-color--lighter:hsla(0,0%,100%,0.3);--md-footer-bg-color:rgba(0,0,0,0.87);--md-footer-bg-color--dark:rgba(0,0,0,0.32)}.md-icon svg{display:block;width:1.2rem;height:1.2rem;fill:currentColor}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{font-feature-settings:"kern","liga";font-family:var(--md-text-font-family,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif}body,code,input,kbd,pre{color:var(--md-typeset-color)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family,_),SFMono-Regular,Consolas,Menlo,monospace}:root{--md-typeset-table--ascending:url("data:image/svg+xml;charset=utf-8,");--md-typeset-table--descending:url("data:image/svg+xml;charset=utf-8,")}.md-typeset{font-size:.8rem;line-height:1.6;-webkit-print-color-adjust:exact;color-adjust:exact}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin:1em 0}.md-typeset h1{margin:0 0 1.25em;color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{margin:1.6em 0 .64em;font-size:1.5625em;line-height:1.4}.md-typeset h3{margin:1.6em 0 .8em;font-weight:400;font-size:1.25em;line-height:1.5;letter-spacing:-.01em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{margin:1em 0;font-weight:700;letter-spacing:-.01em}.md-typeset h5,.md-typeset h6{margin:1.25em 0;color:var(--md-default-fg-color--light);font-weight:700;font-size:.8em;letter-spacing:-.01em}.md-typeset h5{text-transform:uppercase}.md-typeset hr{display:flow-root;margin:1.5em 0;border-bottom:.05rem solid var(--md-default-fg-color--lightest)}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{padding:0 .2941176471em;font-size:.85em;word-break:break-word;background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset code:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:initial;padding:initial;background-color:initial;box-shadow:none}.md-typeset a code{color:currentColor}.md-typeset pre{position:relative;display:flow-root;line-height:1.4}.md-typeset pre>code{display:block;margin:0;padding:.7720588235em 1.1764705882em;overflow:auto;word-break:normal;box-shadow:none;-webkit-box-decoration-break:slice;box-decoration-break:slice;touch-action:auto;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-typeset pre>code::-webkit-scrollbar{width:.2rem;height:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:44.9375em){.md-typeset>pre{margin:1em -.8rem}.md-typeset>pre code{border-radius:0}}.md-typeset kbd{display:inline-block;padding:0 .6666666667em;color:var(--md-default-fg-color);font-size:.75em;vertical-align:text-top;word-break:break-word;background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset}.md-typeset mark{color:inherit;word-break:break-word;background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{text-decoration:none;border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help}@media (hover:none){.md-typeset abbr{position:relative}.md-typeset abbr[title]:focus:after,.md-typeset abbr[title]:hover:after{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:absolute;left:0;display:inline-block;width:auto;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;max-width:80%;margin-top:2em;padding:.2rem .3rem;color:var(--md-default-bg-color);font-size:.7rem;background-color:var(--md-default-fg-color);border-radius:.1rem;content:attr(title)}}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em;margin-left:0}.md-typeset blockquote{display:flow-root;padding-left:.6rem;color:var(--md-default-fg-color--light);border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{padding-right:.6rem;padding-left:0;border-right:.2rem solid var(--md-default-fg-color--lighter);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{display:flow-root;margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em;margin-left:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em;margin-left:0}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em;margin-left:0}.md-typeset dd{margin:1em 0 1.5em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:0}.md-typeset img,.md-typeset svg{max-width:100%;height:auto}.md-typeset img[align=left],.md-typeset svg[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right],.md-typeset svg[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child,.md-typeset svg[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;margin:0 auto;text-align:center}.md-typeset figure img{display:block}.md-typeset figcaption{max-width:24rem;margin:1em auto 2em;font-style:italic}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){display:inline-block;max-width:100%;overflow:auto;font-size:.64rem;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .05rem rgba(0,0,0,.1);touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:5rem;padding:.9375em 1.25em;color:var(--md-default-bg-color);vertical-align:top;background-color:var(--md-default-fg-color--light)}.md-typeset table:not([class]) th a{color:inherit}.md-typeset table:not([class]) td{padding:.9375em 1.25em;vertical-align:top;border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-typeset table:not([class]) tr{transition:background-color 125ms}.md-typeset table:not([class]) tr:hover{background-color:rgba(0,0,0,.035);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}.md-typeset table th[role=columnheader]:after{display:inline-block;width:1.2em;height:1.2em;margin-left:.5em;vertical-align:sub;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:currentColor;-webkit-mask-image:var(--md-typeset-table--ascending);mask-image:var(--md-typeset-table--ascending)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:currentColor;-webkit-mask-image:var(--md-typeset-table--descending);mask-image:var(--md-typeset-table--descending)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{height:100%;overflow-x:hidden;font-size:125%}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{position:relative;display:flex;flex-direction:column;width:100%;min-height:100%;font-size:.5rem;background-color:var(--md-default-bg-color)}@media print{body{display:block}}@media screen and (max-width:59.9375em){body[data-md-state=lock]{position:fixed}}.md-grid{max-width:61rem;margin-right:auto;margin-left:auto}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.md-toggle{display:none}.md-option{position:absolute;width:0;height:0;opacity:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-style:auto}.md-skip{position:fixed;z-index:-1;margin:.5rem;padding:.3rem .5rem;color:var(--md-default-bg-color);font-size:.64rem;background-color:var(--md-default-fg-color);border-radius:.1rem;transform:translateY(.4rem);opacity:0}.md-skip:focus{z-index:10;transform:translateY(0);opacity:1;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms}@page{margin:25mm}.md-announce{overflow:auto;background-color:var(--md-footer-bg-color)}@media print{.md-announce{display:none}}.md-announce__inner{margin:.6rem auto;padding:0 .8rem;color:var(--md-footer-fg-color);font-size:.7rem}:root{--md-clipboard-icon:url("data:image/svg+xml;charset=utf-8,")}.md-clipboard{position:absolute;top:.5em;right:.5em;z-index:1;width:1.5em;height:1.5em;color:var(--md-default-fg-color--lightest);border-radius:.1rem;cursor:pointer;transition:color .25s}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{display:block;width:1.125em;height:1.125em;margin:0 auto;background-color:currentColor;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{color:var(--md-accent-fg-color);background-color:var(--md-accent-fg-color--transparent)}.md-content{flex-grow:1;overflow:hidden;scroll-padding-top:51.2rem}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){.md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem;margin-left:.8rem}.md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-right:.8rem;margin-left:1.2rem}}.md-content__inner:before{display:block;height:.4rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__button{float:right;margin:.4rem 0 .4rem .4rem;padding:0}@media print{.md-content__button{display:none}}[dir=rtl] .md-content__button{float:left;margin-right:.4rem;margin-left:0}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}.md-dialog{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:fixed;right:.8rem;bottom:.8rem;left:auto;z-index:2;min-width:11.1rem;padding:.4rem .6rem;background-color:var(--md-default-fg-color);border-radius:.1rem;transform:translateY(100%);opacity:0;transition:transform 0ms .4s,opacity .4s;pointer-events:none}@media print{.md-dialog{display:none}}[dir=rtl] .md-dialog{right:auto;left:.8rem}.md-dialog[data-md-state=open]{transform:translateY(0);opacity:1;transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s;pointer-events:auto}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-typeset .md-button{display:inline-block;padding:.625em 2em;color:var(--md-primary-fg-color);font-weight:700;border:.1rem solid;border-radius:.1rem;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{color:var(--md-accent-bg-color);background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}.md-typeset .md-input{height:1.8rem;padding:0 .6rem;font-size:.8rem;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 .025rem .05rem rgba(0,0,0,.1);transition:box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{box-shadow:0 .4rem 1rem rgba(0,0,0,.15),0 .025rem .05rem rgba(0,0,0,.15)}.md-typeset .md-input--stretch{width:100%}.md-header{position:-webkit-sticky;position:sticky;top:0;right:0;left:0;z-index:2;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem transparent,0 .2rem .4rem transparent}@media print{.md-header{display:none}}.md-header[data-md-state=shadow]{box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2);transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header[data-md-state=hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header .focus-visible{outline-color:currentColor}.md-header__inner{display:flex;align-items:center;padding:0 .2rem}.md-header__button{position:relative;z-index:1;margin:.2rem;padding:.4rem;color:currentColor;vertical-align:middle;cursor:pointer;transition:opacity .25s}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.1875em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{display:block;width:1.2rem;height:1.2rem;fill:currentColor}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{position:absolute;display:flex;max-width:100%;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-header__topic+.md-header__topic{z-index:-1;transform:translateX(1.25rem);opacity:0;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;pointer-events:none}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__title{flex-grow:1;height:2.4rem;margin-right:.4rem;margin-left:1rem;font-size:.9rem;line-height:2.4rem}.md-header__title[data-md-state=active] .md-header__topic{z-index:-1;transform:translateX(-1.25rem);opacity:0;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;pointer-events:none}[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic{transform:translateX(1.25rem)}.md-header__title[data-md-state=active] .md-header__topic+.md-header__topic{z-index:0;transform:translateX(0);opacity:1;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;pointer-events:auto}.md-header__title>.md-header__ellipsis{position:relative;width:100%;height:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;white-space:nowrap;transition:max-width 0ms .25s,opacity .25s .25s}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__source{display:none}@media screen and (min-width:60em){.md-header__source{display:block;width:11.7rem;max-width:11.7rem;margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem;margin-left:0}}@media screen and (min-width:76.25em){.md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-footer{color:var(--md-footer-fg-color);background-color:var(--md-footer-bg-color)}@media print{.md-footer{display:none}}.md-footer__inner{padding:.2rem;overflow:auto}.md-footer__link{display:flex;padding-top:1.4rem;padding-bottom:.4rem;transition:opacity .25s}@media screen and (min-width:45em){.md-footer__link{width:50%}}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}.md-footer__link--prev{float:left}@media screen and (max-width:44.9375em){.md-footer__link--prev{width:25%}.md-footer__link--prev .md-footer__title{display:none}}[dir=rtl] .md-footer__link--prev{float:right}[dir=rtl] .md-footer__link--prev svg{transform:scaleX(-1)}.md-footer__link--next{float:right;text-align:right}@media screen and (max-width:44.9375em){.md-footer__link--next{width:75%}}[dir=rtl] .md-footer__link--next{float:left;text-align:left}[dir=rtl] .md-footer__link--next svg{transform:scaleX(-1)}.md-footer__title{position:relative;flex-grow:1;max-width:calc(100% - 2.4rem);padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{position:absolute;right:0;left:0;margin-top:-1rem;padding:0 1rem;font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-footer-copyright{width:100%;margin:auto .6rem;padding:.4rem 0;color:var(--md-footer-fg-color--lighter);font-size:.64rem}@media screen and (min-width:45em){.md-footer-copyright{width:auto}}.md-footer-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-footer-social{margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-footer-social{padding:.6rem 0}}.md-footer-social__link{display:inline-block;width:1.6rem;height:1.6rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-footer-social__link svg{max-height:.8rem;vertical-align:-25%;fill:currentColor}:root{--md-nav-icon--prev:url("data:image/svg+xml;charset=utf-8,");--md-nav-icon--next:url("data:image/svg+xml;charset=utf-8,");--md-toc-icon:url("data:image/svg+xml;charset=utf-8,")}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{display:block;padding:0 .6rem;overflow:hidden;font-weight:700;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{width:auto;height:100%}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{display:block;width:2.4rem;height:2.4rem;fill:currentColor}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 .6rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:.6rem;padding-left:0}.md-nav__link{display:block;margin-top:.625em;overflow:hidden;text-overflow:ellipsis;cursor:pointer;transition:color 125ms;scroll-snap-align:start}.md-nav__link[data-md-state=blur]{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active{color:var(--md-typeset-a-color)}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{display:block;width:100%;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);background-color:currentColor}.md-nav--primary .md-nav__link[for=__toc]~.md-nav,.md-nav__source{display:none}@media screen and (max-width:76.1875em){.md-nav--primary,.md-nav--primary .md-nav{position:absolute;top:0;right:0;left:0;z-index:1;display:flex;flex-direction:column;height:100%;background-color:var(--md-default-bg-color)}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{position:relative;height:5.6rem;padding:3rem .8rem .2rem;color:var(--md-default-fg-color--light);font-weight:400;line-height:2.4rem;white-space:nowrap;background-color:var(--md-default-fg-color--lightest);cursor:pointer}.md-nav--primary .md-nav__title .md-nav__icon{position:absolute;top:.4rem;left:.4rem;display:block;width:1.2rem;height:1.2rem;margin:.2rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem;left:auto}.md-nav--primary .md-nav__title .md-nav__icon:after{display:block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-nav--primary .md-nav__title~.md-nav__list{overflow-y:auto;background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color)}.md-nav--primary .md-nav__title .md-logo{position:absolute;top:.2rem;left:.2rem;display:block;margin:.2rem;padding:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-logo{right:.2rem;left:auto}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{padding:0;border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link .md-nav__icon{position:absolute;top:50%;right:.6rem;width:1.2rem;height:1.2rem;margin-top:-.6rem;color:inherit;font-size:1.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{right:auto;left:.6rem}.md-nav--primary .md-nav__link .md-nav__icon:after{display:block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem;padding-left:0}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;transform:translateX(100%);opacity:0;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{transform:translateX(0);opacity:1;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.9375em){.md-nav--primary .md-nav__link[for=__toc]{display:block;padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__link[for=__toc]{padding-right:.8rem;padding-left:2.4rem}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{display:block;padding:0 .2rem;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color--dark)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-nav--integrated .md-nav__link[for=__toc]{display:block;padding-right:2.4rem;scroll-snap-align:none}[dir=rtl] .md-nav--integrated .md-nav__link[for=__toc]{padding-right:.8rem;padding-left:2.4rem}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width:76.25em){.md-nav{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon,.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav,.md-nav__toggle:indeterminate~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{display:none}.md-nav__item--section>.md-nav{display:block}.md-nav__item--section>.md-nav>.md-nav__title{display:block;padding:0;pointer-events:none;scroll-snap-align:start}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{float:right;width:.9rem;height:.9rem;transition:transform .25s}[dir=rtl] .md-nav__icon{float:left;transform:rotate(180deg)}.md-nav__icon:after{display:inline-block;width:100%;height:100%;vertical-align:-.1rem;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon,.md-nav__item--nested .md-nav__toggle:indeterminate~.md-nav__link .md-nav__icon{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__list>.md-nav__item--nested,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav>.md-nav__title{display:block;padding:0 .6rem;pointer-events:none;scroll-snap-align:start}.md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav__item{padding-right:.6rem}.md-nav--lifted .md-nav[data-md-level="1"]{display:block}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:block;margin-bottom:1.25em;border-left:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav>.md-nav__title{display:none}}:root{--md-search-result-icon:url("data:image/svg+xml;charset=utf-8,")}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{z-index:1;opacity:0}@media screen and (max-width:59.9375em){.md-search__overlay{position:absolute;top:.2rem;left:-2.2rem;width:2rem;height:2rem;overflow:hidden;background-color:var(--md-default-bg-color);border-radius:1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;pointer-events:none}[dir=rtl] .md-search__overlay{right:-2.2rem;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;background-color:rgba(0,0,0,.54);cursor:pointer;transition:width 0ms .25s,height 0ms .25s,opacity .25s}[dir=rtl] .md-search__overlay{right:0;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s}}@media screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.9375em){.md-search__inner{position:fixed;top:0;left:100%;z-index:2;width:100%;height:100%;transform:translateX(5%);opacity:0;transition:right 0ms .3s,left 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;transform:translateX(0);opacity:1;transition:right 0ms 0ms,left 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:auto}html [dir=rtl] .md-search__inner{right:100%;left:auto;transform:translateX(-5%)}}@media screen and (min-width:60em){.md-search__inner{position:relative;float:right;width:11.7rem;padding:.1rem 0;transition:width .25s cubic-bezier(.1,.7,.1,1)}[dir=rtl] .md-search__inner{float:left}}@media screen and (min-width:60em) and (max-width:76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{position:relative}@media screen and (min-width:60em){.md-search__form{border-radius:.1rem}}.md-search__input{position:relative;z-index:2;padding:0 2.2rem 0 3.6rem;text-overflow:ellipsis;background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem transparent;transition:color .25s,background-color .25s,box-shadow .25s}[dir=rtl] .md-search__input{padding:0 3.6rem 0 2.2rem}.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s;transition:color .25s}.md-search__input::-moz-placeholder{-moz-transition:color .25s;transition:color .25s}.md-search__input::-ms-input-placeholder{-ms-transition:color .25s;transition:color .25s}.md-search__input::placeholder{transition:color .25s}.md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}[data-md-toggle=search]:checked~.md-header .md-search__input{box-shadow:0 0 .6rem rgba(0,0,0,.07)}@media screen and (max-width:59.9375em){.md-search__input{width:100%;height:2.4rem;font-size:.9rem}}@media screen and (min-width:60em){.md-search__input{width:100%;height:1.8rem;padding-left:2.2rem;color:inherit;font-size:.8rem;background-color:rgba(0,0,0,.26);border-radius:.1rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}.md-search__input::-webkit-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{color:var(--md-default-fg-color);text-overflow:clip;background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0}[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:var(--md-default-fg-color--light)}}.md-search__icon{position:absolute;z-index:2;width:1.2rem;height:1.2rem;cursor:pointer;transition:color .25s,opacity .25s}.md-search__icon:hover{opacity:.7}.md-search__icon[for=__search]{top:.3rem;left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem;left:auto}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.9375em){.md-search__icon[for=__search]{top:.6rem;left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem;left:auto}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}.md-search__icon[type=reset]{top:.3rem;right:.5rem;transform:scale(.75);opacity:0;transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s;pointer-events:none}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:.5rem}@media screen and (max-width:59.9375em){.md-search__icon[type=reset]{top:.6rem;right:.8rem}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]{transform:scale(1);opacity:1;pointer-events:auto}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;z-index:1;width:100%;overflow:hidden;border-radius:0 0 .1rem .1rem}@media screen and (max-width:59.9375em){.md-search__output{top:2.4rem;bottom:0}}@media screen and (min-width:60em){.md-search__output{top:1.9rem;opacity:0;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}}.md-search__scrollwrap{height:100%;overflow-y:auto;background-color:var(--md-default-bg-color);-webkit-backface-visibility:hidden;backface-visibility:hidden;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1), (max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-search__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{padding:0 .8rem;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;background-color:var(--md-default-fg-color--lightest);scroll-snap-align:start}@media screen and (min-width:60em){.md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem;padding-left:0}}.md-search-result__list{margin:0;padding:0;list-style:none}.md-search-result__item{box-shadow:0 -.05rem 0 var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;transition:background-color .25s;scroll-snap-align:start}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more summary{display:block;padding:.75em .8rem;color:var(--md-typeset-a-color);font-size:.64rem;outline:none;cursor:pointer;transition:color .25s,background-color .25s;scroll-snap-align:start}@media screen and (min-width:60em){.md-search-result__more summary{padding-left:2.2rem}[dir=rtl] .md-search-result__more summary{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__more summary:focus,.md-search-result__more summary:hover{color:var(--md-accent-fg-color);background-color:var(--md-accent-fg-color--transparent)}.md-search-result__more summary::-webkit-details-marker,.md-search-result__more summary::marker{display:none}.md-search-result__more summary~*>*{opacity:.65}.md-search-result__article{position:relative;padding:0 .8rem;overflow:hidden}@media screen and (min-width:60em){.md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__article--document .md-search-result__title{margin:.55rem 0;font-weight:400;font-size:.8rem;line-height:1.4}.md-search-result__icon{position:absolute;left:0;width:1.2rem;height:1.2rem;margin:.5rem;color:var(--md-default-fg-color--light)}@media screen and (max-width:59.9375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{display:inline-block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-search-result__icon{right:0;left:auto}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result__title{margin:.5em 0;font-weight:700;font-size:.64rem;line-height:1.6}.md-search-result__teaser{display:-webkit-box;max-height:2rem;margin:.5em 0;overflow:hidden;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}@media screen and (max-width:44.9375em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}.md-search-result__teaser mark{text-decoration:underline;background-color:initial}.md-search-result__terms{margin:.5em 0;font-size:.64rem;font-style:italic}.md-search-result mark{color:var(--md-accent-fg-color);background-color:initial}.md-select{position:relative;z-index:1}.md-select__inner{position:absolute;top:calc(100% - .2rem);left:50%;max-height:0;margin-top:.2rem;color:var(--md-default-fg-color);background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);transform:translate3d(-50%,.3rem,0);opacity:0;transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;transform:translate3d(-50%,0,0);opacity:1;transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{position:absolute;top:0;left:50%;width:0;height:0;margin-top:-.2rem;margin-left:-.2rem;border-left:.2rem solid transparent;border-right:.2rem solid transparent;border-top:0;border-bottom:.2rem solid transparent;border-bottom-color:var(--md-default-bg-color);content:""}.md-select__list{max-height:inherit;margin:0;padding:0;overflow:auto;font-size:.8rem;list-style-type:none;border-radius:.1rem}.md-select__item{line-height:1.8rem}.md-select__link{display:block;width:100%;padding-right:1.2rem;padding-left:.6rem;outline:none;cursor:pointer;transition:background-color .25s,color .25s;scroll-snap-align:start}[dir=rtl] .md-select__link{padding-right:.6rem;padding-left:1.2rem}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{position:-webkit-sticky;position:sticky;top:2.4rem;flex-shrink:0;align-self:flex-start;width:12.1rem;padding:1.2rem 0}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.1875em){.md-sidebar--primary{position:fixed;top:0;left:-12.1rem;z-index:3;display:block;width:12.1rem;height:100%;background-color:var(--md-default-bg-color);transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s}[dir=rtl] .md-sidebar--primary{right:-12.1rem;left:auto}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{position:absolute;top:0;right:0;bottom:0;left:0;margin:0;-ms-scroll-snap-type:none;scroll-snap-type:none;overflow:hidden}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{margin:0 .2rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:76.1875em){.md-overlay{position:fixed;top:0;z-index:3;width:0;height:0;background-color:rgba(0,0,0,.54);opacity:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s}[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s}}@-webkit-keyframes md-source__facts--done{0%{height:0}to{height:.65rem}}@keyframes md-source__facts--done{0%{height:0}to{height:.65rem}}@-webkit-keyframes md-source__fact--done{0%{transform:translateY(100%);opacity:0}50%{opacity:0}to{transform:translateY(0);opacity:1}}@keyframes md-source__fact--done{0%{transform:translateY(100%);opacity:0}50%{opacity:0}to{transform:translateY(0);opacity:1}}:root{--md-source-forks-icon:url("data:image/svg+xml;charset=utf-8,");--md-source-repositories-icon:url("data:image/svg+xml;charset=utf-8,");--md-source-stars-icon:url("data:image/svg+xml;charset=utf-8,");--md-source-version-icon:url("data:image/svg+xml;charset=utf-8,")}.md-source{display:block;font-size:.65rem;line-height:1.2;white-space:nowrap;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:opacity .25s}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;width:2rem;height:2.4rem;vertical-align:middle}.md-source__icon svg{margin-top:.6rem;margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem;margin-left:0}.md-source__icon+.md-source__repository{margin-left:-2rem;padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem;margin-left:0;padding-right:2rem;padding-left:0}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);margin-left:.6rem;overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{margin:.1rem 0 0;padding:0;overflow:hidden;font-size:.55rem;list-style-type:none;opacity:.75}[data-md-state=done] .md-source__facts{-webkit-animation:md-source__facts--done .25s ease-in;animation:md-source__facts--done .25s ease-in}.md-source__fact{display:inline-block}[data-md-state=done] .md-source__fact{-webkit-animation:md-source__fact--done .4s ease-out;animation:md-source__fact--done .4s ease-out}.md-source__fact:before{display:inline-block;width:.6rem;height:.6rem;margin-right:.1rem;vertical-align:text-top;background-color:currentColor;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-source__fact:nth-child(1n+2):before{margin-left:.4rem}[dir=rtl] .md-source__fact{margin-right:0;margin-left:.1rem}[dir=rtl] .md-source__fact:nth-child(1n+2):before{margin-right:.4rem;margin-left:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-tabs{width:100%;overflow:auto;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color)}@media print{.md-tabs{display:none}}@media screen and (max-width:76.1875em){.md-tabs{display:none}}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs__list{margin:0 0 0 .2rem;padding:0;white-space:nowrap;list-style:none;contain:content}[dir=rtl] .md-tabs__list{margin-right:.2rem;margin-left:0}.md-tabs__item{display:inline-block;height:2.4rem;padding-right:.6rem;padding-left:.6rem}.md-tabs__link{display:block;margin-top:.8rem;font-size:.7rem;-webkit-backface-visibility:hidden;backface-visibility:hidden;opacity:.7;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link--active,.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[data-md-state=hidden] .md-tabs__link{transform:translateY(50%);opacity:0;transition:transform 0ms .1s,opacity .1s}.md-top{position:-webkit-sticky;position:sticky;bottom:.4rem;z-index:1;float:right;margin:-2.8rem .4rem .4rem;padding:.4rem;color:var(--md-primary-bg-color);background:var(--md-primary-fg-color);border-radius:100%;outline:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 .025rem .05rem rgba(0,0,0,.1);transform:translateY(0);transition:opacity 125ms,transform 125ms cubic-bezier(.4,0,.2,1),background-color 125ms}[dir=rtl] .md-top{float:left}.md-top[data-md-state=hidden]{transform:translateY(-.2rem);opacity:0;pointer-events:none}.md-top:focus,.md-top:hover{background:var(--md-accent-fg-color);transform:scale(1.1)}:root{--md-version-icon:url("data:image/svg+xml;charset=utf-8,")}.md-version{flex-shrink:0;height:2.4rem;font-size:.8rem}.md-version__current{position:relative;top:.05rem;margin-right:.4rem;margin-left:1.4rem;color:inherit;outline:none;cursor:pointer}[dir=rtl] .md-version__current{margin-right:1.4rem;margin-left:.4rem}.md-version__current:after{display:inline-block;width:.4rem;height:.6rem;margin-left:.4rem;background-color:currentColor;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-version__current:after{margin-right:.4rem;margin-left:0}.md-version__list{position:absolute;top:.15rem;z-index:1;max-height:0;margin:.2rem .8rem;padding:0;overflow:auto;color:var(--md-default-fg-color);list-style-type:none;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);opacity:0;transition:max-height 0ms .5s,opacity .25s .25s;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}.md-version__item{line-height:1.8rem}.md-version__link{display:block;width:100%;padding-right:1.2rem;padding-left:.6rem;white-space:nowrap;outline:none;cursor:pointer;transition:color .25s,background-color .25s;scroll-snap-align:start}[dir=rtl] .md-version__link{padding-right:.6rem;padding-left:1.2rem}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--abstract:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--info:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--tip:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--success:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--question:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--warning:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--failure:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--danger:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--bug:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--example:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--quote:url("data:image/svg+xml;charset=utf-8,")}.md-typeset .admonition,.md-typeset details{margin:1.5625em 0;padding:0 .6rem;overflow:hidden;color:var(--md-admonition-fg-color);font-size:.64rem;page-break-inside:avoid;background-color:var(--md-admonition-bg-color);border-left:.2rem solid #448aff;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 .025rem .05rem rgba(0,0,0,.05)}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.2rem solid #448aff;border-left:none}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-top:1em;margin-bottom:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}.md-typeset .admonition-title,.md-typeset summary{position:relative;margin:0 -.6rem 0 -.8rem;padding:.4rem .6rem .4rem 2rem;font-weight:700;background-color:rgba(68,138,255,.1);border-left:.2rem solid #448aff}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{margin:0 -.8rem 0 -.6rem;padding:.4rem 2rem .4rem .6rem;border-right:.2rem solid #448aff;border-left:none}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}.md-typeset .admonition-title:before,.md-typeset summary:before{position:absolute;left:.6rem;width:1rem;height:1rem;background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem;left:auto}.md-typeset .admonition-title+.tabbed-set:last-child,.md-typeset summary+.tabbed-set:last-child{margin-top:0}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:rgba(68,138,255,.1);border-color:#448aff}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-color:#00b0ff}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary,.md-typeset .summary>.admonition-title,.md-typeset .summary>summary,.md-typeset .tldr>.admonition-title,.md-typeset .tldr>summary{background-color:rgba(0,176,255,.1);border-color:#00b0ff}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before,.md-typeset .summary>.admonition-title:before,.md-typeset .summary>summary:before,.md-typeset .tldr>.admonition-title:before,.md-typeset .tldr>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-color:#00b8d4}.md-typeset .info>.admonition-title,.md-typeset .info>summary,.md-typeset .todo>.admonition-title,.md-typeset .todo>summary{background-color:rgba(0,184,212,.1);border-color:#00b8d4}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before,.md-typeset .todo>.admonition-title:before,.md-typeset .todo>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .hint>.admonition-title,.md-typeset .hint>summary,.md-typeset .important>.admonition-title,.md-typeset .important>summary,.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:rgba(0,191,165,.1);border-color:#00bfa5}.md-typeset .hint>.admonition-title:before,.md-typeset .hint>summary:before,.md-typeset .important>.admonition-title:before,.md-typeset .important>summary:before,.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-color:#00c853}.md-typeset .check>.admonition-title,.md-typeset .check>summary,.md-typeset .done>.admonition-title,.md-typeset .done>summary,.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:rgba(0,200,83,.1);border-color:#00c853}.md-typeset .check>.admonition-title:before,.md-typeset .check>summary:before,.md-typeset .done>.admonition-title:before,.md-typeset .done>summary:before,.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-color:#64dd17}.md-typeset .faq>.admonition-title,.md-typeset .faq>summary,.md-typeset .help>.admonition-title,.md-typeset .help>summary,.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:rgba(100,221,23,.1);border-color:#64dd17}.md-typeset .faq>.admonition-title:before,.md-typeset .faq>summary:before,.md-typeset .help>.admonition-title:before,.md-typeset .help>summary:before,.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-color:#ff9100}.md-typeset .attention>.admonition-title,.md-typeset .attention>summary,.md-typeset .caution>.admonition-title,.md-typeset .caution>summary,.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:rgba(255,145,0,.1);border-color:#ff9100}.md-typeset .attention>.admonition-title:before,.md-typeset .attention>summary:before,.md-typeset .caution>.admonition-title:before,.md-typeset .caution>summary:before,.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-color:#ff5252}.md-typeset .fail>.admonition-title,.md-typeset .fail>summary,.md-typeset .failure>.admonition-title,.md-typeset .failure>summary,.md-typeset .missing>.admonition-title,.md-typeset .missing>summary{background-color:rgba(255,82,82,.1);border-color:#ff5252}.md-typeset .fail>.admonition-title:before,.md-typeset .fail>summary:before,.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before,.md-typeset .missing>.admonition-title:before,.md-typeset .missing>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-color:#ff1744}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary,.md-typeset .error>.admonition-title,.md-typeset .error>summary{background-color:rgba(255,23,68,.1);border-color:#ff1744}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before,.md-typeset .error>.admonition-title:before,.md-typeset .error>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:rgba(245,0,87,.1);border-color:#f50057}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:rgba(124,77,255,.1);border-color:#7c4dff}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .cite>.admonition-title,.md-typeset .cite>summary,.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:hsla(0,0%,62%,.1);border-color:#9e9e9e}.md-typeset .cite>.admonition-title:before,.md-typeset .cite>summary:before,.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}:root{--md-footnotes-icon:url("data:image/svg+xml;charset=utf-8,")}.md-typeset [id^="fnref:"]:target{scroll-margin-top:0;margin-top:-3.4rem;padding-top:3.4rem}.md-typeset [id^="fn:"]:target{scroll-margin-top:0;margin-top:-3.45rem;padding-top:3.45rem}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}.md-typeset .footnote>ol{margin-left:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{transform:translateX(0);opacity:1}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-backref{display:inline-block;color:var(--md-typeset-a-color);font-size:0;vertical-align:text-bottom;transform:translateX(.25rem);opacity:0;transition:color .25s,transform .25s .25s,opacity 125ms .25s}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);transform:translateX(0);opacity:1}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{display:inline-block;width:.8rem;height:.8rem;background-color:currentColor;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}.md-typeset .headerlink{display:inline-block;margin-left:.5rem;color:var(--md-default-fg-color--lighter);opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem;margin-left:0}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{scroll-margin-top:3.6rem}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{scroll-margin-top:0}.md-typeset h1:target:before,.md-typeset h2:target:before,.md-typeset h3:target:before{display:block;margin-top:-3.4rem;padding-top:3.4rem;content:""}.md-typeset h4:target{scroll-margin-top:0}.md-typeset h4:target:before{display:block;margin-top:-3.45rem;padding-top:3.45rem;content:""}.md-typeset h5:target,.md-typeset h6:target{scroll-margin-top:0}.md-typeset h5:target:before,.md-typeset h6:target:before{display:block;margin-top:-3.6rem;padding-top:3.6rem;content:""}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.9375em){.md-typeset div.arithmatex{margin:0 -.8rem}}.md-typeset div.arithmatex>*{width:-webkit-min-content;width:-moz-min-content;width:min-content;margin:1em auto!important;padding:0 .8rem;touch-action:auto}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:.8rem;padding-left:.8rem;overflow:auto;box-shadow:none}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url("data:image/svg+xml;charset=utf-8,")}.md-typeset details{display:flow-root;padding-top:0;overflow:visible}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){padding-bottom:0;box-shadow:none}.md-typeset details:not([open])>summary{border-radius:.1rem}.md-typeset details:after{display:table;content:""}.md-typeset summary{display:block;min-height:1rem;padding:.4rem 1.8rem .4rem 2rem;border-top-left-radius:.1rem;border-top-right-radius:.1rem;cursor:pointer}[dir=rtl] .md-typeset summary{padding:.4rem 2.2rem .4rem 1.8rem}.md-typeset summary:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}.md-typeset summary:after{position:absolute;top:.4rem;right:.4rem;width:1rem;height:1rem;background-color:currentColor;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transform:rotate(0deg);transition:transform .25s;content:""}[dir=rtl] .md-typeset summary:after{right:auto;left:.4rem;transform:rotate(180deg)}.md-typeset summary::-webkit-details-marker,.md-typeset summary::marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{display:inline-flex;height:1.125em;vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{width:1.125em;max-height:100%;fill:currentColor}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{margin:0 -.125em;padding:0 .125em;border-radius:.1rem}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em;background-color:var(--md-code-hl-color)}.highlight [data-linenos]:before{position:-webkit-sticky;position:sticky;left:-1.1764705882em;float:left;margin-right:1.1764705882em;margin-left:-1.1764705882em;padding-left:1.1764705882em;color:var(--md-default-fg-color--light);background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;content:attr(data-linenos);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable{display:flow-root;overflow:hidden}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable .linenos{padding:.7720588235em 0 .7720588235em 1.1764705882em;font-size:.85em;background-color:var(--md-code-bg-color);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable .linenodiv{padding-right:.5882352941em;box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;overflow:hidden}.md-typeset .highlighttable{margin:1em 0;direction:ltr;border-radius:.1rem}.md-typeset .highlighttable code{border-radius:0}@media screen and (max-width:44.9375em){.md-typeset>.highlight{margin:1em -.8rem}.md-typeset>.highlight .hll{margin:0 -.8rem;padding:0 .8rem}.md-typeset>.highlight code{border-radius:0}.md-typeset>.highlighttable{margin:1em -.8rem;border-radius:0}.md-typeset>.highlighttable .hll{margin:0 -.8rem;padding:0 .8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{position:relative;margin:0;color:inherit;-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.md-typeset .keys span{padding:0 .2em;color:var(--md-default-fg-color--light)}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{padding-right:.4em;content:"⎇"}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{padding-right:.4em;content:"⌘"}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{padding-right:.4em;content:"⌃"}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{padding-right:.4em;content:"◆"}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{padding-right:.4em;content:"⌥"}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{padding-right:.4em;content:"⇧"}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{padding-right:.4em;content:"❖"}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{padding-right:.4em;content:"⊞"}.md-typeset .keys .key-arrow-down:before{padding-right:.4em;content:"↓"}.md-typeset .keys .key-arrow-left:before{padding-right:.4em;content:"←"}.md-typeset .keys .key-arrow-right:before{padding-right:.4em;content:"→"}.md-typeset .keys .key-arrow-up:before{padding-right:.4em;content:"↑"}.md-typeset .keys .key-backspace:before{padding-right:.4em;content:"⌫"}.md-typeset .keys .key-backtab:before{padding-right:.4em;content:"⇤"}.md-typeset .keys .key-caps-lock:before{padding-right:.4em;content:"⇪"}.md-typeset .keys .key-clear:before{padding-right:.4em;content:"⌧"}.md-typeset .keys .key-context-menu:before{padding-right:.4em;content:"☰"}.md-typeset .keys .key-delete:before{padding-right:.4em;content:"⌦"}.md-typeset .keys .key-eject:before{padding-right:.4em;content:"⏏"}.md-typeset .keys .key-end:before{padding-right:.4em;content:"⤓"}.md-typeset .keys .key-escape:before{padding-right:.4em;content:"⎋"}.md-typeset .keys .key-home:before{padding-right:.4em;content:"⤒"}.md-typeset .keys .key-insert:before{padding-right:.4em;content:"⎀"}.md-typeset .keys .key-page-down:before{padding-right:.4em;content:"⇟"}.md-typeset .keys .key-page-up:before{padding-right:.4em;content:"⇞"}.md-typeset .keys .key-print-screen:before{padding-right:.4em;content:"⎙"}.md-typeset .keys .key-tab:after{padding-left:.4em;content:"⇥"}.md-typeset .keys .key-num-enter:after{padding-left:.4em;content:"⌤"}.md-typeset .keys .key-enter:after{padding-left:.4em;content:"⏎"}.md-typeset .tabbed-content{display:none;order:99;width:100%;box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}@media print{.md-typeset .tabbed-content{display:block;order:0}}.md-typeset .tabbed-content>.highlight:only-child pre,.md-typeset .tabbed-content>.highlighttable:only-child,.md-typeset .tabbed-content>pre:only-child{margin:0}.md-typeset .tabbed-content>.highlight:only-child pre>code,.md-typeset .tabbed-content>.highlighttable:only-child>code,.md-typeset .tabbed-content>pre:only-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-content>.tabbed-set{margin:0}.md-typeset .tabbed-set{position:relative;display:flex;flex-wrap:wrap;margin:1em 0;border-radius:.1rem}.md-typeset .tabbed-set>input{position:absolute;width:0;height:0;opacity:0}.md-typeset .tabbed-set>input:checked+label{color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:checked+label+.tabbed-content{display:block}.md-typeset .tabbed-set>input:focus+label{outline-style:auto}.md-typeset .tabbed-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.md-typeset .tabbed-set>label{z-index:1;width:auto;padding:.9375em 1.25em .78125em;color:var(--md-default-fg-color--light);font-weight:700;font-size:.64rem;border-bottom:.1rem solid transparent;cursor:pointer;transition:color .25s}.md-typeset .tabbed-set>label:hover{color:var(--md-accent-fg-color)}:root{--md-tasklist-icon:url("data:image/svg+xml;charset=utf-8,");--md-tasklist-icon--checked:url("data:image/svg+xml;charset=utf-8,")}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:auto}.md-typeset .task-list-control [type=checkbox]{z-index:-1;opacity:0}.md-typeset .task-list-indicator:before{position:absolute;top:.15em;left:-1.5em;width:1.25em;height:1.25em;background-color:var(--md-default-fg-color--lightest);-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em;left:auto}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media screen and (min-width:45em){.md-typeset .inline{float:left;width:11.7rem;margin-top:0;margin-right:.8rem;margin-bottom:.8rem}.md-typeset .inline.end,[dir=rtl] .md-typeset .inline{float:right;margin-right:0;margin-left:.8rem}[dir=rtl] .md-typeset .inline.end{float:left;margin-right:.8rem;margin-left:0}} -/*# sourceMappingURL=main.6f955dcd.min.css.map */ \ No newline at end of file diff --git a/assets/stylesheets/main.6f955dcd.min.css.map b/assets/stylesheets/main.6f955dcd.min.css.map deleted file mode 100644 index 70f977dc..00000000 --- a/assets/stylesheets/main.6f955dcd.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["src/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/assets/stylesheets/main.scss","src/assets/stylesheets/main/_reset.scss","src/assets/stylesheets/main/_colors.scss","src/assets/stylesheets/main/_icons.scss","src/assets/stylesheets/main/_typeset.scss","src/assets/stylesheets/utilities/_break.scss","node_modules/material-shadows/material-shadows.scss","src/assets/stylesheets/main/layout/_base.scss","src/assets/stylesheets/main/layout/_announce.scss","src/assets/stylesheets/main/layout/_clipboard.scss","src/assets/stylesheets/main/layout/_content.scss","src/assets/stylesheets/main/layout/_dialog.scss","src/assets/stylesheets/main/layout/_form.scss","src/assets/stylesheets/main/layout/_header.scss","src/assets/stylesheets/main/layout/_footer.scss","src/assets/stylesheets/main/layout/_nav.scss","src/assets/stylesheets/main/layout/_search.scss","src/assets/stylesheets/main/layout/_select.scss","src/assets/stylesheets/main/layout/_sidebar.scss","src/assets/stylesheets/main/layout/_source.scss","src/assets/stylesheets/main/layout/_tabs.scss","src/assets/stylesheets/main/layout/_top.scss","src/assets/stylesheets/main/layout/_version.scss","src/assets/stylesheets/main/extensions/markdown/_admonition.scss","node_modules/material-design-color/material-color.scss","src/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/assets/stylesheets/main/extensions/markdown/_toc.scss","src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/assets/stylesheets/main/_modifiers.scss"],"names":[],"mappings":"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","file":"src/assets/stylesheets/main.scss","sourcesContent":["////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Keyboard key\n .keys {\n\n // Keyboard key icon\n kbd::before,\n kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n }\n\n // Surrounding text\n span {\n padding: 0 px2em(3.2px);\n color: var(--md-default-fg-color--light);\n }\n\n // Define keyboard keys with left icon\n @each $name, $code in (\n\n // Modifiers\n \"alt\": \"\\2387\",\n \"left-alt\": \"\\2387\",\n \"right-alt\": \"\\2387\",\n \"command\": \"\\2318\",\n \"left-command\": \"\\2318\",\n \"right-command\": \"\\2318\",\n \"control\": \"\\2303\",\n \"left-control\": \"\\2303\",\n \"right-control\": \"\\2303\",\n \"meta\": \"\\25C6\",\n \"left-meta\": \"\\25C6\",\n \"right-meta\": \"\\25C6\",\n \"option\": \"\\2325\",\n \"left-option\": \"\\2325\",\n \"right-option\": \"\\2325\",\n \"shift\": \"\\21E7\",\n \"left-shift\": \"\\21E7\",\n \"right-shift\": \"\\21E7\",\n \"super\": \"\\2756\",\n \"left-super\": \"\\2756\",\n \"right-super\": \"\\2756\",\n \"windows\": \"\\229E\",\n \"left-windows\": \"\\229E\",\n \"right-windows\": \"\\229E\",\n\n // Other keys\n \"arrow-down\": \"\\2193\",\n \"arrow-left\": \"\\2190\",\n \"arrow-right\": \"\\2192\",\n \"arrow-up\": \"\\2191\",\n \"backspace\": \"\\232B\",\n \"backtab\": \"\\21E4\",\n \"caps-lock\": \"\\21EA\",\n \"clear\": \"\\2327\",\n \"context-menu\": \"\\2630\",\n \"delete\": \"\\2326\",\n \"eject\": \"\\23CF\",\n \"end\": \"\\2913\",\n \"escape\": \"\\238B\",\n \"home\": \"\\2912\",\n \"insert\": \"\\2380\",\n \"page-down\": \"\\21DF\",\n \"page-up\": \"\\21DE\",\n \"print-screen\": \"\\2399\"\n ) {\n .key-#{$name} {\n &::before {\n padding-right: px2em(6.4px);\n content: $code;\n }\n }\n }\n\n // Define keyboard keys with right icon\n @each $name, $code in (\n \"tab\": \"\\21E5\",\n \"num-enter\": \"\\2324\",\n \"enter\": \"\\23CE\"\n ) {\n .key-#{$name} {\n &::after {\n padding-left: px2em(6.4px);\n content: $code;\n }\n }\n }\n }\n}\n","@charset \"UTF-8\";\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\nbody {\n margin: 0;\n}\n\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\na {\n color: inherit;\n text-decoration: none;\n}\n\nhr {\n display: block;\n box-sizing: content-box;\n height: 0.05rem;\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n line-height: 1em;\n}\n\nimg {\n border-style: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\ninput {\n border: 0;\n outline: none;\n}\n\n:root {\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n --md-primary-fg-color: hsla(231, 48%, 48%, 1);\n --md-primary-fg-color--light: hsla(231, 44%, 56%, 1);\n --md-primary-fg-color--dark: hsla(232, 54%, 41%, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-accent-fg-color: hsla(231, 99%, 66%, 1);\n --md-accent-fg-color--transparent: hsla(231, 99%, 66%, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n}\n:root > * {\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n --md-code-hl-color: hsla(60, 100%, 50%, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n --md-typeset-color: var(--md-default-fg-color);\n --md-typeset-a-color: var(--md-primary-fg-color);\n --md-typeset-mark-color: hsla(60, 100%, 50%, 0.5);\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n}\n\n.md-icon svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family: var(--md-text-font-family, _), -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family: var(--md-code-font-family, _), SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n:root {\n --md-typeset-table--ascending: svg-load(\"material/arrow-down.svg\");\n --md-typeset-table--descending: svg-load(\"material/arrow-up.svg\");\n}\n\n.md-typeset {\n font-size: 0.8rem;\n line-height: 1.6;\n color-adjust: exact;\n}\n@media print {\n .md-typeset {\n font-size: 0.68rem;\n }\n}\n.md-typeset ul,\n.md-typeset ol,\n.md-typeset dl,\n.md-typeset figure,\n.md-typeset blockquote,\n.md-typeset pre {\n margin: 1em 0;\n}\n.md-typeset h1 {\n margin: 0 0 1.25em;\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: 2em;\n line-height: 1.3;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 {\n margin: 1.6em 0 0.64em;\n font-weight: 300;\n font-size: 1.5625em;\n line-height: 1.4;\n letter-spacing: -0.01em;\n}\n.md-typeset h3 {\n margin: 1.6em 0 0.8em;\n font-weight: 400;\n font-size: 1.25em;\n line-height: 1.5;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 + h3 {\n margin-top: 0.8em;\n}\n.md-typeset h4 {\n margin: 1em 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n}\n.md-typeset h5,\n.md-typeset h6 {\n margin: 1.25em 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.8em;\n letter-spacing: -0.01em;\n}\n.md-typeset h5 {\n text-transform: uppercase;\n}\n.md-typeset hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: 0.05rem solid var(--md-default-fg-color--lightest);\n}\n.md-typeset a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n}\n.md-typeset a, .md-typeset a::before {\n transition: color 125ms;\n}\n.md-typeset a:focus, .md-typeset a:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n}\n@media print {\n .md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n white-space: pre-wrap;\n }\n}\n.md-typeset code {\n padding: 0 0.2941176471em;\n font-size: 0.85em;\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: 0.1rem;\n box-decoration-break: clone;\n}\n.md-typeset code:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset h1 code,\n.md-typeset h2 code,\n.md-typeset h3 code,\n.md-typeset h4 code,\n.md-typeset h5 code,\n.md-typeset h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n}\n.md-typeset a code {\n color: currentColor;\n}\n.md-typeset pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n}\n.md-typeset pre > code {\n display: block;\n margin: 0;\n padding: 0.7720588235em 1.1764705882em;\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-typeset pre > code:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-typeset pre > code::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > pre {\n margin: 1em -0.8rem;\n }\n .md-typeset > pre code {\n border-radius: 0;\n }\n}\n.md-typeset kbd {\n display: inline-block;\n padding: 0 0.6666666667em;\n color: var(--md-default-fg-color);\n font-size: 0.75em;\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.1rem 0 0.05rem var(--md-typeset-kbd-border-color), 0 0.1rem 0 var(--md-typeset-kbd-border-color), 0 -0.1rem 0.2rem var(--md-typeset-kbd-accent-color) inset;\n}\n.md-typeset mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n}\n.md-typeset abbr {\n text-decoration: none;\n border-bottom: 0.05rem dotted var(--md-default-fg-color--light);\n cursor: help;\n}\n@media (hover: none) {\n .md-typeset abbr {\n position: relative;\n }\n .md-typeset abbr[title]:focus::after, .md-typeset abbr[title]:hover::after {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: 0.2rem 0.3rem;\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n content: attr(title);\n }\n}\n.md-typeset small {\n opacity: 0.75;\n}\n.md-typeset sup,\n.md-typeset sub {\n margin-left: 0.078125em;\n}\n[dir=rtl] .md-typeset sup,\n[dir=rtl] .md-typeset sub {\n margin-right: 0.078125em;\n margin-left: initial;\n}\n.md-typeset blockquote {\n display: flow-root;\n padding-left: 0.6rem;\n color: var(--md-default-fg-color--light);\n border-left: 0.2rem solid var(--md-default-fg-color--lighter);\n}\n[dir=rtl] .md-typeset blockquote {\n padding-right: 0.6rem;\n padding-left: initial;\n border-right: 0.2rem solid var(--md-default-fg-color--lighter);\n border-left: initial;\n}\n.md-typeset ul {\n list-style-type: disc;\n}\n.md-typeset ul,\n.md-typeset ol {\n display: flow-root;\n margin-left: 0.625em;\n padding: 0;\n}\n[dir=rtl] .md-typeset ul,\n[dir=rtl] .md-typeset ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset ul ol,\n.md-typeset ol ol {\n list-style-type: lower-alpha;\n}\n.md-typeset ul ol ol,\n.md-typeset ol ol ol {\n list-style-type: lower-roman;\n}\n.md-typeset ul li,\n.md-typeset ol li {\n margin-bottom: 0.5em;\n margin-left: 1.25em;\n}\n[dir=rtl] .md-typeset ul li,\n[dir=rtl] .md-typeset ol li {\n margin-right: 1.25em;\n margin-left: initial;\n}\n.md-typeset ul li p,\n.md-typeset ul li blockquote,\n.md-typeset ol li p,\n.md-typeset ol li blockquote {\n margin: 0.5em 0;\n}\n.md-typeset ul li:last-child,\n.md-typeset ol li:last-child {\n margin-bottom: 0;\n}\n.md-typeset ul li ul,\n.md-typeset ul li ol,\n.md-typeset ol li ul,\n.md-typeset ol li ol {\n margin: 0.5em 0 0.5em 0.625em;\n}\n[dir=rtl] .md-typeset ul li ul,\n[dir=rtl] .md-typeset ul li ol,\n[dir=rtl] .md-typeset ol li ul,\n[dir=rtl] .md-typeset ol li ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset dd {\n margin: 1em 0 1.5em 1.875em;\n}\n[dir=rtl] .md-typeset dd {\n margin-right: 1.875em;\n margin-left: initial;\n}\n.md-typeset img,\n.md-typeset svg {\n max-width: 100%;\n height: auto;\n}\n.md-typeset img[align=left],\n.md-typeset svg[align=left] {\n margin: 1em;\n margin-left: 0;\n}\n.md-typeset img[align=right],\n.md-typeset svg[align=right] {\n margin: 1em;\n margin-right: 0;\n}\n.md-typeset img[align]:only-child,\n.md-typeset svg[align]:only-child {\n margin-top: 0;\n}\n.md-typeset figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n}\n.md-typeset figure img {\n display: block;\n}\n.md-typeset figcaption {\n max-width: 24rem;\n margin: 1em auto 2em;\n font-style: italic;\n}\n.md-typeset iframe {\n max-width: 100%;\n}\n.md-typeset table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: 0.64rem;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.05rem rgba(0, 0, 0, 0.1);\n touch-action: auto;\n}\n@media print {\n .md-typeset table:not([class]) {\n display: table;\n }\n}\n.md-typeset table:not([class]) + * {\n margin-top: 1.5em;\n}\n.md-typeset table:not([class]) th > *:first-child,\n.md-typeset table:not([class]) td > *:first-child {\n margin-top: 0;\n}\n.md-typeset table:not([class]) th > *:last-child,\n.md-typeset table:not([class]) td > *:last-child {\n margin-bottom: 0;\n}\n.md-typeset table:not([class]) th:not([align]),\n.md-typeset table:not([class]) td:not([align]) {\n text-align: left;\n}\n[dir=rtl] .md-typeset table:not([class]) th:not([align]),\n[dir=rtl] .md-typeset table:not([class]) td:not([align]) {\n text-align: right;\n}\n.md-typeset table:not([class]) th {\n min-width: 5rem;\n padding: 0.9375em 1.25em;\n color: var(--md-default-bg-color);\n vertical-align: top;\n background-color: var(--md-default-fg-color--light);\n}\n.md-typeset table:not([class]) th a {\n color: inherit;\n}\n.md-typeset table:not([class]) td {\n padding: 0.9375em 1.25em;\n vertical-align: top;\n border-top: 0.05rem solid var(--md-default-fg-color--lightest);\n}\n.md-typeset table:not([class]) tr {\n transition: background-color 125ms;\n}\n.md-typeset table:not([class]) tr:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 0.05rem 0 var(--md-default-bg-color) inset;\n}\n.md-typeset table:not([class]) tr:first-child td {\n border-top: 0;\n}\n.md-typeset table:not([class]) a {\n word-break: normal;\n}\n.md-typeset table th[role=columnheader] {\n cursor: pointer;\n}\n.md-typeset table th[role=columnheader]::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: sub;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-typeset table th[role=columnheader][aria-sort=ascending]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--ascending);\n}\n.md-typeset table th[role=columnheader][aria-sort=descending]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--descending);\n}\n.md-typeset__scrollwrap {\n margin: 1em -0.8rem;\n overflow-x: auto;\n touch-action: auto;\n}\n.md-typeset__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 0.8rem;\n}\n@media print {\n .md-typeset__table {\n display: block;\n }\n}\nhtml .md-typeset__table table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n}\n\nhtml {\n height: 100%;\n overflow-x: hidden;\n font-size: 125%;\n}\n@media screen and (min-width: 100em) {\n html {\n font-size: 137.5%;\n }\n}\n@media screen and (min-width: 125em) {\n html {\n font-size: 150%;\n }\n}\n\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n font-size: 0.5rem;\n background-color: var(--md-default-bg-color);\n}\n@media print {\n body {\n display: block;\n }\n}\n@media screen and (max-width: 59.9375em) {\n body[data-md-state=lock] {\n position: fixed;\n }\n}\n\n.md-grid {\n max-width: 61rem;\n margin-right: auto;\n margin-left: auto;\n}\n\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n}\n@media print {\n .md-container {\n display: block;\n }\n}\n\n.md-main {\n flex-grow: 1;\n}\n.md-main__inner {\n display: flex;\n height: 100%;\n margin-top: 1.5rem;\n}\n\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.md-toggle {\n display: none;\n}\n\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-option:checked + label:not([hidden]) {\n display: block;\n}\n.md-option.focus-visible + label {\n outline-style: auto;\n}\n\n.md-skip {\n position: fixed;\n z-index: -1;\n margin: 0.5rem;\n padding: 0.3rem 0.5rem;\n color: var(--md-default-bg-color);\n font-size: 0.64rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n transform: translateY(0.4rem);\n opacity: 0;\n}\n.md-skip:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 175ms 75ms;\n}\n\n@page {\n margin: 25mm;\n}\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-announce {\n display: none;\n }\n}\n.md-announce__inner {\n margin: 0.6rem auto;\n padding: 0 0.8rem;\n color: var(--md-footer-fg-color);\n font-size: 0.7rem;\n}\n\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n.md-clipboard {\n position: absolute;\n top: 0.5em;\n right: 0.5em;\n z-index: 1;\n width: 1.5em;\n height: 1.5em;\n color: var(--md-default-fg-color--lightest);\n border-radius: 0.1rem;\n cursor: pointer;\n transition: color 250ms;\n}\n@media print {\n .md-clipboard {\n display: none;\n }\n}\n.md-clipboard:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n:hover > .md-clipboard {\n color: var(--md-default-fg-color--light);\n}\n.md-clipboard:focus, .md-clipboard:hover {\n color: var(--md-accent-fg-color);\n}\n.md-clipboard::after {\n display: block;\n width: 1.125em;\n height: 1.125em;\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-clipboard--inline {\n cursor: pointer;\n}\n.md-clipboard--inline code {\n transition: color 250ms, background-color 250ms;\n}\n.md-clipboard--inline:focus code, .md-clipboard--inline:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n\n.md-content {\n flex-grow: 1;\n overflow: hidden;\n scroll-padding-top: 51.2rem;\n}\n.md-content__inner {\n margin: 0 0.8rem 1.2rem;\n padding-top: 0.6rem;\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-left: 1.2rem;\n }\n [dir=rtl] .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n margin-left: 0.8rem;\n }\n .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n }\n [dir=rtl] .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 0.8rem;\n margin-left: 1.2rem;\n }\n}\n.md-content__inner::before {\n display: block;\n height: 0.4rem;\n content: \"\";\n}\n.md-content__inner > :last-child {\n margin-bottom: 0;\n}\n.md-content__button {\n float: right;\n margin: 0.4rem 0;\n margin-left: 0.4rem;\n padding: 0;\n}\n@media print {\n .md-content__button {\n display: none;\n }\n}\n[dir=rtl] .md-content__button {\n float: left;\n margin-right: 0.4rem;\n margin-left: initial;\n}\n[dir=rtl] .md-content__button svg {\n transform: scaleX(-1);\n}\n.md-typeset .md-content__button {\n color: var(--md-default-fg-color--lighter);\n}\n.md-content__button svg {\n display: inline;\n vertical-align: top;\n}\n\n.md-dialog {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: fixed;\n right: 0.8rem;\n bottom: 0.8rem;\n left: initial;\n z-index: 2;\n min-width: 11.1rem;\n padding: 0.4rem 0.6rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n transform: translateY(100%);\n opacity: 0;\n transition: transform 0ms 400ms, opacity 400ms;\n pointer-events: none;\n}\n@media print {\n .md-dialog {\n display: none;\n }\n}\n[dir=rtl] .md-dialog {\n right: initial;\n left: 0.8rem;\n}\n.md-dialog[data-md-state=open] {\n transform: translateY(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1), opacity 400ms;\n pointer-events: initial;\n}\n.md-dialog__inner {\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n}\n\n.md-typeset .md-button {\n display: inline-block;\n padding: 0.625em 2em;\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: 0.1rem solid currentColor;\n border-radius: 0.1rem;\n transition: color 125ms, background-color 125ms, border-color 125ms;\n}\n.md-typeset .md-button--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n}\n.md-typeset .md-button:focus, .md-typeset .md-button:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .md-input {\n height: 1.8rem;\n padding: 0 0.6rem;\n font-size: 0.8rem;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.1);\n transition: box-shadow 250ms;\n}\n.md-typeset .md-input:focus, .md-typeset .md-input:hover {\n box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.15), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.15);\n}\n.md-typeset .md-input--stretch {\n width: 100%;\n}\n\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 2;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0), 0 0.2rem 0.4rem rgba(0, 0, 0, 0);\n}\n@media print {\n .md-header {\n display: none;\n }\n}\n.md-header[data-md-state=shadow] {\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2);\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), box-shadow 250ms;\n}\n.md-header[data-md-state=hidden] {\n transform: translateY(-100%);\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), box-shadow 250ms;\n}\n.md-header .focus-visible {\n outline-color: currentColor;\n}\n.md-header__inner {\n display: flex;\n align-items: center;\n padding: 0 0.2rem;\n}\n.md-header__button {\n position: relative;\n z-index: 1;\n margin: 0.2rem;\n padding: 0.4rem;\n color: currentColor;\n vertical-align: middle;\n cursor: pointer;\n transition: opacity 250ms;\n}\n.md-header__button:hover {\n opacity: 0.7;\n}\n.md-header__button:not([hidden]) {\n display: inline-block;\n}\n.md-header__button:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-header__button.md-logo {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n@media screen and (max-width: 76.1875em) {\n .md-header__button.md-logo {\n display: none;\n }\n}\n.md-header__button.md-logo img,\n.md-header__button.md-logo svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n@media screen and (min-width: 60em) {\n .md-header__button[for=__search] {\n display: none;\n }\n}\n.no-js .md-header__button[for=__search] {\n display: none;\n}\n[dir=rtl] .md-header__button[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (min-width: 76.25em) {\n .md-header__button[for=__drawer] {\n display: none;\n }\n}\n.md-header__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n}\n.md-header__topic + .md-header__topic {\n z-index: -1;\n transform: translateX(1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__topic + .md-header__topic {\n transform: translateX(-1.25rem);\n}\n.md-header__title {\n flex-grow: 1;\n height: 2.4rem;\n margin-right: 0.4rem;\n margin-left: 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-header__title[data-md-state=active] .md-header__topic {\n z-index: -1;\n transform: translateX(-1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic {\n transform: translateX(1.25rem);\n}\n.md-header__title[data-md-state=active] .md-header__topic + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n pointer-events: initial;\n}\n.md-header__title > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n}\n.md-header__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition: max-width 0ms 250ms, opacity 250ms 250ms;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-header__option {\n max-width: 0;\n opacity: 0;\n transition: max-width 0ms, opacity 0ms;\n}\n.md-header__source {\n display: none;\n}\n@media screen and (min-width: 60em) {\n .md-header__source {\n display: block;\n width: 11.7rem;\n max-width: 11.7rem;\n margin-left: 1rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1rem;\n margin-left: initial;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-header__source {\n margin-left: 1.4rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1.4rem;\n }\n}\n\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-footer {\n display: none;\n }\n}\n.md-footer__inner {\n padding: 0.2rem;\n overflow: auto;\n}\n.md-footer__link {\n display: flex;\n padding-top: 1.4rem;\n padding-bottom: 0.4rem;\n transition: opacity 250ms;\n}\n@media screen and (min-width: 45em) {\n .md-footer__link {\n width: 50%;\n }\n}\n.md-footer__link:focus, .md-footer__link:hover {\n opacity: 0.7;\n}\n.md-footer__link--prev {\n float: left;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--prev {\n width: 25%;\n }\n .md-footer__link--prev .md-footer__title {\n display: none;\n }\n}\n[dir=rtl] .md-footer__link--prev {\n float: right;\n}\n[dir=rtl] .md-footer__link--prev svg {\n transform: scaleX(-1);\n}\n.md-footer__link--next {\n float: right;\n text-align: right;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--next {\n width: 75%;\n }\n}\n[dir=rtl] .md-footer__link--next {\n float: left;\n text-align: left;\n}\n[dir=rtl] .md-footer__link--next svg {\n transform: scaleX(-1);\n}\n.md-footer__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - 2.4rem);\n padding: 0 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-footer__button {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n.md-footer__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: -1rem;\n padding: 0 1rem;\n font-size: 0.64rem;\n opacity: 0.7;\n}\n\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n}\n.md-footer-meta__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: 0.2rem;\n}\nhtml .md-footer-meta.md-typeset a {\n color: var(--md-footer-fg-color--light);\n}\nhtml .md-footer-meta.md-typeset a:focus, html .md-footer-meta.md-typeset a:hover {\n color: var(--md-footer-fg-color);\n}\n\n.md-footer-copyright {\n width: 100%;\n margin: auto 0.6rem;\n padding: 0.4rem 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: 0.64rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-copyright {\n width: auto;\n }\n}\n.md-footer-copyright__highlight {\n color: var(--md-footer-fg-color--light);\n}\n\n.md-footer-social {\n margin: 0 0.4rem;\n padding: 0.2rem 0 0.6rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-social {\n padding: 0.6rem 0;\n }\n}\n.md-footer-social__link {\n display: inline-block;\n width: 1.6rem;\n height: 1.6rem;\n text-align: center;\n}\n.md-footer-social__link::before {\n line-height: 1.9;\n}\n.md-footer-social__link svg {\n max-height: 0.8rem;\n vertical-align: -25%;\n fill: currentColor;\n}\n\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n.md-nav {\n font-size: 0.7rem;\n line-height: 1.3;\n}\n.md-nav__title {\n display: block;\n padding: 0 0.6rem;\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n}\n.md-nav__title .md-nav__button {\n display: none;\n}\n.md-nav__title .md-nav__button img {\n width: auto;\n height: 100%;\n}\n.md-nav__title .md-nav__button.md-logo img,\n.md-nav__title .md-nav__button.md-logo svg {\n display: block;\n width: 2.4rem;\n height: 2.4rem;\n fill: currentColor;\n}\n.md-nav__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-nav__item {\n padding: 0 0.6rem;\n}\n.md-nav__item .md-nav__item {\n padding-right: 0;\n}\n[dir=rtl] .md-nav__item .md-nav__item {\n padding-right: 0.6rem;\n padding-left: 0;\n}\n.md-nav__link {\n display: block;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n}\n.md-nav__link[data-md-state=blur] {\n color: var(--md-default-fg-color--light);\n}\n.md-nav__item .md-nav__link--active {\n color: var(--md-typeset-a-color);\n}\n.md-nav__item--nested > .md-nav__link {\n color: inherit;\n}\n.md-nav__link:focus, .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-nav--primary .md-nav__link[for=__toc] {\n display: none;\n}\n.md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n}\n.md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: none;\n}\n.md-nav__source {\n display: none;\n}\n@media screen and (max-width: 76.1875em) {\n .md-nav--primary, .md-nav--primary .md-nav {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n .md-nav--primary .md-nav__title,\n.md-nav--primary .md-nav__item {\n font-size: 0.8rem;\n line-height: 1.5;\n }\n .md-nav--primary .md-nav__title {\n position: relative;\n height: 5.6rem;\n padding: 3rem 0.8rem 0.2rem;\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: 2.4rem;\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n position: absolute;\n top: 0.4rem;\n left: 0.4rem;\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon {\n right: 0.4rem;\n left: initial;\n }\n .md-nav--primary .md-nav__title .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0.05rem 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child {\n border-top: 0;\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n .md-nav--primary .md-nav__title .md-logo {\n position: absolute;\n top: 0.2rem;\n left: 0.2rem;\n display: block;\n margin: 0.2rem;\n padding: 0.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-logo {\n right: 0.2rem;\n left: initial;\n }\n .md-nav--primary .md-nav__list {\n flex: 1;\n }\n .md-nav--primary .md-nav__item {\n padding: 0;\n border-top: 0.05rem solid var(--md-default-fg-color--lightest);\n }\n .md-nav--primary .md-nav__item--nested > .md-nav__link {\n padding-right: 2.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__item--nested > .md-nav__link {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link:focus, .md-nav--primary .md-nav__item--active > .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n }\n .md-nav--primary .md-nav__link {\n position: relative;\n margin-top: 0;\n padding: 0.6rem 0.8rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon {\n position: absolute;\n top: 50%;\n right: 0.6rem;\n width: 1.2rem;\n height: 1.2rem;\n margin-top: -0.6rem;\n color: inherit;\n font-size: 1.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon {\n right: initial;\n left: 0.6rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n [dir=rtl] .md-nav--primary .md-nav__icon::after {\n transform: scale(-1);\n }\n .md-nav--primary .md-nav--secondary .md-nav__link {\n position: static;\n }\n .md-nav--primary .md-nav--secondary .md-nav {\n position: static;\n background-color: transparent;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-left: 1.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-right: 1.4rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-left: 2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-right: 2rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 2.6rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 2.6rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 3.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 3.2rem;\n padding-left: initial;\n }\n .md-nav--secondary {\n background-color: transparent;\n }\n .md-nav__toggle ~ .md-nav {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), opacity 125ms 50ms;\n }\n [dir=rtl] .md-nav__toggle ~ .md-nav {\n transform: translateX(-100%);\n }\n .md-nav__toggle:checked ~ .md-nav {\n transform: translateX(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 125ms 125ms;\n }\n .md-nav__toggle:checked ~ .md-nav > .md-nav__list {\n backface-visibility: hidden;\n }\n}\n@media screen and (max-width: 59.9375em) {\n .md-nav--primary .md-nav__link[for=__toc] {\n display: block;\n padding-right: 2.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__link[for=__toc] {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--primary .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n .md-nav__source {\n display: block;\n padding: 0 0.2rem;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-nav--integrated .md-nav__link[for=__toc] {\n display: block;\n padding-right: 2.4rem;\n scroll-snap-align: initial;\n }\n [dir=rtl] .md-nav--integrated .md-nav__link[for=__toc] {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--integrated .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--integrated .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n}\n@media screen and (min-width: 60em) {\n .md-nav--secondary .md-nav__title[for=__toc] {\n scroll-snap-align: start;\n }\n .md-nav--secondary .md-nav__title .md-nav__icon {\n display: none;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-nav {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n scroll-snap-align: start;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n display: none;\n }\n .md-nav__toggle ~ .md-nav {\n display: none;\n }\n .md-nav__toggle:checked ~ .md-nav, .md-nav__toggle:indeterminate ~ .md-nav {\n display: block;\n }\n .md-nav__item--nested > .md-nav > .md-nav__title {\n display: none;\n }\n .md-nav__item--section {\n display: block;\n margin: 1.25em 0;\n }\n .md-nav__item--section:last-child {\n margin-bottom: 0;\n }\n .md-nav__item--section > .md-nav__link {\n display: none;\n }\n .md-nav__item--section > .md-nav {\n display: block;\n }\n .md-nav__item--section > .md-nav > .md-nav__title {\n display: block;\n padding: 0;\n pointer-events: none;\n scroll-snap-align: start;\n }\n .md-nav__item--section > .md-nav > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n .md-nav__icon {\n float: right;\n width: 0.9rem;\n height: 0.9rem;\n transition: transform 250ms;\n }\n [dir=rtl] .md-nav__icon {\n float: left;\n transform: rotate(180deg);\n }\n .md-nav__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: -0.1rem;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link .md-nav__icon, .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link .md-nav__icon {\n transform: rotate(90deg);\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--nested,\n.md-nav--lifted > .md-nav__title {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active {\n display: block;\n padding: 0;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav > .md-nav__title {\n display: block;\n padding: 0 0.6rem;\n pointer-events: none;\n scroll-snap-align: start;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item > .md-nav__item {\n padding-right: 0.6rem;\n }\n .md-nav--lifted .md-nav[data-md-level=\"1\"] {\n display: block;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: 0.05rem solid var(--md-primary-fg-color);\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav > .md-nav__title {\n display: none;\n }\n}\n\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n.md-search {\n position: relative;\n}\n@media screen and (min-width: 60em) {\n .md-search {\n padding: 0.2rem 0;\n }\n}\n.no-js .md-search {\n display: none;\n}\n.md-search__overlay {\n z-index: 1;\n opacity: 0;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__overlay {\n position: absolute;\n top: 0.2rem;\n left: -2.2rem;\n width: 2rem;\n height: 2rem;\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: 1rem;\n transform-origin: center;\n transition: transform 300ms 100ms, opacity 200ms 200ms;\n pointer-events: none;\n }\n [dir=rtl] .md-search__overlay {\n right: -2.2rem;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n opacity: 1;\n transition: transform 400ms, opacity 100ms;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n cursor: pointer;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [dir=rtl] .md-search__overlay {\n right: 0;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n width: 100%;\n height: 200vh;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@media screen and (max-width: 29.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(45);\n }\n}\n@media screen and (min-width: 30em) and (max-width: 44.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(60);\n }\n}\n@media screen and (min-width: 45em) and (max-width: 59.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(75);\n }\n}\n.md-search__inner {\n backface-visibility: hidden;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__inner {\n position: fixed;\n top: 0;\n left: 100%;\n z-index: 2;\n width: 100%;\n height: 100%;\n transform: translateX(5%);\n opacity: 0;\n transition: right 0ms 300ms, left 0ms 300ms, transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 150ms 150ms;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n left: 0;\n transform: translateX(0);\n opacity: 1;\n transition: right 0ms 0ms, left 0ms 0ms, transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms 150ms;\n }\n [dir=rtl] [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n right: 0;\n left: initial;\n }\n html [dir=rtl] .md-search__inner {\n right: 100%;\n left: initial;\n transform: translateX(-5%);\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__inner {\n position: relative;\n float: right;\n width: 11.7rem;\n padding: 0.1rem 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n }\n [dir=rtl] .md-search__inner {\n float: left;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 34.4rem;\n }\n}\n.md-search__form {\n position: relative;\n}\n@media screen and (min-width: 60em) {\n .md-search__form {\n border-radius: 0.1rem;\n }\n}\n.md-search__input {\n position: relative;\n z-index: 2;\n padding: 0 2.2rem 0 3.6rem;\n text-overflow: ellipsis;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 0.6rem transparent;\n transition: color 250ms, background-color 250ms, box-shadow 250ms;\n}\n[dir=rtl] .md-search__input {\n padding: 0 3.6rem 0 2.2rem;\n}\n.md-search__input::placeholder {\n transition: color 250ms;\n}\n.md-search__input ~ .md-search__icon, .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n}\n.md-search__input::-ms-clear {\n display: none;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input {\n box-shadow: 0 0 0.6rem rgba(0, 0, 0, 0.07);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__input {\n width: 100%;\n height: 2.4rem;\n font-size: 0.9rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__input {\n width: 100%;\n height: 1.8rem;\n padding-left: 2.2rem;\n color: inherit;\n font-size: 0.8rem;\n background-color: rgba(0, 0, 0, 0.26);\n border-radius: 0.1rem;\n }\n [dir=rtl] .md-search__input {\n padding-right: 2.2rem;\n }\n .md-search__input + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n .md-search__input::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n .md-search__input:hover {\n background-color: rgba(255, 255, 255, 0.12);\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input {\n color: var(--md-default-fg-color);\n text-overflow: clip;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem 0.1rem 0 0;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle=search]:checked ~ .md-header .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n }\n}\n.md-search__icon {\n position: absolute;\n z-index: 2;\n width: 1.2rem;\n height: 1.2rem;\n cursor: pointer;\n transition: color 250ms, opacity 250ms;\n}\n.md-search__icon:hover {\n opacity: 0.7;\n}\n.md-search__icon[for=__search] {\n top: 0.3rem;\n left: 0.5rem;\n}\n[dir=rtl] .md-search__icon[for=__search] {\n right: 0.5rem;\n left: initial;\n}\n[dir=rtl] .md-search__icon[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__icon[for=__search] {\n top: 0.6rem;\n left: 0.8rem;\n }\n [dir=rtl] .md-search__icon[for=__search] {\n right: 0.8rem;\n left: initial;\n }\n .md-search__icon[for=__search] svg:first-child {\n display: none;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__icon[for=__search] {\n pointer-events: none;\n }\n .md-search__icon[for=__search] svg:last-child {\n display: none;\n }\n}\n.md-search__icon[type=reset] {\n top: 0.3rem;\n right: 0.5rem;\n transform: scale(0.75);\n opacity: 0;\n transition: transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-search__icon[type=reset] {\n right: initial;\n left: 0.5rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__icon[type=reset] {\n top: 0.6rem;\n right: 0.8rem;\n }\n [dir=rtl] .md-search__icon[type=reset] {\n right: initial;\n left: 0.8rem;\n }\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__icon[type=reset] {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__icon[type=reset]:hover {\n opacity: 0.7;\n}\n.md-search__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 0.1rem 0.1rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__output {\n top: 2.4rem;\n bottom: 0;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__output {\n top: 1.9rem;\n opacity: 0;\n transition: opacity 400ms;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__output {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.4);\n opacity: 1;\n }\n}\n.md-search__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n backface-visibility: hidden;\n touch-action: pan-y;\n}\n@media (max-resolution: 1dppx) {\n .md-search__scrollwrap {\n transform: translateZ(0);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search__scrollwrap {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-search__scrollwrap {\n width: 34.4rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__scrollwrap {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__scrollwrap {\n max-height: 75vh;\n }\n .md-search__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n .md-search__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n }\n}\n\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n}\n.md-search-result__meta {\n padding: 0 0.8rem;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.8rem;\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__meta {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__meta {\n padding-right: 2.2rem;\n padding-left: initial;\n }\n}\n.md-search-result__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-search-result__item {\n box-shadow: 0 -0.05rem 0 var(--md-default-fg-color--lightest);\n}\n.md-search-result__item:first-child {\n box-shadow: none;\n}\n.md-search-result__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n}\n.md-search-result__link:focus, .md-search-result__link:hover {\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__link:last-child p:last-child {\n margin-bottom: 0.6rem;\n}\n.md-search-result__more summary {\n display: block;\n padding: 0.75em 0.8rem;\n color: var(--md-typeset-a-color);\n font-size: 0.64rem;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__more summary {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__more summary {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__more summary:focus, .md-search-result__more summary:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__more summary::marker, .md-search-result__more summary::-webkit-details-marker {\n display: none;\n}\n.md-search-result__more summary ~ * > * {\n opacity: 0.65;\n}\n.md-search-result__article {\n position: relative;\n padding: 0 0.8rem;\n overflow: hidden;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__article {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__article {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__article--document .md-search-result__title {\n margin: 0.55rem 0;\n font-weight: 400;\n font-size: 0.8rem;\n line-height: 1.4;\n}\n.md-search-result__icon {\n position: absolute;\n left: 0;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.5rem;\n color: var(--md-default-fg-color--light);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search-result__icon {\n display: none;\n }\n}\n.md-search-result__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-search-result__icon {\n right: 0;\n left: initial;\n}\n[dir=rtl] .md-search-result__icon::after {\n transform: scaleX(-1);\n}\n.md-search-result__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: 0.64rem;\n line-height: 1.6;\n}\n.md-search-result__teaser {\n display: -webkit-box;\n max-height: 2rem;\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n}\n@media screen and (max-width: 44.9375em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n.md-search-result__teaser mark {\n text-decoration: underline;\n background-color: transparent;\n}\n.md-search-result__terms {\n margin: 0.5em 0;\n font-size: 0.64rem;\n font-style: italic;\n}\n.md-search-result mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n}\n\n.md-select {\n position: relative;\n z-index: 1;\n}\n.md-select__inner {\n position: absolute;\n top: calc(100% - 0.2rem);\n left: 50%;\n max-height: 0;\n margin-top: 0.2rem;\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n transform: translate3d(-50%, 0.3rem, 0);\n opacity: 0;\n transition: transform 250ms 375ms, opacity 250ms 250ms, max-height 0ms 500ms;\n}\n.md-select:focus-within .md-select__inner, .md-select:hover .md-select__inner {\n max-height: 10rem;\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms, max-height 0ms;\n}\n.md-select__inner::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: -0.2rem;\n margin-left: -0.2rem;\n border: 0.2rem solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n}\n.md-select__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: 0.8rem;\n list-style-type: none;\n border-radius: 0.1rem;\n}\n.md-select__item {\n line-height: 1.8rem;\n}\n.md-select__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n outline: none;\n cursor: pointer;\n transition: background-color 250ms, color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-select__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-select__link:focus, .md-select__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-select__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n.md-sidebar {\n position: sticky;\n top: 2.4rem;\n flex-shrink: 0;\n align-self: flex-start;\n width: 12.1rem;\n padding: 1.2rem 0;\n}\n@media print {\n .md-sidebar {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-sidebar--primary {\n position: fixed;\n top: 0;\n left: -12.1rem;\n z-index: 3;\n display: block;\n width: 12.1rem;\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), box-shadow 250ms;\n }\n [dir=rtl] .md-sidebar--primary {\n right: -12.1rem;\n left: initial;\n }\n [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.4);\n transform: translateX(12.1rem);\n }\n [dir=rtl] [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n transform: translateX(-12.1rem);\n }\n .md-sidebar--primary .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar {\n height: 0;\n }\n .no-js .md-sidebar {\n height: auto;\n }\n}\n.md-sidebar--secondary {\n display: none;\n order: 2;\n}\n@media screen and (min-width: 60em) {\n .md-sidebar--secondary {\n height: 0;\n }\n .no-js .md-sidebar--secondary {\n height: auto;\n }\n .md-sidebar--secondary:not([hidden]) {\n display: block;\n }\n .md-sidebar--secondary .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n}\n.md-sidebar__scrollwrap {\n margin: 0 0.2rem;\n overflow-y: auto;\n backface-visibility: hidden;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-sidebar__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n\n@media screen and (max-width: 76.1875em) {\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 3;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n opacity: 0;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [data-md-toggle=drawer]:checked ~ .md-overlay {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@keyframes md-source__facts--done {\n 0% {\n height: 0;\n }\n 100% {\n height: 0.65rem;\n }\n}\n@keyframes md-source__fact--done {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n.md-source {\n display: block;\n font-size: 0.65rem;\n line-height: 1.2;\n white-space: nowrap;\n backface-visibility: hidden;\n transition: opacity 250ms;\n}\n.md-source:hover {\n opacity: 0.7;\n}\n.md-source__icon {\n display: inline-block;\n width: 2rem;\n height: 2.4rem;\n vertical-align: middle;\n}\n.md-source__icon svg {\n margin-top: 0.6rem;\n margin-left: 0.6rem;\n}\n[dir=rtl] .md-source__icon svg {\n margin-right: 0.6rem;\n margin-left: initial;\n}\n.md-source__icon + .md-source__repository {\n margin-left: -2rem;\n padding-left: 2rem;\n}\n[dir=rtl] .md-source__icon + .md-source__repository {\n margin-right: -2rem;\n margin-left: initial;\n padding-right: 2rem;\n padding-left: initial;\n}\n.md-source__repository {\n display: inline-block;\n max-width: calc(100% - 1.2rem);\n margin-left: 0.6rem;\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n}\n.md-source__facts {\n margin: 0.1rem 0 0;\n padding: 0;\n overflow: hidden;\n font-size: 0.55rem;\n list-style-type: none;\n opacity: 0.75;\n}\n[data-md-state=done] .md-source__facts {\n animation: md-source__facts--done 250ms ease-in;\n}\n.md-source__fact {\n display: inline-block;\n}\n[data-md-state=done] .md-source__fact {\n animation: md-source__fact--done 400ms ease-out;\n}\n.md-source__fact::before {\n display: inline-block;\n width: 0.6rem;\n height: 0.6rem;\n margin-right: 0.1rem;\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-source__fact:nth-child(1n+2)::before {\n margin-left: 0.4rem;\n}\n[dir=rtl] .md-source__fact {\n margin-right: initial;\n margin-left: 0.1rem;\n}\n[dir=rtl] .md-source__fact:nth-child(1n+2)::before {\n margin-right: 0.4rem;\n margin-left: initial;\n}\n.md-source__fact--version::before {\n mask-image: var(--md-source-version-icon);\n}\n.md-source__fact--stars::before {\n mask-image: var(--md-source-stars-icon);\n}\n.md-source__fact--forks::before {\n mask-image: var(--md-source-forks-icon);\n}\n.md-source__fact--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n}\n\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n}\n@media print {\n .md-tabs {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-tabs {\n display: none;\n }\n}\n.md-tabs[data-md-state=hidden] {\n pointer-events: none;\n}\n.md-tabs__list {\n margin: 0;\n margin-left: 0.2rem;\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n}\n[dir=rtl] .md-tabs__list {\n margin-right: 0.2rem;\n margin-left: initial;\n}\n.md-tabs__item {\n display: inline-block;\n height: 2.4rem;\n padding-right: 0.6rem;\n padding-left: 0.6rem;\n}\n.md-tabs__link {\n display: block;\n margin-top: 0.8rem;\n font-size: 0.7rem;\n backface-visibility: hidden;\n opacity: 0.7;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms;\n}\n.md-tabs__link--active, .md-tabs__link:focus, .md-tabs__link:hover {\n color: inherit;\n opacity: 1;\n}\n.md-tabs__item:nth-child(2) .md-tabs__link {\n transition-delay: 20ms;\n}\n.md-tabs__item:nth-child(3) .md-tabs__link {\n transition-delay: 40ms;\n}\n.md-tabs__item:nth-child(4) .md-tabs__link {\n transition-delay: 60ms;\n}\n.md-tabs__item:nth-child(5) .md-tabs__link {\n transition-delay: 80ms;\n}\n.md-tabs__item:nth-child(6) .md-tabs__link {\n transition-delay: 100ms;\n}\n.md-tabs__item:nth-child(7) .md-tabs__link {\n transition-delay: 120ms;\n}\n.md-tabs__item:nth-child(8) .md-tabs__link {\n transition-delay: 140ms;\n}\n.md-tabs__item:nth-child(9) .md-tabs__link {\n transition-delay: 160ms;\n}\n.md-tabs__item:nth-child(10) .md-tabs__link {\n transition-delay: 180ms;\n}\n.md-tabs__item:nth-child(11) .md-tabs__link {\n transition-delay: 200ms;\n}\n.md-tabs__item:nth-child(12) .md-tabs__link {\n transition-delay: 220ms;\n}\n.md-tabs__item:nth-child(13) .md-tabs__link {\n transition-delay: 240ms;\n}\n.md-tabs__item:nth-child(14) .md-tabs__link {\n transition-delay: 260ms;\n}\n.md-tabs__item:nth-child(15) .md-tabs__link {\n transition-delay: 280ms;\n}\n.md-tabs__item:nth-child(16) .md-tabs__link {\n transition-delay: 300ms;\n}\n.md-tabs[data-md-state=hidden] .md-tabs__link {\n transform: translateY(50%);\n opacity: 0;\n transition: transform 0ms 100ms, opacity 100ms;\n}\n\n.md-top {\n position: sticky;\n bottom: 0.4rem;\n z-index: 1;\n float: right;\n margin: -2.8rem 0.4rem 0.4rem;\n padding: 0.4rem;\n color: var(--md-primary-bg-color);\n background: var(--md-primary-fg-color);\n border-radius: 100%;\n outline: none;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.1);\n transform: translateY(0);\n transition: opacity 125ms, transform 125ms cubic-bezier(0.4, 0, 0.2, 1), background-color 125ms;\n}\n[dir=rtl] .md-top {\n float: left;\n}\n.md-top[data-md-state=hidden] {\n transform: translateY(-0.2rem);\n opacity: 0;\n pointer-events: none;\n}\n.md-top:focus, .md-top:hover {\n background: var(--md-accent-fg-color);\n transform: scale(1.1);\n}\n\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n.md-version {\n flex-shrink: 0;\n height: 2.4rem;\n font-size: 0.8rem;\n}\n.md-version__current {\n position: relative;\n top: 0.05rem;\n margin-right: 0.4rem;\n margin-left: 1.4rem;\n color: inherit;\n outline: none;\n cursor: pointer;\n}\n[dir=rtl] .md-version__current {\n margin-right: 1.4rem;\n margin-left: 0.4rem;\n}\n.md-version__current::after {\n display: inline-block;\n width: 0.4rem;\n height: 0.6rem;\n margin-left: 0.4rem;\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n}\n[dir=rtl] .md-version__current::after {\n margin-right: 0.4rem;\n margin-left: initial;\n}\n.md-version__list {\n position: absolute;\n top: 0.15rem;\n z-index: 1;\n max-height: 0;\n margin: 0.2rem 0.8rem;\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n opacity: 0;\n transition: max-height 0ms 500ms, opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n}\n.md-version:focus-within .md-version__list, .md-version:hover .md-version__list {\n max-height: 10rem;\n opacity: 1;\n transition: max-height 0ms, opacity 250ms;\n}\n.md-version__item {\n line-height: 1.8rem;\n}\n.md-version__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-version__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-version__link:focus, .md-version__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-version__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n:root {\n --md-admonition-icon--note:\n svg-load(\"material/pencil.svg\");\n --md-admonition-icon--abstract:\n svg-load(\"material/text-subject.svg\");\n --md-admonition-icon--info:\n svg-load(\"material/information.svg\");\n --md-admonition-icon--tip:\n svg-load(\"material/fire.svg\");\n --md-admonition-icon--success:\n svg-load(\"material/check-circle.svg\");\n --md-admonition-icon--question:\n svg-load(\"material/help-circle.svg\");\n --md-admonition-icon--warning:\n svg-load(\"material/alert.svg\");\n --md-admonition-icon--failure:\n svg-load(\"material/close-circle.svg\");\n --md-admonition-icon--danger:\n svg-load(\"material/flash-circle.svg\");\n --md-admonition-icon--bug:\n svg-load(\"material/bug.svg\");\n --md-admonition-icon--example:\n svg-load(\"material/format-list-numbered.svg\");\n --md-admonition-icon--quote:\n svg-load(\"material/format-quote-close.svg\");\n}\n\n.md-typeset .admonition, .md-typeset details {\n margin: 1.5625em 0;\n padding: 0 0.6rem;\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: 0.64rem;\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: 0.2rem solid #448aff;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.05);\n}\n@media print {\n .md-typeset .admonition, .md-typeset details {\n box-shadow: none;\n }\n}\n[dir=rtl] .md-typeset .admonition, [dir=rtl] .md-typeset details {\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\n.md-typeset .admonition .admonition, .md-typeset details .admonition, .md-typeset .admonition details, .md-typeset details details {\n margin-top: 1em;\n margin-bottom: 1em;\n}\n.md-typeset .admonition .md-typeset__scrollwrap, .md-typeset details .md-typeset__scrollwrap {\n margin: 1em -0.6rem;\n}\n.md-typeset .admonition .md-typeset__table, .md-typeset details .md-typeset__table {\n padding: 0 0.6rem;\n}\n.md-typeset .admonition > .tabbed-set:only-child, .md-typeset details > .tabbed-set:only-child {\n margin-top: 0;\n}\nhtml .md-typeset .admonition > :last-child, html .md-typeset details > :last-child {\n margin-bottom: 0.6rem;\n}\n.md-typeset .admonition-title, .md-typeset summary {\n position: relative;\n margin: 0 -0.6rem 0 -0.8rem;\n padding: 0.4rem 0.6rem 0.4rem 2rem;\n font-weight: 700;\n background-color: rgba(68, 138, 255, 0.1);\n border-left: 0.2rem solid #448aff;\n}\n[dir=rtl] .md-typeset .admonition-title, [dir=rtl] .md-typeset summary {\n margin: 0 -0.8rem 0 -0.6rem;\n padding: 0.4rem 2rem 0.4rem 0.6rem;\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\nhtml .md-typeset .admonition-title:last-child, html .md-typeset summary:last-child {\n margin-bottom: 0;\n}\n.md-typeset .admonition-title::before, .md-typeset summary::before {\n position: absolute;\n left: 0.6rem;\n width: 1rem;\n height: 1rem;\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .admonition-title::before, [dir=rtl] .md-typeset summary::before {\n right: 0.6rem;\n left: initial;\n}\n.md-typeset .admonition-title + .tabbed-set:last-child, .md-typeset summary + .tabbed-set:last-child {\n margin-top: 0;\n}\n\n.md-typeset .admonition.note, .md-typeset details.note {\n border-color: #448aff;\n}\n\n.md-typeset .note > .admonition-title, .md-typeset .note > summary {\n background-color: rgba(68, 138, 255, 0.1);\n border-color: #448aff;\n}\n.md-typeset .note > .admonition-title::before, .md-typeset .note > summary::before {\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.abstract, .md-typeset details.abstract, .md-typeset .admonition.tldr, .md-typeset details.tldr, .md-typeset .admonition.summary, .md-typeset details.summary {\n border-color: #00b0ff;\n}\n\n.md-typeset .abstract > .admonition-title, .md-typeset .abstract > summary, .md-typeset .tldr > .admonition-title, .md-typeset .tldr > summary, .md-typeset .summary > .admonition-title, .md-typeset .summary > summary {\n background-color: rgba(0, 176, 255, 0.1);\n border-color: #00b0ff;\n}\n.md-typeset .abstract > .admonition-title::before, .md-typeset .abstract > summary::before, .md-typeset .tldr > .admonition-title::before, .md-typeset .tldr > summary::before, .md-typeset .summary > .admonition-title::before, .md-typeset .summary > summary::before {\n background-color: #00b0ff;\n mask-image: var(--md-admonition-icon--abstract);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.info, .md-typeset details.info, .md-typeset .admonition.todo, .md-typeset details.todo {\n border-color: #00b8d4;\n}\n\n.md-typeset .info > .admonition-title, .md-typeset .info > summary, .md-typeset .todo > .admonition-title, .md-typeset .todo > summary {\n background-color: rgba(0, 184, 212, 0.1);\n border-color: #00b8d4;\n}\n.md-typeset .info > .admonition-title::before, .md-typeset .info > summary::before, .md-typeset .todo > .admonition-title::before, .md-typeset .todo > summary::before {\n background-color: #00b8d4;\n mask-image: var(--md-admonition-icon--info);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.tip, .md-typeset details.tip, .md-typeset .admonition.important, .md-typeset details.important, .md-typeset .admonition.hint, .md-typeset details.hint {\n border-color: #00bfa5;\n}\n\n.md-typeset .tip > .admonition-title, .md-typeset .tip > summary, .md-typeset .important > .admonition-title, .md-typeset .important > summary, .md-typeset .hint > .admonition-title, .md-typeset .hint > summary {\n background-color: rgba(0, 191, 165, 0.1);\n border-color: #00bfa5;\n}\n.md-typeset .tip > .admonition-title::before, .md-typeset .tip > summary::before, .md-typeset .important > .admonition-title::before, .md-typeset .important > summary::before, .md-typeset .hint > .admonition-title::before, .md-typeset .hint > summary::before {\n background-color: #00bfa5;\n mask-image: var(--md-admonition-icon--tip);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.success, .md-typeset details.success, .md-typeset .admonition.done, .md-typeset details.done, .md-typeset .admonition.check, .md-typeset details.check {\n border-color: #00c853;\n}\n\n.md-typeset .success > .admonition-title, .md-typeset .success > summary, .md-typeset .done > .admonition-title, .md-typeset .done > summary, .md-typeset .check > .admonition-title, .md-typeset .check > summary {\n background-color: rgba(0, 200, 83, 0.1);\n border-color: #00c853;\n}\n.md-typeset .success > .admonition-title::before, .md-typeset .success > summary::before, .md-typeset .done > .admonition-title::before, .md-typeset .done > summary::before, .md-typeset .check > .admonition-title::before, .md-typeset .check > summary::before {\n background-color: #00c853;\n mask-image: var(--md-admonition-icon--success);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.question, .md-typeset details.question, .md-typeset .admonition.faq, .md-typeset details.faq, .md-typeset .admonition.help, .md-typeset details.help {\n border-color: #64dd17;\n}\n\n.md-typeset .question > .admonition-title, .md-typeset .question > summary, .md-typeset .faq > .admonition-title, .md-typeset .faq > summary, .md-typeset .help > .admonition-title, .md-typeset .help > summary {\n background-color: rgba(100, 221, 23, 0.1);\n border-color: #64dd17;\n}\n.md-typeset .question > .admonition-title::before, .md-typeset .question > summary::before, .md-typeset .faq > .admonition-title::before, .md-typeset .faq > summary::before, .md-typeset .help > .admonition-title::before, .md-typeset .help > summary::before {\n background-color: #64dd17;\n mask-image: var(--md-admonition-icon--question);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.warning, .md-typeset details.warning, .md-typeset .admonition.attention, .md-typeset details.attention, .md-typeset .admonition.caution, .md-typeset details.caution {\n border-color: #ff9100;\n}\n\n.md-typeset .warning > .admonition-title, .md-typeset .warning > summary, .md-typeset .attention > .admonition-title, .md-typeset .attention > summary, .md-typeset .caution > .admonition-title, .md-typeset .caution > summary {\n background-color: rgba(255, 145, 0, 0.1);\n border-color: #ff9100;\n}\n.md-typeset .warning > .admonition-title::before, .md-typeset .warning > summary::before, .md-typeset .attention > .admonition-title::before, .md-typeset .attention > summary::before, .md-typeset .caution > .admonition-title::before, .md-typeset .caution > summary::before {\n background-color: #ff9100;\n mask-image: var(--md-admonition-icon--warning);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.failure, .md-typeset details.failure, .md-typeset .admonition.missing, .md-typeset details.missing, .md-typeset .admonition.fail, .md-typeset details.fail {\n border-color: #ff5252;\n}\n\n.md-typeset .failure > .admonition-title, .md-typeset .failure > summary, .md-typeset .missing > .admonition-title, .md-typeset .missing > summary, .md-typeset .fail > .admonition-title, .md-typeset .fail > summary {\n background-color: rgba(255, 82, 82, 0.1);\n border-color: #ff5252;\n}\n.md-typeset .failure > .admonition-title::before, .md-typeset .failure > summary::before, .md-typeset .missing > .admonition-title::before, .md-typeset .missing > summary::before, .md-typeset .fail > .admonition-title::before, .md-typeset .fail > summary::before {\n background-color: #ff5252;\n mask-image: var(--md-admonition-icon--failure);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.danger, .md-typeset details.danger, .md-typeset .admonition.error, .md-typeset details.error {\n border-color: #ff1744;\n}\n\n.md-typeset .danger > .admonition-title, .md-typeset .danger > summary, .md-typeset .error > .admonition-title, .md-typeset .error > summary {\n background-color: rgba(255, 23, 68, 0.1);\n border-color: #ff1744;\n}\n.md-typeset .danger > .admonition-title::before, .md-typeset .danger > summary::before, .md-typeset .error > .admonition-title::before, .md-typeset .error > summary::before {\n background-color: #ff1744;\n mask-image: var(--md-admonition-icon--danger);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.bug, .md-typeset details.bug {\n border-color: #f50057;\n}\n\n.md-typeset .bug > .admonition-title, .md-typeset .bug > summary {\n background-color: rgba(245, 0, 87, 0.1);\n border-color: #f50057;\n}\n.md-typeset .bug > .admonition-title::before, .md-typeset .bug > summary::before {\n background-color: #f50057;\n mask-image: var(--md-admonition-icon--bug);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.example, .md-typeset details.example {\n border-color: #7c4dff;\n}\n\n.md-typeset .example > .admonition-title, .md-typeset .example > summary {\n background-color: rgba(124, 77, 255, 0.1);\n border-color: #7c4dff;\n}\n.md-typeset .example > .admonition-title::before, .md-typeset .example > summary::before {\n background-color: #7c4dff;\n mask-image: var(--md-admonition-icon--example);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.quote, .md-typeset details.quote, .md-typeset .admonition.cite, .md-typeset details.cite {\n border-color: #9e9e9e;\n}\n\n.md-typeset .quote > .admonition-title, .md-typeset .quote > summary, .md-typeset .cite > .admonition-title, .md-typeset .cite > summary {\n background-color: rgba(158, 158, 158, 0.1);\n border-color: #9e9e9e;\n}\n.md-typeset .quote > .admonition-title::before, .md-typeset .quote > summary::before, .md-typeset .cite > .admonition-title::before, .md-typeset .cite > summary::before {\n background-color: #9e9e9e;\n mask-image: var(--md-admonition-icon--quote);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n.md-typeset [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n}\n.md-typeset [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n}\n.md-typeset .footnote {\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n}\n.md-typeset .footnote > ol {\n margin-left: 0;\n}\n.md-typeset .footnote > ol > li {\n transition: color 125ms;\n}\n.md-typeset .footnote > ol > li:target {\n color: var(--md-default-fg-color);\n}\n.md-typeset .footnote > ol > li:hover .footnote-backref, .md-typeset .footnote > ol > li:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n}\n.md-typeset .footnote > ol > li > :first-child {\n margin-top: 0;\n}\n.md-typeset .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(0.25rem);\n opacity: 0;\n transition: color 250ms, transform 250ms 250ms, opacity 125ms 250ms;\n}\n@media print {\n .md-typeset .footnote-backref {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n}\n[dir=rtl] .md-typeset .footnote-backref {\n transform: translateX(-0.25rem);\n}\n.md-typeset .footnote-backref:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset .footnote-backref::before {\n display: inline-block;\n width: 0.8rem;\n height: 0.8rem;\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .footnote-backref::before svg {\n transform: scaleX(-1);\n}\n\n.md-typeset .headerlink {\n display: inline-block;\n margin-left: 0.5rem;\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition: color 250ms, opacity 125ms;\n}\n@media print {\n .md-typeset .headerlink {\n display: none;\n }\n}\n[dir=rtl] .md-typeset .headerlink {\n margin-right: 0.5rem;\n margin-left: initial;\n}\n.md-typeset :hover > .headerlink,\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus {\n opacity: 1;\n transition: color 250ms, opacity 125ms;\n}\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus,\n.md-typeset .headerlink:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset :target {\n scroll-margin-top: 3.6rem;\n}\n.md-typeset h1:target,\n.md-typeset h2:target,\n.md-typeset h3:target {\n scroll-margin-top: initial;\n}\n.md-typeset h1:target::before,\n.md-typeset h2:target::before,\n.md-typeset h3:target::before {\n display: block;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n content: \"\";\n}\n.md-typeset h4:target {\n scroll-margin-top: initial;\n}\n.md-typeset h4:target::before {\n display: block;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n content: \"\";\n}\n.md-typeset h5:target,\n.md-typeset h6:target {\n scroll-margin-top: initial;\n}\n.md-typeset h5:target::before,\n.md-typeset h6:target::before {\n display: block;\n margin-top: -3.6rem;\n padding-top: 3.6rem;\n content: \"\";\n}\n\n.md-typeset div.arithmatex {\n overflow: auto;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset div.arithmatex {\n margin: 0 -0.8rem;\n }\n}\n.md-typeset div.arithmatex > * {\n width: min-content;\n margin: 1em auto !important;\n padding: 0 0.8rem;\n touch-action: auto;\n}\n\n.md-typeset del.critic,\n.md-typeset ins.critic,\n.md-typeset .critic.comment {\n box-decoration-break: clone;\n}\n.md-typeset del.critic {\n background-color: var(--md-typeset-del-color);\n}\n.md-typeset ins.critic {\n background-color: var(--md-typeset-ins-color);\n}\n.md-typeset .critic.comment {\n color: var(--md-code-hl-comment-color);\n}\n.md-typeset .critic.comment::before {\n content: \"/* \";\n}\n.md-typeset .critic.comment::after {\n content: \" */\";\n}\n.md-typeset .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: 0.8rem;\n padding-left: 0.8rem;\n overflow: auto;\n box-shadow: none;\n}\n.md-typeset .critic.block > :first-child {\n margin-top: 0.5em;\n}\n.md-typeset .critic.block > :last-child {\n margin-bottom: 0.5em;\n}\n\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n.md-typeset details {\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n}\n.md-typeset details[open] > summary::after {\n transform: rotate(90deg);\n}\n.md-typeset details:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n}\n.md-typeset details:not([open]) > summary {\n border-radius: 0.1rem;\n}\n.md-typeset details::after {\n display: table;\n content: \"\";\n}\n.md-typeset summary {\n display: block;\n min-height: 1rem;\n padding: 0.4rem 1.8rem 0.4rem 2rem;\n border-top-left-radius: 0.1rem;\n border-top-right-radius: 0.1rem;\n cursor: pointer;\n}\n[dir=rtl] .md-typeset summary {\n padding: 0.4rem 2.2rem 0.4rem 1.8rem;\n}\n.md-typeset summary:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset summary::after {\n position: absolute;\n top: 0.4rem;\n right: 0.4rem;\n width: 1rem;\n height: 1rem;\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n}\n[dir=rtl] .md-typeset summary::after {\n right: initial;\n left: 0.4rem;\n transform: rotate(180deg);\n}\n.md-typeset summary::marker, .md-typeset summary::-webkit-details-marker {\n display: none;\n}\n\n.md-typeset .emojione,\n.md-typeset .twemoji,\n.md-typeset .gemoji {\n display: inline-flex;\n height: 1.125em;\n vertical-align: text-top;\n}\n.md-typeset .emojione svg,\n.md-typeset .twemoji svg,\n.md-typeset .gemoji svg {\n width: 1.125em;\n max-height: 100%;\n fill: currentColor;\n}\n\n.highlight .o,\n.highlight .ow {\n color: var(--md-code-hl-operator-color);\n}\n.highlight .p {\n color: var(--md-code-hl-punctuation-color);\n}\n.highlight .cpf,\n.highlight .l,\n.highlight .s,\n.highlight .sb,\n.highlight .sc,\n.highlight .s2,\n.highlight .si,\n.highlight .s1,\n.highlight .ss {\n color: var(--md-code-hl-string-color);\n}\n.highlight .cp,\n.highlight .se,\n.highlight .sh,\n.highlight .sr,\n.highlight .sx {\n color: var(--md-code-hl-special-color);\n}\n.highlight .m,\n.highlight .mb,\n.highlight .mf,\n.highlight .mh,\n.highlight .mi,\n.highlight .il,\n.highlight .mo {\n color: var(--md-code-hl-number-color);\n}\n.highlight .k,\n.highlight .kd,\n.highlight .kn,\n.highlight .kp,\n.highlight .kr,\n.highlight .kt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .kc,\n.highlight .n {\n color: var(--md-code-hl-name-color);\n}\n.highlight .no,\n.highlight .nb,\n.highlight .bp {\n color: var(--md-code-hl-constant-color);\n}\n.highlight .nc,\n.highlight .ne,\n.highlight .nf,\n.highlight .nn {\n color: var(--md-code-hl-function-color);\n}\n.highlight .nd,\n.highlight .ni,\n.highlight .nl,\n.highlight .nt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .c,\n.highlight .cm,\n.highlight .c1,\n.highlight .ch,\n.highlight .cs,\n.highlight .sd {\n color: var(--md-code-hl-comment-color);\n}\n.highlight .na,\n.highlight .nv,\n.highlight .vc,\n.highlight .vg,\n.highlight .vi {\n color: var(--md-code-hl-variable-color);\n}\n.highlight .ge,\n.highlight .gr,\n.highlight .gh,\n.highlight .go,\n.highlight .gp,\n.highlight .gs,\n.highlight .gu,\n.highlight .gt {\n color: var(--md-code-hl-generic-color);\n}\n.highlight .gd,\n.highlight .gi {\n margin: 0 -0.125em;\n padding: 0 0.125em;\n border-radius: 0.1rem;\n}\n.highlight .gd {\n background-color: var(--md-typeset-del-color);\n}\n.highlight .gi {\n background-color: var(--md-typeset-ins-color);\n}\n.highlight .hll {\n display: block;\n margin: 0 -1.1764705882em;\n padding: 0 1.1764705882em;\n background-color: var(--md-code-hl-color);\n}\n.highlight [data-linenos]::before {\n position: sticky;\n left: -1.1764705882em;\n float: left;\n margin-right: 1.1764705882em;\n margin-left: -1.1764705882em;\n padding-left: 1.1764705882em;\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n}\n\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n}\n.highlighttable tbody,\n.highlighttable td {\n display: block;\n padding: 0;\n}\n.highlighttable tr {\n display: flex;\n}\n.highlighttable pre {\n margin: 0;\n}\n.highlighttable .linenos {\n padding: 0.7720588235em 1.1764705882em;\n padding-right: 0;\n font-size: 0.85em;\n background-color: var(--md-code-bg-color);\n user-select: none;\n}\n.highlighttable .linenodiv {\n padding-right: 0.5882352941em;\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n}\n.highlighttable .linenodiv pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n}\n.highlighttable .code {\n flex: 1;\n overflow: hidden;\n}\n\n.md-typeset .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: 0.1rem;\n}\n.md-typeset .highlighttable code {\n border-radius: 0;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > .highlight {\n margin: 1em -0.8rem;\n }\n .md-typeset > .highlight .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n .md-typeset > .highlight code {\n border-radius: 0;\n }\n .md-typeset > .highlighttable {\n margin: 1em -0.8rem;\n border-radius: 0;\n }\n .md-typeset > .highlighttable .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n}\n\n.md-typeset .keys kbd::before,\n.md-typeset .keys kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n}\n.md-typeset .keys span {\n padding: 0 0.2em;\n color: var(--md-default-fg-color--light);\n}\n.md-typeset .keys .key-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-left-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-right-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-left-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-right-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-left-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-right-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-left-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-right-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-left-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-right-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-left-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-right-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-left-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-right-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-left-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-right-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-arrow-down::before {\n padding-right: 0.4em;\n content: \"↓\";\n}\n.md-typeset .keys .key-arrow-left::before {\n padding-right: 0.4em;\n content: \"←\";\n}\n.md-typeset .keys .key-arrow-right::before {\n padding-right: 0.4em;\n content: \"→\";\n}\n.md-typeset .keys .key-arrow-up::before {\n padding-right: 0.4em;\n content: \"↑\";\n}\n.md-typeset .keys .key-backspace::before {\n padding-right: 0.4em;\n content: \"⌫\";\n}\n.md-typeset .keys .key-backtab::before {\n padding-right: 0.4em;\n content: \"⇤\";\n}\n.md-typeset .keys .key-caps-lock::before {\n padding-right: 0.4em;\n content: \"⇪\";\n}\n.md-typeset .keys .key-clear::before {\n padding-right: 0.4em;\n content: \"⌧\";\n}\n.md-typeset .keys .key-context-menu::before {\n padding-right: 0.4em;\n content: \"☰\";\n}\n.md-typeset .keys .key-delete::before {\n padding-right: 0.4em;\n content: \"⌦\";\n}\n.md-typeset .keys .key-eject::before {\n padding-right: 0.4em;\n content: \"⏏\";\n}\n.md-typeset .keys .key-end::before {\n padding-right: 0.4em;\n content: \"⤓\";\n}\n.md-typeset .keys .key-escape::before {\n padding-right: 0.4em;\n content: \"⎋\";\n}\n.md-typeset .keys .key-home::before {\n padding-right: 0.4em;\n content: \"⤒\";\n}\n.md-typeset .keys .key-insert::before {\n padding-right: 0.4em;\n content: \"⎀\";\n}\n.md-typeset .keys .key-page-down::before {\n padding-right: 0.4em;\n content: \"⇟\";\n}\n.md-typeset .keys .key-page-up::before {\n padding-right: 0.4em;\n content: \"⇞\";\n}\n.md-typeset .keys .key-print-screen::before {\n padding-right: 0.4em;\n content: \"⎙\";\n}\n.md-typeset .keys .key-tab::after {\n padding-left: 0.4em;\n content: \"⇥\";\n}\n.md-typeset .keys .key-num-enter::after {\n padding-left: 0.4em;\n content: \"⌤\";\n}\n.md-typeset .keys .key-enter::after {\n padding-left: 0.4em;\n content: \"⏎\";\n}\n\n.md-typeset .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 -0.05rem var(--md-default-fg-color--lightest);\n}\n@media print {\n .md-typeset .tabbed-content {\n display: block;\n order: initial;\n }\n}\n.md-typeset .tabbed-content > pre:only-child,\n.md-typeset .tabbed-content > .highlight:only-child pre,\n.md-typeset .tabbed-content > .highlighttable:only-child {\n margin: 0;\n}\n.md-typeset .tabbed-content > pre:only-child > code,\n.md-typeset .tabbed-content > .highlight:only-child pre > code,\n.md-typeset .tabbed-content > .highlighttable:only-child > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.md-typeset .tabbed-content > .tabbed-set {\n margin: 0;\n}\n.md-typeset .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: 0.1rem;\n}\n.md-typeset .tabbed-set > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-typeset .tabbed-set > input:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .tabbed-set > input:checked + label + .tabbed-content {\n display: block;\n}\n.md-typeset .tabbed-set > input:focus + label {\n outline-style: auto;\n}\n.md-typeset .tabbed-set > input:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset .tabbed-set > label {\n z-index: 1;\n width: auto;\n padding: 0.9375em 1.25em 0.78125em;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.64rem;\n border-bottom: 0.1rem solid transparent;\n cursor: pointer;\n transition: color 250ms;\n}\n.md-typeset .tabbed-set > label:hover {\n color: var(--md-accent-fg-color);\n}\n\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n.md-typeset .task-list-item {\n position: relative;\n list-style-type: none;\n}\n.md-typeset .task-list-item [type=checkbox] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n}\n[dir=rtl] .md-typeset .task-list-item [type=checkbox] {\n right: -2em;\n left: initial;\n}\n.md-typeset .task-list-control [type=checkbox] {\n z-index: -1;\n opacity: 0;\n}\n.md-typeset .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: -1.5em;\n width: 1.25em;\n height: 1.25em;\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .task-list-indicator::before {\n right: -1.5em;\n left: initial;\n}\n.md-typeset [type=checkbox]:checked + .task-list-indicator::before {\n background-color: #00e676;\n mask-image: var(--md-tasklist-icon--checked);\n}\n\n@media screen and (min-width: 45em) {\n .md-typeset .inline {\n float: left;\n width: 11.7rem;\n margin-top: 0;\n margin-right: 0.8rem;\n margin-bottom: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n .md-typeset .inline.end {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline.end {\n float: left;\n margin-right: 0.8rem;\n margin-left: 0;\n }\n}\n\n/*# sourceMappingURL=main.css.map */","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Enforce correct box model and prevent adjustments of font size after\n// orientation changes in IE and iOS\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n// All elements shall inherit the document default\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n// Remove margin in all browsers\nbody {\n margin: 0;\n}\n\n// Reset tap outlines on iOS and Android\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\n// Reset link styles\na {\n color: inherit;\n text-decoration: none;\n}\n\n// Normalize horizontal separator styles\nhr {\n display: block;\n box-sizing: content-box;\n height: px2rem(1px);\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\n// Normalize font-size in all browsers\nsmall {\n font-size: 80%;\n}\n\n// Prevent subscript and superscript from affecting line-height\nsub,\nsup {\n line-height: 1em;\n}\n\n// Remove border on image\nimg {\n border-style: none;\n}\n\n// Reset table styles\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\n// Reset table cell styles\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\n// Reset button styles\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\n// Reset input styles\ninput {\n border: 0;\n outline: none;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Color definitions\n:root {\n\n // Default color shades\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n\n // Primary color shades\n --md-primary-fg-color: hsla(#{hex2hsl($clr-indigo-500)}, 1);\n --md-primary-fg-color--light: hsla(#{hex2hsl($clr-indigo-400)}, 1);\n --md-primary-fg-color--dark: hsla(#{hex2hsl($clr-indigo-700)}, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Accent color shades\n --md-accent-fg-color: hsla(#{hex2hsl($clr-indigo-a200)}, 1);\n --md-accent-fg-color--transparent: hsla(#{hex2hsl($clr-indigo-a200)}, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Light theme (default)\n > * {\n\n // Code color shades\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n\n // Code highlighting color shades\n --md-code-hl-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n\n // Typeset color shades\n --md-typeset-color: var(--md-default-fg-color);\n --md-typeset-a-color: var(--md-primary-fg-color);\n\n // Typeset `mark` color shades\n --md-typeset-mark-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n\n // Typeset `del` and `ins` color shades\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n\n // Typeset `kbd` color shades\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n\n // Admonition color shades\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n\n // Footer color shades\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon\n.md-icon {\n\n // SVG defaults\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: font definitions\n// ----------------------------------------------------------------------------\n\n// Enable font-smoothing in Webkit and FF\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Define default fonts\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family:\n var(--md-text-font-family, _),\n -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\n// Define monospaced fonts\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family:\n var(--md-code-font-family, _),\n SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: typesetted content\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-typeset-table--ascending: svg-load(\"material/arrow-down.svg\");\n --md-typeset-table--descending: svg-load(\"material/arrow-up.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Content that is typeset - if possible, all margins, paddings and font sizes\n// should be set in ems, so nested blocks (e.g. admonitions) render correctly.\n.md-typeset {\n font-size: px2rem(16px);\n line-height: 1.6;\n color-adjust: exact;\n\n // [print]: We'll use a smaller `font-size` for printing, so code examples\n // don't break too early, and `16px` looks too big anyway.\n @media print {\n font-size: px2rem(13.6px);\n }\n\n // Default spacing\n ul,\n ol,\n dl,\n figure,\n blockquote,\n pre {\n margin: 1em 0;\n }\n\n // Headline on level 1\n h1 {\n margin: 0 0 px2em(40px, 32px);\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: px2em(32px);\n line-height: 1.3;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 2\n h2 {\n margin: px2em(40px, 25px) 0 px2em(16px, 25px);\n font-weight: 300;\n font-size: px2em(25px);\n line-height: 1.4;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3\n h3 {\n margin: px2em(32px, 20px) 0 px2em(16px, 20px);\n font-weight: 400;\n font-size: px2em(20px);\n line-height: 1.5;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3 following level 2\n h2 + h3 {\n margin-top: px2em(16px, 20px);\n }\n\n // Headline on level 4\n h4 {\n margin: px2em(16px) 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5-6\n h5,\n h6 {\n margin: px2em(16px, 12.8px) 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2em(12.8px);\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5\n h5 {\n text-transform: uppercase;\n }\n\n // Horizontal separator\n hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: px2rem(1px) solid var(--md-default-fg-color--lightest);\n }\n\n // Text link\n a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n\n // Also enable color transition on pseudo elements\n &,\n &::before {\n transition: color 125ms;\n }\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n\n // Code block\n code,\n pre,\n kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n\n // [print]: Wrap text and hide scollbars\n @media print {\n white-space: pre-wrap;\n }\n }\n\n // Inline code block\n code {\n padding: 0 px2em(4px, 13.6px);\n font-size: px2em(13.6px);\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: px2rem(2px);\n box-decoration-break: clone;\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Code block in headline\n h1 code,\n h2 code,\n h3 code,\n h4 code,\n h5 code,\n h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n }\n\n // Ensure link color in code blocks\n a code {\n color: currentColor;\n }\n\n // Unformatted content\n pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n\n // Code block\n > code {\n display: block;\n margin: 0;\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Code block on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Unformatted text\n > pre {\n margin: 1em px2rem(-16px);\n\n // Code block\n code {\n border-radius: 0;\n }\n }\n }\n\n // Keyboard key\n kbd {\n display: inline-block;\n padding: 0 px2em(8px, 12px);\n color: var(--md-default-fg-color);\n font-size: px2em(12px);\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(2px) 0 px2rem(1px) var(--md-typeset-kbd-border-color),\n 0 px2rem(2px) 0 var(--md-typeset-kbd-border-color),\n 0 px2rem(-2px) px2rem(4px) var(--md-typeset-kbd-accent-color) inset;\n }\n\n // Text highlighting marker\n mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n }\n\n // Abbreviation\n abbr {\n text-decoration: none;\n border-bottom: px2rem(1px) dotted var(--md-default-fg-color--light);\n cursor: help;\n\n // Show tooltip for touch devices\n @media (hover: none) {\n position: relative;\n\n // Tooltip\n &[title]:focus::after,\n &[title]:hover::after {\n @include z-depth(2);\n\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: px2rem(4px) px2rem(6px);\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n content: attr(title);\n }\n }\n }\n\n // Small text\n small {\n opacity: 0.75;\n }\n\n // Superscript and subscript\n sup,\n sub {\n margin-left: px2em(1px, 12.8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(1px, 12.8px);\n margin-left: initial;\n }\n }\n\n // Blockquotes, possibly nested\n blockquote {\n display: flow-root;\n padding-left: px2rem(12px);\n color: var(--md-default-fg-color--light);\n border-left: px2rem(4px) solid var(--md-default-fg-color--lighter);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: initial;\n border-right: px2rem(4px) solid var(--md-default-fg-color--lighter);\n border-left: initial;\n }\n }\n\n // Unordered list\n ul {\n list-style-type: disc;\n }\n\n // Unordered and ordered list\n ul,\n ol {\n display: flow-root;\n margin-left: px2em(10px);\n padding: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n\n // Nested ordered list\n ol {\n list-style-type: lower-alpha;\n\n // Triply nested ordered list\n ol {\n list-style-type: lower-roman;\n }\n }\n\n // List element\n li {\n margin-bottom: 0.5em;\n margin-left: px2em(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(20px);\n margin-left: initial;\n }\n\n // Adjust spacing\n p,\n blockquote {\n margin: 0.5em 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Nested list\n ul,\n ol {\n margin: 0.5em 0 0.5em px2em(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n }\n }\n }\n\n // Definition list\n dd {\n margin: 1em 0 1.5em px2em(30px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(30px);\n margin-left: initial;\n }\n }\n\n // Image or icon\n img,\n svg {\n max-width: 100%;\n height: auto;\n\n // Adjust spacing when left-aligned\n &[align=\"left\"] {\n margin: 1em;\n margin-left: 0;\n }\n\n // Adjust spacing when right-aligned\n &[align=\"right\"] {\n margin: 1em;\n margin-right: 0;\n }\n\n // Adjust spacing when sole children\n &[align]:only-child {\n margin-top: 0;\n }\n }\n\n // Figure\n figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n\n // Figure images\n img {\n display: block;\n }\n }\n\n // Figure caption\n figcaption {\n max-width: px2rem(480px);\n margin: 1em auto 2em;\n font-style: italic;\n }\n\n // Limit width to container\n iframe {\n max-width: 100%;\n }\n\n // Data table\n table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: px2rem(12.8px);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.1);\n touch-action: auto;\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: table;\n }\n\n // Due to margin collapse because of the necessary inline-block hack, we\n // cannot increase the bottom margin on the table, so we just increase the\n // top margin on the following element\n + * {\n margin-top: 1.5em;\n }\n\n // Elements in table heading and cell\n th > *,\n td > * {\n\n // Adjust spacing on first child\n &:first-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Table heading and cell\n th:not([align]),\n td:not([align]) {\n text-align: left;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n text-align: right;\n }\n }\n\n // Table heading\n th {\n min-width: px2rem(100px);\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n color: var(--md-default-bg-color);\n vertical-align: top;\n background-color: var(--md-default-fg-color--light);\n\n // Links in table headings\n a {\n color: inherit;\n }\n }\n\n // Table cell\n td {\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n vertical-align: top;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n }\n\n // Table row\n tr {\n transition: background-color 125ms;\n\n // Table row on hover\n &:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 px2rem(1px) 0 var(--md-default-bg-color) inset;\n }\n\n // Hide border on first table row\n &:first-child td {\n border-top: 0;\n }\n }\n\n // Text link in table\n a {\n word-break: normal;\n }\n }\n\n // Sortable table\n table th[role=\"columnheader\"] {\n cursor: pointer;\n\n // Sort icon\n &::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: sub;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Sort ascending\n &[aria-sort=\"ascending\"]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--ascending);\n }\n\n // Sort descending\n &[aria-sort=\"descending\"]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--descending);\n }\n }\n\n // Data table scroll wrapper\n &__scrollwrap {\n margin: 1em px2rem(-16px);\n overflow-x: auto;\n touch-action: auto;\n }\n\n // Data table wrapper\n &__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 px2rem(16px);\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: block;\n }\n\n // Data table\n html & table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Device-specific breakpoints\n///\n/// @example\n/// $break-devices: (\n/// mobile: (\n/// portrait: 220px 479px,\n/// landscape: 480px 719px\n/// ),\n/// tablet: (\n/// portrait: 720px 959px,\n/// landscape: 960px 1219px\n/// ),\n/// screen: (\n/// small: 1220px 1599px,\n/// medium: 1600px 1999px,\n/// large: 2000px\n/// )\n/// );\n///\n$break-devices: () !default;\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\n///\n/// Choose minimum and maximum device widths\n///\n@function break-select-min-max($devices) {\n $min: 1000000;\n $max: 0;\n @each $key, $value in $devices {\n @while type-of($value) == map {\n $value: break-select-min-max($value);\n }\n @if type-of($value) == list {\n @each $number in $value {\n @if type-of($number) == number {\n $min: min($number, $min);\n @if $max {\n $max: max($number, $max);\n }\n } @else {\n @error \"Invalid number: #{$number}\";\n }\n }\n } @else if type-of($value) == number {\n $min: min($value, $min);\n $max: null;\n } @else {\n @error \"Invalid value: #{$value}\";\n }\n }\n @return $min, $max;\n}\n\n///\n/// Select minimum and maximum widths for a device breakpoint\n///\n@function break-select-device($device) {\n $current: $break-devices;\n @for $n from 1 through length($device) {\n @if type-of($current) == map {\n $current: map-get($current, nth($device, $n));\n } @else {\n @error \"Invalid device map: #{$devices}\";\n }\n }\n @if type-of($current) == list or type-of($current) == number {\n $current: (default: $current);\n }\n @return break-select-min-max($current);\n}\n\n// ----------------------------------------------------------------------------\n// Mixins\n// ----------------------------------------------------------------------------\n\n///\n/// A minimum-maximum media query breakpoint\n///\n@mixin break-at($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (min-width: $breakpoint) {\n @content;\n }\n } @else if type-of($breakpoint) == list {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @if type-of($min) == number and type-of($max) == number {\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// An orientation media query breakpoint\n///\n@mixin break-at-orientation($breakpoint) {\n @if type-of($breakpoint) == string {\n @media screen and (orientation: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A maximum-aspect-ratio media query breakpoint\n///\n@mixin break-at-ratio($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (max-aspect-ratio: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A minimum-maximum media query device breakpoint\n///\n@mixin break-at-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n @if nth($breakpoint, 2) {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A minimum media query device breakpoint\n///\n@mixin break-from-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $min: nth($breakpoint, 1);\n\n @media screen and (min-width: $min) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A maximum media query device breakpoint\n///\n@mixin break-to-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $max: nth($breakpoint, 2);\n\n @media screen and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n","//\n// Name: Material Shadows\n// Description: Mixins for Material Design Shadows.\n// Version: 3.0.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-shadows\n//\n// twitter: @mrmlnc\n//\n// ------------------------------------\n\n\n// Mixins\n// ------------------------------------\n\n@mixin z-depth-transition() {\n transition: box-shadow .28s cubic-bezier(.4, 0, .2, 1);\n}\n\n@mixin z-depth-focus() {\n box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);\n}\n\n@mixin z-depth-2dp() {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14),\n 0 1px 5px 0 rgba(0, 0, 0, .12),\n 0 3px 1px -2px rgba(0, 0, 0, .2);\n}\n\n@mixin z-depth-3dp() {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, .14),\n 0 1px 8px 0 rgba(0, 0, 0, .12),\n 0 3px 3px -2px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-4dp() {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14),\n 0 1px 10px 0 rgba(0, 0, 0, .12),\n 0 2px 4px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-6dp() {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, .14),\n 0 1px 18px 0 rgba(0, 0, 0, .12),\n 0 3px 5px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-8dp() {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14),\n 0 3px 14px 2px rgba(0, 0, 0, .12),\n 0 5px 5px -3px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-16dp() {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14),\n 0 6px 30px 5px rgba(0, 0, 0, .12),\n 0 8px 10px -5px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-24dp() {\n box-shadow: 0 9px 46px 8px rgba(0, 0, 0, .14),\n 0 24px 38px 3px rgba(0, 0, 0, .12),\n 0 11px 15px -7px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth($dp: 2) {\n @if $dp == 2 {\n @include z-depth-2dp();\n } @else if $dp == 3 {\n @include z-depth-3dp();\n } @else if $dp == 4 {\n @include z-depth-4dp();\n } @else if $dp == 6 {\n @include z-depth-6dp();\n } @else if $dp == 8 {\n @include z-depth-8dp();\n } @else if $dp == 16 {\n @include z-depth-16dp();\n } @else if $dp == 24 {\n @include z-depth-24dp();\n }\n}\n\n\n// Class generator\n// ------------------------------------\n\n@mixin z-depth-classes($transition: false, $focus: false) {\n @if $transition == true {\n &-transition {\n @include z-depth-transition();\n }\n }\n\n @if $focus == true {\n &-focus {\n @include z-depth-focus();\n }\n }\n\n // The available values for the shadow depth\n @each $depth in 2, 3, 4, 6, 8, 16, 24 {\n &-#{$depth}dp {\n @include z-depth($depth);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: base grid and containers\n// ----------------------------------------------------------------------------\n\n// Stretch container to viewport and set base `font-size`\nhtml {\n height: 100%;\n overflow-x: hidden;\n // Hack: normally, we would set the base `font-size` to `62.5%`, so we can\n // base all calculations on `10px`, but Chromium and Chrome define a minimal\n // `font-size` of `12px` if the system language is set to Chinese. For this\n // reason we just double the `font-size` and set it to `20px`.\n //\n // See https://github.com/squidfunk/mkdocs-material/issues/911\n font-size: 125%;\n\n // [screen medium +]: Set base `font-size` to `11px`\n @include break-from-device(screen medium) {\n font-size: 137.5%;\n }\n\n // [screen large +]: Set base `font-size` to `12px`\n @include break-from-device(screen large) {\n font-size: 150%;\n }\n}\n\n// Stretch body to container - flexbox is used, so the footer will always be\n// aligned to the bottom of the viewport\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n // Hack: reset `font-size` to `10px`, so the spacing for all inline elements\n // is correct again. Otherwise the spacing would be based on `20px`.\n font-size: px2rem(10px);\n background-color: var(--md-default-bg-color);\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n\n // Body in locked state\n &[data-md-state=\"lock\"] {\n\n // [tablet portrait -]: Omit scroll bubbling\n @include break-to-device(tablet portrait) {\n position: fixed;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Grid container - this class is applied to wrapper elements within the\n// header, content area and footer, and makes sure that their width is limited\n// to `1220px`, and they are rendered centered if the screen is larger.\n.md-grid {\n max-width: px2rem(1220px);\n margin-right: auto;\n margin-left: auto;\n}\n\n// Main container\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n}\n\n// Main area - stretch to remaining space of container\n.md-main {\n flex-grow: 1;\n\n // Main area wrapper\n &__inner {\n display: flex;\n height: 100%;\n margin-top: px2rem(24px + 6px);\n }\n}\n\n// Add ellipsis in case of overflowing text\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: navigational elements\n// ----------------------------------------------------------------------------\n\n// Toggle - this class is applied to checkbox elements, which are used to\n// implement the CSS-only drawer and navigation, as well as the search\n.md-toggle {\n display: none;\n}\n\n// Option - this class is applied to radio elements, which are used to\n// implement the color palette toggle\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Option label for checked radio button\n &:checked + label:not([hidden]) {\n display: block;\n }\n\n // Option label on focus\n &.focus-visible + label {\n outline-style: auto;\n }\n}\n\n// Skip link\n.md-skip {\n position: fixed;\n // Hack: if we don't set the negative `z-index`, the skip link will force the\n // creation of new layers when code blocks are near the header on scrolling\n z-index: -1;\n margin: px2rem(10px);\n padding: px2rem(6px) px2rem(10px);\n color: var(--md-default-bg-color);\n font-size: px2rem(12.8px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n transform: translateY(px2rem(8px));\n opacity: 0;\n\n // Show skip link on focus\n &:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 175ms 75ms;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: print styles\n// ----------------------------------------------------------------------------\n\n// Add margins to page\n@page {\n margin: 25mm;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Announcement bar\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide announcement bar\n @media print {\n display: none;\n }\n\n // Announcement wrapper\n &__inner {\n margin: px2rem(12px) auto;\n padding: 0 px2rem(16px);\n color: var(--md-footer-fg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Button to copy to clipboard\n.md-clipboard {\n position: absolute;\n top: px2em(8px);\n right: px2em(8px);\n z-index: 1;\n width: px2em(24px);\n height: px2em(24px);\n color: var(--md-default-fg-color--lightest);\n border-radius: px2rem(2px);\n cursor: pointer;\n transition: color 250ms;\n\n // [print]: Hide button\n @media print {\n display: none;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Darken color on code block hover\n :hover > & {\n color: var(--md-default-fg-color--light);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Button icon - the width and height are defined in `em`, so the size is\n // automatically adjusted for nested code blocks (e.g. in admonitions)\n &::after {\n display: block;\n width: px2em(18px);\n height: px2em(18px);\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Inline button\n &--inline {\n cursor: pointer;\n\n // Code block\n code {\n transition:\n color 250ms,\n background-color 250ms;\n }\n\n // Code block on focus/hover\n &:focus code,\n &:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Content area\n.md-content {\n flex-grow: 1;\n // Hack: we must use `overflow: hidden`, so the content area is capped by\n // the dimensions of its parent. Otherwise, long code blocks might lead to\n // a wider content area which will break everything. This, however, induces\n // margin collapse, which will break scroll margins. Adding a large enough\n // scroll padding seems to do the trick, at least in Chrome and Firefox.\n overflow: hidden;\n scroll-padding-top: px2rem(1024px);\n\n // Content wrapper\n &__inner {\n margin: 0 px2rem(16px) px2rem(24px);\n padding-top: px2rem(12px);\n\n // [screen +]: Adjust spacing between content area and sidebars\n @include break-from-device(screen) {\n\n // Sidebar with navigation is visible\n .md-sidebar--primary:not([hidden]) ~ .md-content > & {\n margin-left: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(24px);\n margin-left: px2rem(16px);\n }\n }\n\n // Sidebar with table of contents is visible\n .md-sidebar--secondary:not([hidden]) ~ .md-content > & {\n margin-right: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(16px);\n margin-left: px2rem(24px);\n }\n }\n }\n\n // Hack: add pseudo element for spacing, as the overflow of the content\n // container may not be hidden due to an imminent offset error on targets\n &::before {\n display: block;\n height: px2rem(8px);\n content: \"\";\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0;\n }\n }\n\n // Button inside of the content area - these buttons are meant for actions on\n // a document-level, i.e. linking to related source code files, printing etc.\n &__button {\n float: right;\n margin: px2rem(8px) 0;\n margin-left: px2rem(8px);\n padding: 0;\n\n // [print]: Hide buttons\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(8px);\n margin-left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // Adjust default link color for icons\n .md-typeset & {\n color: var(--md-default-fg-color--lighter);\n }\n\n // Align with body copy located next to icon\n svg {\n display: inline;\n vertical-align: top;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Dialog\n.md-dialog {\n @include z-depth(2);\n\n position: fixed;\n right: px2rem(16px);\n bottom: px2rem(16px);\n left: initial;\n z-index: 2;\n min-width: px2rem(222px);\n padding: px2rem(8px) px2rem(12px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n transform: translateY(100%);\n opacity: 0;\n transition:\n transform 0ms 400ms,\n opacity 400ms;\n pointer-events: none;\n\n // [print]: Hide dialog\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n\n // Dialog in open state\n &[data-md-state=\"open\"] {\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1),\n opacity 400ms;\n pointer-events: initial;\n }\n\n // Dialog wrapper\n &__inner {\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Form button\n .md-button {\n display: inline-block;\n padding: px2em(10px) px2em(32px);\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: px2rem(2px) solid currentColor;\n border-radius: px2rem(2px);\n transition:\n color 125ms,\n background-color 125ms,\n border-color 125ms;\n\n // Primary button\n &--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n }\n }\n\n // Form input\n .md-input {\n height: px2rem(36px);\n padding: 0 px2rem(12px);\n font-size: px2rem(16px);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);\n transition: box-shadow 250ms;\n\n // Input on focus/hover\n &:focus,\n &:hover {\n box-shadow:\n 0 px2rem(8px) px2rem(20px) hsla(0, 0%, 0%, 0.15),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.15);\n }\n\n // Stretch to full width\n &--stretch {\n width: 100%;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Header - by default, the header will be sticky and stay always on top of the\n// viewport. If this behavior is not desired, just set `position: static`.\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 2;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n // Hack: reduce jitter by adding a transparent box shadow of the same size\n // so the size of the layer doesn't change during animation\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0);\n\n // [print]: Hide header\n @media print {\n display: none;\n }\n\n // Header in shadow state, i.e. shadow is visible\n &[data-md-state=\"shadow\"] {\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0.1),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0.2);\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n box-shadow 250ms;\n }\n\n // Header in hidden state, i.e. moved out of sight\n &[data-md-state=\"hidden\"] {\n transform: translateY(-100%);\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n box-shadow 250ms;\n }\n\n // Link or button on focus\n .focus-visible {\n outline-color: currentColor;\n }\n\n // Header wrapper\n &__inner {\n display: flex;\n align-items: center;\n padding: 0 px2rem(4px);\n }\n\n // Header button\n &__button {\n position: relative;\n z-index: 1;\n margin: px2rem(4px);\n padding: px2rem(8px);\n color: currentColor;\n vertical-align: middle;\n cursor: pointer;\n transition: opacity 250ms;\n\n // Button on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Header button is visible\n &:not([hidden]) {\n display: inline-block;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // [tablet -]: Hide button\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n }\n\n // Button for search\n &[for=\"__search\"] {\n\n // [tablet landscape +]: Hide button\n @include break-from-device(tablet landscape) {\n display: none;\n }\n\n // [no-js]: Hide button\n .no-js & {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Button for drawer\n &[for=\"__drawer\"] {\n\n // [screen +]: Hide button\n @include break-from-device(screen) {\n display: none;\n }\n }\n }\n\n // Header topic\n &__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n\n // Second header topic - title of the current page\n & + & {\n z-index: -1;\n transform: translateX(px2rem(25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-25px));\n }\n }\n }\n\n // Header title\n &__title {\n flex-grow: 1;\n height: px2rem(48px);\n margin-right: px2rem(8px);\n margin-left: px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n\n // Header title in active state, i.e. page title is visible\n &[data-md-state=\"active\"] .md-header__topic {\n z-index: -1;\n transform: translateX(px2rem(-25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(25px));\n }\n\n // Second header topic - title of the current page\n + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: initial;\n }\n }\n\n // Add ellipsis in case of overflowing text\n > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n }\n }\n\n // Header option\n &__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition:\n max-width 0ms 250ms,\n opacity 250ms 250ms;\n\n // Hide toggle when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-width: 0;\n opacity: 0;\n transition:\n max-width 0ms,\n opacity 0ms;\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n\n // [tablet landscape +]: Show repository information\n @include break-from-device(tablet landscape) {\n display: block;\n width: px2rem(234px);\n max-width: px2rem(234px);\n margin-left: px2rem(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(20px);\n margin-left: initial;\n }\n }\n\n // [screen +]: Adjust spacing of search bar\n @include break-from-device(screen) {\n margin-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Footer\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide footer\n @media print {\n display: none;\n }\n\n // Footer wrapper\n &__inner {\n padding: px2rem(4px);\n overflow: auto;\n }\n\n // Footer link to previous and next page\n &__link {\n display: flex;\n padding-top: px2rem(28px);\n padding-bottom: px2rem(8px);\n transition: opacity 250ms;\n\n // [tablet +]: Adjust width to 50/50\n @include break-from-device(tablet) {\n width: 50%;\n }\n\n // Footer link on focus/hover\n &:focus,\n &:hover {\n opacity: 0.7;\n }\n\n // Footer link to previous page\n &--prev {\n float: left;\n\n // [mobile -]: Adjust width to 25/75 and hide title\n @include break-to-device(mobile) {\n width: 25%;\n\n // Hide footer title\n .md-footer__title {\n display: none;\n }\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Footer link to next page\n &--next {\n float: right;\n text-align: right;\n\n // [mobile -]: Adjust width to 25/75\n @include break-to-device(mobile) {\n width: 75%;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n text-align: left;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n\n // Footer title\n &__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - #{px2rem(48px)});\n padding: 0 px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n }\n\n // Footer link button\n &__button {\n margin: px2rem(4px);\n padding: px2rem(8px);\n }\n\n // Footer link direction (i.e. prev and next)\n &__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: px2rem(-20px);\n padding: 0 px2rem(20px);\n font-size: px2rem(12.8px);\n opacity: 0.7;\n }\n}\n\n// Footer metadata\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n\n // Footer metadata wrapper\n &__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: px2rem(4px);\n }\n\n // Lighten color for non-hovered text links\n html &.md-typeset a {\n color: var(--md-footer-fg-color--light);\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-footer-fg-color);\n }\n }\n}\n\n// Footer copyright and theme information\n.md-footer-copyright {\n width: 100%;\n margin: auto px2rem(12px);\n padding: px2rem(8px) 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: px2rem(12.8px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n width: auto;\n }\n\n // Footer copyright highlight - this is the upper part of the copyright and\n // theme information, which will include a darker color than the theme link\n &__highlight {\n color: var(--md-footer-fg-color--light);\n }\n}\n\n// Footer social links\n.md-footer-social {\n margin: 0 px2rem(8px);\n padding: px2rem(4px) 0 px2rem(12px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n padding: px2rem(12px) 0;\n }\n\n // Footer social link\n &__link {\n display: inline-block;\n width: px2rem(32px);\n height: px2rem(32px);\n text-align: center;\n\n // Adjust line-height to match height for correct alignment\n &::before {\n line-height: 1.9;\n }\n\n // Fill icon with current color\n svg {\n max-height: px2rem(16px);\n vertical-align: -25%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Navigation\n.md-nav {\n font-size: px2rem(14px);\n line-height: 1.3;\n\n // Navigation title\n &__title {\n display: block;\n padding: 0 px2rem(12px);\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n\n // Navigaton button\n .md-nav__button {\n display: none;\n\n // Stretch images based on height, as it's the smaller dimension\n img {\n width: auto;\n height: 100%;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(48px);\n height: px2rem(48px);\n fill: currentColor;\n }\n }\n }\n }\n\n // Navigation list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Navigation item\n &__item {\n padding: 0 px2rem(12px);\n\n // Navigation item on level 2\n & & {\n padding-right: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: 0;\n }\n }\n }\n\n // Navigation link\n &__link {\n display: block;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n\n // Link in blurred state\n &[data-md-state=\"blur\"] {\n color: var(--md-default-fg-color--light);\n }\n\n // Active link\n .md-nav__item &--active {\n color: var(--md-typeset-a-color);\n }\n\n // Navigation link in nested list\n .md-nav__item--nested > & {\n color: inherit;\n }\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Navigation link to table of contents\n .md-nav--primary &[for=\"__toc\"] {\n display: none;\n\n // Table of contents icon\n .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n }\n\n // Hide table of contents\n ~ .md-nav {\n display: none;\n }\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n }\n\n // [tablet -]: Layered navigation\n @include break-to-device(tablet) {\n\n // Primary and nested navigation\n &--primary,\n &--primary & {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n\n // Primary navigation\n &--primary {\n\n // Navigation title and item\n .md-nav__title,\n .md-nav__item {\n font-size: px2rem(16px);\n line-height: 1.5;\n }\n\n // Navigation title\n .md-nav__title {\n position: relative;\n height: px2rem(112px);\n padding: px2rem(60px) px2rem(16px) px2rem(4px);\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: px2rem(48px);\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: px2rem(8px);\n left: px2rem(8px);\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(4px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(8px);\n left: initial;\n }\n\n // Navigation icon in link to previous level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n\n // Navigation list\n ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow:\n 0 px2rem(1px) 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Omit border on first child\n > :first-child {\n border-top: 0;\n }\n }\n\n // Top-level navigation title\n &[for=\"__drawer\"] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n\n // Button with logo, pointing to `config.site_url`\n .md-logo {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(4px);\n display: block;\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(4px);\n left: initial;\n }\n }\n }\n\n // Navigation list\n .md-nav__list {\n flex: 1;\n }\n\n // Navigation item\n .md-nav__item {\n padding: 0;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n\n // Navigation link in nested navigation\n &--nested > .md-nav__link {\n padding-right: px2rem(48px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n }\n\n // Navigation link in active navigation\n &--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n\n // Navigation link\n .md-nav__link {\n position: relative;\n margin-top: 0;\n padding: px2rem(12px) px2rem(16px);\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: 50%;\n right: px2rem(12px);\n width: px2rem(24px);\n height: px2rem(24px);\n margin-top: px2rem(-12px);\n color: inherit;\n font-size: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(12px);\n }\n\n // Navigation icon in link to next level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n }\n\n // Flip icon vertically\n .md-nav__icon {\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] &::after {\n transform: scale(-1);\n }\n }\n\n // Table of contents contained in primary navigation\n .md-nav--secondary {\n\n // Navigation link - omit unnecessary layering\n .md-nav__link {\n position: static;\n }\n\n // Navigation on level 2-6\n .md-nav {\n position: static;\n background-color: transparent;\n\n // Navigation link on level 3\n .md-nav__link {\n padding-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(28px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 4\n .md-nav .md-nav__link {\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 5\n .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(52px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(52px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 6\n .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(64px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(64px);\n padding-left: initial;\n }\n }\n }\n }\n }\n\n // Table of contents\n &--secondary {\n background-color: transparent;\n }\n\n // Toggle for nested navigation\n &__toggle ~ & {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n opacity 125ms 50ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(-100%);\n }\n }\n\n // Show nested navigation when toggle is active\n &__toggle:checked ~ & {\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 125ms 125ms;\n\n // Navigation list\n > .md-nav__list {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n }\n }\n }\n\n // [tablet portrait -]: Layered navigation with table of contents\n @include break-to-device(tablet portrait) {\n\n // Show link to table of contents\n &--primary &__link[for=\"__toc\"] {\n display: block;\n padding-right: px2rem(48px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n\n // Repository information container\n &__source {\n display: block;\n padding: 0 px2rem(4px);\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n }\n\n // [tablet landscape]: Layered navigation with table of contents\n @include break-at-device(tablet landscape) {\n\n // Show link to integrated table of contents\n &--integrated &__link[for=\"__toc\"] {\n display: block;\n padding-right: px2rem(48px);\n scroll-snap-align: initial;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n }\n\n // [tablet landscape +]: Tree-like table of contents\n @include break-from-device(tablet landscape) {\n\n // Navigation title\n &--secondary &__title {\n\n // Adjust snapping behavior\n &[for=\"__toc\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n }\n\n // [screen +]: Tree-like navigation\n @include break-from-device(screen) {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n\n // Navigation title\n &--primary &__title {\n\n // Adjust snapping behavior\n &[for=\"__drawer\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n\n // Hide toggle for nested navigation\n &__toggle ~ & {\n display: none;\n }\n\n // Show nested navigation when toggle is active or indeterminate\n &__toggle:checked ~ &,\n &__toggle:indeterminate ~ & {\n display: block;\n }\n\n // Hide navigation title in nested navigation\n &__item--nested > & > &__title {\n display: none;\n }\n\n // Navigation section\n &__item--section {\n display: block;\n margin: 1.25em 0;\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Hide navigation link, as sections are always expanded\n > .md-nav__link {\n display: none;\n }\n\n // Navigation\n > .md-nav {\n display: block;\n\n // Navigation title\n > .md-nav__title {\n display: block;\n padding: 0;\n pointer-events: none;\n scroll-snap-align: start;\n }\n\n // Adjust spacing on next level item\n > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n }\n }\n\n // Navigation icon\n &__icon {\n float: right;\n width: px2rem(18px);\n height: px2rem(18px);\n transition: transform 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n transform: rotate(180deg);\n }\n\n // Navigation icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: px2rem(-2px);\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Navigation icon - rotate icon when toggle is active or indeterminate\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link &,\n .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link & {\n transform: rotate(90deg);\n }\n }\n\n // Modifier for when navigation tabs are rendered\n &--lifted {\n\n // Hide nested items on level 1 and site title\n > .md-nav__list > .md-nav__item--nested,\n > .md-nav__title {\n display: none;\n }\n\n // Hide level 1 items\n > .md-nav__list > .md-nav__item {\n display: none;\n\n // Active parent navigation item\n &--active {\n display: block;\n padding: 0;\n\n // Hide nested links\n > .md-nav__link {\n display: none;\n }\n\n // Show title and adjust spacing\n > .md-nav > .md-nav__title {\n display: block;\n padding: 0 px2rem(12px);\n pointer-events: none;\n scroll-snap-align: start;\n }\n }\n\n // Adjust spacing for navigation item on level 2\n > .md-nav__item {\n padding-right: px2rem(12px);\n }\n }\n\n // Hack: Always show active navigation tab on breakpoint screen, despite\n // of checkbox being checked or not. Fixes #1655.\n .md-nav[data-md-level=\"1\"] {\n display: block;\n }\n }\n\n // Modifier for when table of contents is rendered in primary navigation\n &--integrated &__link[for=\"__toc\"] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: px2rem(1px) solid var(--md-primary-fg-color);\n\n // Hide navigation title\n > .md-nav__title {\n display: none;\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Search\n.md-search {\n position: relative;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding: px2rem(4px) 0;\n }\n\n // [no-js]: Hide search\n .no-js & {\n display: none;\n }\n\n // Search overlay\n &__overlay {\n z-index: 1;\n opacity: 0;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(-44px);\n width: px2rem(40px);\n height: px2rem(40px);\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(20px);\n transform-origin: center;\n transition:\n transform 300ms 100ms,\n opacity 200ms 200ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-44px);\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n opacity: 1;\n transition:\n transform 400ms,\n opacity 100ms;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n cursor: pointer;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n width: 100%;\n // Hack: when the header is translated upon scrolling, a new layer is\n // induced, which means that the height will now refer to the height of\n // the header, albeit positioning is fixed. This should be mitigated\n // in all cases when setting the height to 2x the viewport.\n height: 200vh;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [mobile portrait -]: Scale up 45 times\n @include break-to-device(mobile portrait) {\n transform: scale(45);\n }\n\n // [mobile landscape]: Scale up 60 times\n @include break-at-device(mobile landscape) {\n transform: scale(60);\n }\n\n // [tablet portrait]: Scale up 75 times\n @include break-at-device(tablet portrait) {\n transform: scale(75);\n }\n }\n }\n\n // Search wrapper\n &__inner {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: fixed;\n top: 0;\n left: 100%;\n z-index: 2;\n width: 100%;\n height: 100%;\n transform: translateX(5%);\n opacity: 0;\n transition:\n right 0ms 300ms,\n left 0ms 300ms,\n transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 150ms 150ms;\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n left: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n right 0ms 0ms,\n left 0ms 0ms,\n transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms 150ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n }\n\n // Adjust for right-to-left languages\n html [dir=\"rtl\"] & {\n right: 100%;\n left: initial;\n transform: translateX(-5%);\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: relative;\n float: right;\n width: px2rem(234px);\n padding: px2rem(2px) 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [tablet landscape]: Omit overlaying header title\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Match width of content area\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n }\n }\n\n // Search form\n &__form {\n position: relative;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n border-radius: px2rem(2px);\n }\n }\n\n // Search input\n &__input {\n position: relative;\n z-index: 2;\n padding: 0 px2rem(44px) 0 px2rem(72px);\n text-overflow: ellipsis;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 px2rem(12px) transparent;\n transition:\n color 250ms,\n background-color 250ms,\n box-shadow 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0 px2rem(72px) 0 px2rem(44px);\n }\n\n // Search placeholder\n &::placeholder {\n transition: color 250ms;\n }\n\n // Search icon and placeholder\n ~ .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n\n // Remove the \"x\" rendered by Internet Explorer\n &::-ms-clear {\n display: none;\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n box-shadow: 0 0 px2rem(12px) hsla(0, 0%, 0%, 0.07);\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n width: 100%;\n height: px2rem(48px);\n font-size: px2rem(18px);\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n width: 100%;\n height: px2rem(36px);\n padding-left: px2rem(44px);\n color: inherit;\n font-size: px2rem(16px);\n background-color: hsla(0, 0%, 0%, 0.26);\n border-radius: px2rem(2px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n }\n\n // Search icon\n + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n\n // Search placeholder\n &::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n\n // Search input on hover\n &:hover {\n background-color: hsla(0, 0%, 100%, 0.12);\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n color: var(--md-default-fg-color);\n text-overflow: clip;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px) px2rem(2px) 0 0;\n\n // Search icon and placeholder\n + .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n }\n }\n }\n\n // Search icon\n &__icon {\n position: absolute;\n z-index: 2;\n width: px2rem(24px);\n height: px2rem(24px);\n cursor: pointer;\n transition:\n color 250ms,\n opacity 250ms;\n\n // Search icon on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Search focus button\n &[for=\"__search\"] {\n top: px2rem(6px);\n left: px2rem(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(10px);\n left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(16px);\n left: initial;\n }\n\n // Hide the magnifying glass\n svg:first-child {\n display: none;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n pointer-events: none;\n\n // Hide the back arrow\n svg:last-child {\n display: none;\n }\n }\n }\n\n // Search reset button\n &[type=\"reset\"] {\n top: px2rem(6px);\n right: px2rem(10px);\n transform: scale(0.75);\n opacity: 0;\n transition:\n transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(10px);\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n right: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n }\n\n // Show reset button when search is active and input non-empty\n [data-md-toggle=\"search\"]:checked ~ .md-header\n .md-search__input:valid ~ & {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n\n // Search focus icon\n &:hover {\n opacity: 0.7;\n }\n }\n }\n }\n\n // Search output\n &__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 px2rem(2px) px2rem(2px);\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(48px);\n bottom: 0;\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n top: px2rem(38px);\n opacity: 0;\n transition: opacity 400ms;\n\n // Show output when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n @include z-depth(6);\n\n opacity: 1;\n }\n }\n }\n\n // Search scroll wrapper\n &__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 88+ has weird overscroll behavior. Overall, scroll snapping\n // seems to be something that is not ready for prime time on some browsers.\n // scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Mitigiate excessive repaints on non-retina devices\n @media (max-resolution: 1dppx) {\n transform: translateZ(0);\n }\n\n // [tablet landscape]: Set fixed width to omit unnecessary reflow\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Set fixed width to omit unnecessary reflow\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n\n // [tablet landscape +]: Limit height to viewport\n @include break-from-device(tablet landscape) {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Show scroll wrapper when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-height: 75vh;\n }\n\n // Search scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n}\n\n// Search result\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n\n // Search result metadata\n &__meta {\n padding: 0 px2rem(16px);\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: px2rem(36px);\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: initial;\n }\n }\n }\n\n // Search result list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Search result item\n &__item {\n box-shadow: 0 px2rem(-1px) 0 var(--md-default-fg-color--lightest);\n\n // Omit border on first child\n &:first-child {\n box-shadow: none;\n }\n }\n\n // Search result link\n &__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n\n // Search result link on focus/hover\n &:focus,\n &:hover {\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Adjust spacing on last child of last link\n &:last-child p:last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Search result more link\n &__more summary {\n display: block;\n padding: px2em(12px) px2rem(16px);\n color: var(--md-typeset-a-color);\n font-size: px2rem(12.8px);\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result more link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n\n // Adjust transparency of less relevant results\n ~ * > * {\n opacity: 0.65;\n }\n }\n\n // Search result article\n &__article {\n position: relative;\n padding: 0 px2rem(16px);\n overflow: hidden;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result article document\n &--document {\n\n // Search result title\n .md-search-result__title {\n margin: px2rem(11px) 0;\n font-weight: 400;\n font-size: px2rem(16px);\n line-height: 1.4;\n }\n }\n }\n\n // Search result icon\n &__icon {\n position: absolute;\n left: 0;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(10px);\n color: var(--md-default-fg-color--light);\n\n // [tablet portrait -]: Hide icon\n @include break-to-device(tablet portrait) {\n display: none;\n }\n\n // Search result icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n\n // Flip icon vertically\n &::after {\n transform: scaleX(-1);\n }\n }\n }\n\n // Search result title\n &__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: px2rem(12.8px);\n line-height: 1.6;\n }\n\n // Search result teaser\n &__teaser {\n display: -webkit-box;\n max-height: px2rem(40px);\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n\n // [mobile -]: Adjust number of lines\n @include break-to-device(mobile) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // [tablet landscape]: Adjust number of lines\n @include break-at-device(tablet landscape) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // Search term highlighting\n mark {\n text-decoration: underline;\n background-color: transparent;\n }\n }\n\n // Search result terms\n &__terms {\n margin: 0.5em 0;\n font-size: px2rem(12.8px);\n font-style: italic;\n }\n\n // Search term highlighting\n mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Selection\n.md-select {\n position: relative;\n z-index: 1;\n\n // Selection bubble\n &__inner {\n position: absolute;\n top: calc(100% - #{px2rem(4px)});\n left: 50%;\n max-height: 0;\n margin-top: px2rem(4px);\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n transform: translate3d(-50%, px2rem(6px), 0);\n opacity: 0;\n transition:\n transform 250ms 375ms,\n opacity 250ms 250ms,\n max-height 0ms 500ms;\n\n // Selection bubble on parent focus/hover\n .md-select:focus-within &,\n .md-select:hover & {\n max-height: px2rem(200px);\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms,\n max-height 0ms;\n }\n\n // Selection bubble handle\n &::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: px2rem(-4px);\n margin-left: px2rem(-4px);\n border: px2rem(4px) solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n }\n }\n\n // Selection list\n &__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: px2rem(16px);\n list-style-type: none;\n border-radius: px2rem(2px);\n }\n\n // Selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n outline: none;\n cursor: pointer;\n transition:\n background-color 250ms,\n color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Sidebar\n.md-sidebar {\n position: sticky;\n top: px2rem(48px);\n flex-shrink: 0;\n align-self: flex-start;\n width: px2rem(242px);\n padding: px2rem(24px) 0;\n\n // [print]: Hide sidebar\n @media print {\n display: none;\n }\n\n // [tablet -]: Show navigation as drawer\n @include break-to-device(tablet) {\n\n // Primary sidebar with navigation\n &--primary {\n position: fixed;\n top: 0;\n left: px2rem(-242px);\n z-index: 3;\n display: block;\n width: px2rem(242px);\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-242px);\n left: initial;\n }\n\n // Show sidebar when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ .md-container & {\n @include z-depth(8);\n\n transform: translateX(px2rem(242px));\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-242px));\n }\n }\n\n // Stretch scroll wrapper for primary sidebar\n .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n }\n }\n\n // [screen +]: Show navigation as sidebar\n @include break-from-device(screen) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n }\n\n // Secondary sidebar with table of contents\n &--secondary {\n display: none;\n order: 2;\n\n // [tablet landscape +]: Show table of contents as sidebar\n @include break-from-device(tablet landscape) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n\n // Sidebar is visible\n &:not([hidden]) {\n display: block;\n }\n\n // Ensure smooth scrolling on iOS\n .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n }\n }\n\n // Sidebar scroll wrapper\n &__scrollwrap {\n margin: 0 px2rem(4px);\n overflow-y: auto;\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 81+ exhibits a strange bug, where it scrolls the container\n // to the bottom if `scroll-snap-type` is set on the initial render. For\n // this reason, we disable scroll snapping until this is resolved (#1667).\n // scroll-snap-type: y mandatory;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Sidebar scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n}\n\n// [tablet -]: Show overlay on active drawer\n@include break-to-device(tablet) {\n\n // Sidebar overlay\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 3;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n opacity: 0;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Show overlay when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ & {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// Show repository facts\n@keyframes md-source__facts--done {\n 0% {\n height: 0;\n }\n\n 100% {\n height: px2rem(13px);\n }\n}\n\n// Show repository fact\n@keyframes md-source__fact--done {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n\n 50% {\n opacity: 0;\n }\n\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Repository information\n.md-source {\n display: block;\n font-size: px2rem(13px);\n line-height: 1.2;\n white-space: nowrap;\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n transition: opacity 250ms;\n\n // Repository information on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Repository icon\n &__icon {\n display: inline-block;\n width: px2rem(40px);\n height: px2rem(48px);\n vertical-align: middle;\n\n // Align with margin only (as opposed to normal button alignment)\n svg {\n margin-top: px2rem(12px);\n margin-left: px2rem(12px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(12px);\n margin-left: initial;\n }\n }\n\n // Adjust spacing if icon is present\n + .md-source__repository {\n margin-left: px2rem(-40px);\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(-40px);\n margin-left: initial;\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n }\n\n // Repository name\n &__repository {\n display: inline-block;\n max-width: calc(100% - #{px2rem(24px)});\n margin-left: px2rem(12px);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n }\n\n // Repository facts\n &__facts {\n margin: px2rem(2px) 0 0;\n padding: 0;\n overflow: hidden;\n font-size: px2rem(11px);\n list-style-type: none;\n opacity: 0.75;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: md-source__facts--done 250ms ease-in;\n }\n }\n\n // Repository fact\n &__fact {\n display: inline-block;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: md-source__fact--done 400ms ease-out;\n }\n\n // Repository fact icon\n &::before {\n display: inline-block;\n width: px2rem(12px);\n height: px2rem(12px);\n margin-right: px2rem(2px);\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Adjust spacing for repository fact icon\n &:nth-child(1n+2)::before {\n margin-left: px2rem(8px);\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: initial;\n margin-left: px2rem(2px);\n\n // Adjust spacing for repository fact icon\n &:nth-child(1n+2)::before {\n margin-right: px2rem(8px);\n margin-left: initial;\n }\n }\n\n // Repository fact: version\n &--version::before {\n mask-image: var(--md-source-version-icon);\n }\n\n // Repository fact: stars\n &--stars::before {\n mask-image: var(--md-source-stars-icon);\n }\n\n // Repository fact: forks\n &--forks::before {\n mask-image: var(--md-source-forks-icon);\n }\n\n // Repository fact: repositories\n &--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Navigation tabs\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n\n // [print]: Hide tabs\n @media print {\n display: none;\n }\n\n // [tablet -]: Hide tabs\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Tabs in hidden state, i.e. when scrolling down\n &[data-md-state=\"hidden\"] {\n pointer-events: none;\n }\n\n // Navigation tabs list\n &__list {\n margin: 0;\n margin-left: px2rem(4px);\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(4px);\n margin-left: initial;\n }\n }\n\n // Navigation tabs item\n &__item {\n display: inline-block;\n height: px2rem(48px);\n padding-right: px2rem(12px);\n padding-left: px2rem(12px);\n }\n\n // Navigation tabs link - could be defined as block elements and aligned via\n // line height, but this would imply more repaints when scrolling\n &__link {\n display: block;\n margin-top: px2rem(16px);\n font-size: px2rem(14px);\n // Hack: save a repaint when tabs are appearing on scrolling up\n backface-visibility: hidden;\n opacity: 0.7;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms;\n\n // Active link and link on focus/hover\n &--active,\n &:focus,\n &:hover {\n color: inherit;\n opacity: 1;\n }\n\n // Delay transitions by a small amount\n @for $i from 2 through 16 {\n .md-tabs__item:nth-child(#{$i}) & {\n transition-delay: 20ms * ($i - 1);\n }\n }\n\n // Hide tabs upon scrolling - disable transition to minimizes repaints\n // while scrolling down, while scrolling up seems to be okay\n .md-tabs[data-md-state=\"hidden\"] & {\n transform: translateY(50%);\n opacity: 0;\n transition:\n transform 0ms 100ms,\n opacity 100ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Back-to-top button\n.md-top {\n position: sticky;\n bottom: px2rem(8px);\n z-index: 1;\n float: right;\n margin: px2rem(-56px) px2rem(8px) px2rem(8px);\n padding: px2rem(8px);\n color: var(--md-primary-bg-color);\n background: var(--md-primary-fg-color);\n border-radius: 100%;\n outline: none;\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);\n transform: translateY(0);\n transition:\n opacity 125ms,\n transform 125ms cubic-bezier(0.4, 0, 0.2, 1),\n background-color 125ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n\n // Back-to-top button in hidden state\n &[data-md-state=\"hidden\"] {\n transform: translateY(px2rem(-4px));\n opacity: 0;\n pointer-events: none;\n }\n\n // Back-to-top button on focus/hover\n &:focus,\n &:hover {\n background: var(--md-accent-fg-color);\n transform: scale(1.1);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Version selection\n.md-version {\n flex-shrink: 0;\n height: px2rem(48px);\n font-size: px2rem(16px);\n\n // Current selection\n &__current {\n position: relative;\n // Hack: in general, we would use `vertical-align` to align the version at\n // the bottom with the title, but since the list uses absolute positioning,\n // this won't work consistently. Furthermore, we would need to use inline\n // positioning to align the links, which looks jagged.\n top: px2rem(1px);\n margin-right: px2rem(8px);\n margin-left: px2rem(28px);\n color: inherit;\n outline: none;\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n margin-left: px2rem(8px);\n }\n\n // Version selection icon\n &::after {\n display: inline-block;\n width: px2rem(8px);\n height: px2rem(12px);\n margin-left: px2rem(8px);\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(8px);\n margin-left: initial;\n }\n }\n }\n\n // Version selection list\n &__list {\n position: absolute;\n top: px2rem(3px);\n z-index: 1;\n max-height: 0;\n margin: px2rem(4px) px2rem(16px);\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n opacity: 0;\n transition:\n max-height 0ms 500ms,\n opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n\n // Version selection list on parent focus/hover\n .md-version:focus-within &,\n .md-version:hover & {\n max-height: px2rem(200px);\n opacity: 1;\n transition:\n max-height 0ms,\n opacity 250ms;\n }\n }\n\n // Version selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Version selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n/// Admonition flavours\n$admonitions: (\n note: pencil $clr-blue-a200,\n abstract summary tldr: text-subject $clr-light-blue-a400,\n info todo: information $clr-cyan-a700,\n tip hint important: fire $clr-teal-a700,\n success check done: check-circle $clr-green-a700,\n question help faq: help-circle $clr-light-green-a700,\n warning caution attention: alert $clr-orange-a400,\n failure fail missing: close-circle $clr-red-a200,\n danger error: flash-circle $clr-red-a400,\n bug: bug $clr-pink-a400,\n example: format-list-numbered $clr-deep-purple-a200,\n quote cite: format-quote-close $clr-grey\n) !default;\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n @each $names, $props in $admonitions {\n --md-admonition-icon--#{nth($names, 1)}:\n svg-load(\"material/#{nth($props, 1)}.svg\");\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Admonition\n .admonition {\n margin: px2em(20px, 12.8px) 0;\n padding: 0 px2rem(12px);\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: px2rem(12.8px);\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: px2rem(4px) solid $clr-blue-a200;\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.05);\n\n // [print]: Omit shadow as it may lead to rendering errors\n @media print {\n box-shadow: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust vertical spacing for nested admonitions\n .admonition {\n margin-top: 1em;\n margin-bottom: 1em;\n }\n\n // Adjust spacing for contained table wrappers\n .md-typeset__scrollwrap {\n margin: 1em px2rem(-12px);\n }\n\n // Adjust spacing for contained tables\n .md-typeset__table {\n padding: 0 px2rem(12px);\n }\n\n // Adjust spacing for single-child tabbed block container\n > .tabbed-set:only-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n html & > :last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Admonition title\n .admonition-title {\n position: relative;\n margin: 0 px2rem(-12px) 0 px2rem(-16px);\n padding: px2rem(8px) px2rem(12px) px2rem(8px) px2rem(40px);\n font-weight: 700;\n background-color: transparentize($clr-blue-a200, 0.9);\n border-left: px2rem(4px) solid $clr-blue-a200;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin: 0 px2rem(-16px) 0 px2rem(-12px);\n padding: px2rem(8px) px2rem(40px) px2rem(8px) px2rem(12px);\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust spacing for title-only admonitions\n html &:last-child {\n margin-bottom: 0;\n }\n\n // Admonition icon\n &::before {\n position: absolute;\n left: px2rem(12px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: $clr-blue-a200;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(12px);\n left: initial;\n }\n }\n\n // Adjust spacing on last tabbed block container child - if the tabbed\n // block container is the sole child, it looks better to omit the margin\n + .tabbed-set:last-child {\n margin-top: 0;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: flavours\n// ----------------------------------------------------------------------------\n\n@each $names, $props in $admonitions {\n $name: nth($names, 1);\n $tint: nth($props, 2);\n\n // Admonition flavour\n .md-typeset .admonition.#{$name} {\n border-color: $tint;\n }\n\n // Admonition flavour title\n .md-typeset .#{$name} > .admonition-title {\n background-color: transparentize($tint, 0.9);\n border-color: $tint;\n\n // Admonition icon\n &::before {\n background-color: $tint;\n mask-image: var(--md-admonition-icon--#{$name});\n mask-repeat: no-repeat;\n mask-size: contain;\n }\n }\n\n // Define synonyms for flavours\n @if length($names) > 1 {\n @for $n from 2 through length($names) {\n .#{nth($names, $n)} {\n @extend .#{$name};\n }\n }\n }\n}\n","// ==========================================================================\n//\n// Name: UI Color Palette\n// Description: The color palette of material design.\n// Version: 2.3.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-color\n//\n// twitter: @mrmlnc\n//\n// ==========================================================================\n\n\n//\n// List of base colors\n//\n\n// $clr-red\n// $clr-pink\n// $clr-purple\n// $clr-deep-purple\n// $clr-indigo\n// $clr-blue\n// $clr-light-blue\n// $clr-cyan\n// $clr-teal\n// $clr-green\n// $clr-light-green\n// $clr-lime\n// $clr-yellow\n// $clr-amber\n// $clr-orange\n// $clr-deep-orange\n// $clr-brown\n// $clr-grey\n// $clr-blue-grey\n// $clr-black\n// $clr-white\n\n\n//\n// Red\n//\n\n$clr-red-list: (\n \"base\": #f44336,\n \"50\": #ffebee,\n \"100\": #ffcdd2,\n \"200\": #ef9a9a,\n \"300\": #e57373,\n \"400\": #ef5350,\n \"500\": #f44336,\n \"600\": #e53935,\n \"700\": #d32f2f,\n \"800\": #c62828,\n \"900\": #b71c1c,\n \"a100\": #ff8a80,\n \"a200\": #ff5252,\n \"a400\": #ff1744,\n \"a700\": #d50000\n);\n\n$clr-red: map-get($clr-red-list, \"base\");\n\n$clr-red-50: map-get($clr-red-list, \"50\");\n$clr-red-100: map-get($clr-red-list, \"100\");\n$clr-red-200: map-get($clr-red-list, \"200\");\n$clr-red-300: map-get($clr-red-list, \"300\");\n$clr-red-400: map-get($clr-red-list, \"400\");\n$clr-red-500: map-get($clr-red-list, \"500\");\n$clr-red-600: map-get($clr-red-list, \"600\");\n$clr-red-700: map-get($clr-red-list, \"700\");\n$clr-red-800: map-get($clr-red-list, \"800\");\n$clr-red-900: map-get($clr-red-list, \"900\");\n$clr-red-a100: map-get($clr-red-list, \"a100\");\n$clr-red-a200: map-get($clr-red-list, \"a200\");\n$clr-red-a400: map-get($clr-red-list, \"a400\");\n$clr-red-a700: map-get($clr-red-list, \"a700\");\n\n\n//\n// Pink\n//\n\n$clr-pink-list: (\n \"base\": #e91e63,\n \"50\": #fce4ec,\n \"100\": #f8bbd0,\n \"200\": #f48fb1,\n \"300\": #f06292,\n \"400\": #ec407a,\n \"500\": #e91e63,\n \"600\": #d81b60,\n \"700\": #c2185b,\n \"800\": #ad1457,\n \"900\": #880e4f,\n \"a100\": #ff80ab,\n \"a200\": #ff4081,\n \"a400\": #f50057,\n \"a700\": #c51162\n);\n\n$clr-pink: map-get($clr-pink-list, \"base\");\n\n$clr-pink-50: map-get($clr-pink-list, \"50\");\n$clr-pink-100: map-get($clr-pink-list, \"100\");\n$clr-pink-200: map-get($clr-pink-list, \"200\");\n$clr-pink-300: map-get($clr-pink-list, \"300\");\n$clr-pink-400: map-get($clr-pink-list, \"400\");\n$clr-pink-500: map-get($clr-pink-list, \"500\");\n$clr-pink-600: map-get($clr-pink-list, \"600\");\n$clr-pink-700: map-get($clr-pink-list, \"700\");\n$clr-pink-800: map-get($clr-pink-list, \"800\");\n$clr-pink-900: map-get($clr-pink-list, \"900\");\n$clr-pink-a100: map-get($clr-pink-list, \"a100\");\n$clr-pink-a200: map-get($clr-pink-list, \"a200\");\n$clr-pink-a400: map-get($clr-pink-list, \"a400\");\n$clr-pink-a700: map-get($clr-pink-list, \"a700\");\n\n\n//\n// Purple\n//\n\n$clr-purple-list: (\n \"base\": #9c27b0,\n \"50\": #f3e5f5,\n \"100\": #e1bee7,\n \"200\": #ce93d8,\n \"300\": #ba68c8,\n \"400\": #ab47bc,\n \"500\": #9c27b0,\n \"600\": #8e24aa,\n \"700\": #7b1fa2,\n \"800\": #6a1b9a,\n \"900\": #4a148c,\n \"a100\": #ea80fc,\n \"a200\": #e040fb,\n \"a400\": #d500f9,\n \"a700\": #aa00ff\n);\n\n$clr-purple: map-get($clr-purple-list, \"base\");\n\n$clr-purple-50: map-get($clr-purple-list, \"50\");\n$clr-purple-100: map-get($clr-purple-list, \"100\");\n$clr-purple-200: map-get($clr-purple-list, \"200\");\n$clr-purple-300: map-get($clr-purple-list, \"300\");\n$clr-purple-400: map-get($clr-purple-list, \"400\");\n$clr-purple-500: map-get($clr-purple-list, \"500\");\n$clr-purple-600: map-get($clr-purple-list, \"600\");\n$clr-purple-700: map-get($clr-purple-list, \"700\");\n$clr-purple-800: map-get($clr-purple-list, \"800\");\n$clr-purple-900: map-get($clr-purple-list, \"900\");\n$clr-purple-a100: map-get($clr-purple-list, \"a100\");\n$clr-purple-a200: map-get($clr-purple-list, \"a200\");\n$clr-purple-a400: map-get($clr-purple-list, \"a400\");\n$clr-purple-a700: map-get($clr-purple-list, \"a700\");\n\n\n//\n// Deep purple\n//\n\n$clr-deep-purple-list: (\n \"base\": #673ab7,\n \"50\": #ede7f6,\n \"100\": #d1c4e9,\n \"200\": #b39ddb,\n \"300\": #9575cd,\n \"400\": #7e57c2,\n \"500\": #673ab7,\n \"600\": #5e35b1,\n \"700\": #512da8,\n \"800\": #4527a0,\n \"900\": #311b92,\n \"a100\": #b388ff,\n \"a200\": #7c4dff,\n \"a400\": #651fff,\n \"a700\": #6200ea\n);\n\n$clr-deep-purple: map-get($clr-deep-purple-list, \"base\");\n\n$clr-deep-purple-50: map-get($clr-deep-purple-list, \"50\");\n$clr-deep-purple-100: map-get($clr-deep-purple-list, \"100\");\n$clr-deep-purple-200: map-get($clr-deep-purple-list, \"200\");\n$clr-deep-purple-300: map-get($clr-deep-purple-list, \"300\");\n$clr-deep-purple-400: map-get($clr-deep-purple-list, \"400\");\n$clr-deep-purple-500: map-get($clr-deep-purple-list, \"500\");\n$clr-deep-purple-600: map-get($clr-deep-purple-list, \"600\");\n$clr-deep-purple-700: map-get($clr-deep-purple-list, \"700\");\n$clr-deep-purple-800: map-get($clr-deep-purple-list, \"800\");\n$clr-deep-purple-900: map-get($clr-deep-purple-list, \"900\");\n$clr-deep-purple-a100: map-get($clr-deep-purple-list, \"a100\");\n$clr-deep-purple-a200: map-get($clr-deep-purple-list, \"a200\");\n$clr-deep-purple-a400: map-get($clr-deep-purple-list, \"a400\");\n$clr-deep-purple-a700: map-get($clr-deep-purple-list, \"a700\");\n\n\n//\n// Indigo\n//\n\n$clr-indigo-list: (\n \"base\": #3f51b5,\n \"50\": #e8eaf6,\n \"100\": #c5cae9,\n \"200\": #9fa8da,\n \"300\": #7986cb,\n \"400\": #5c6bc0,\n \"500\": #3f51b5,\n \"600\": #3949ab,\n \"700\": #303f9f,\n \"800\": #283593,\n \"900\": #1a237e,\n \"a100\": #8c9eff,\n \"a200\": #536dfe,\n \"a400\": #3d5afe,\n \"a700\": #304ffe\n);\n\n$clr-indigo: map-get($clr-indigo-list, \"base\");\n\n$clr-indigo-50: map-get($clr-indigo-list, \"50\");\n$clr-indigo-100: map-get($clr-indigo-list, \"100\");\n$clr-indigo-200: map-get($clr-indigo-list, \"200\");\n$clr-indigo-300: map-get($clr-indigo-list, \"300\");\n$clr-indigo-400: map-get($clr-indigo-list, \"400\");\n$clr-indigo-500: map-get($clr-indigo-list, \"500\");\n$clr-indigo-600: map-get($clr-indigo-list, \"600\");\n$clr-indigo-700: map-get($clr-indigo-list, \"700\");\n$clr-indigo-800: map-get($clr-indigo-list, \"800\");\n$clr-indigo-900: map-get($clr-indigo-list, \"900\");\n$clr-indigo-a100: map-get($clr-indigo-list, \"a100\");\n$clr-indigo-a200: map-get($clr-indigo-list, \"a200\");\n$clr-indigo-a400: map-get($clr-indigo-list, \"a400\");\n$clr-indigo-a700: map-get($clr-indigo-list, \"a700\");\n\n\n//\n// Blue\n//\n\n$clr-blue-list: (\n \"base\": #2196f3,\n \"50\": #e3f2fd,\n \"100\": #bbdefb,\n \"200\": #90caf9,\n \"300\": #64b5f6,\n \"400\": #42a5f5,\n \"500\": #2196f3,\n \"600\": #1e88e5,\n \"700\": #1976d2,\n \"800\": #1565c0,\n \"900\": #0d47a1,\n \"a100\": #82b1ff,\n \"a200\": #448aff,\n \"a400\": #2979ff,\n \"a700\": #2962ff\n);\n\n$clr-blue: map-get($clr-blue-list, \"base\");\n\n$clr-blue-50: map-get($clr-blue-list, \"50\");\n$clr-blue-100: map-get($clr-blue-list, \"100\");\n$clr-blue-200: map-get($clr-blue-list, \"200\");\n$clr-blue-300: map-get($clr-blue-list, \"300\");\n$clr-blue-400: map-get($clr-blue-list, \"400\");\n$clr-blue-500: map-get($clr-blue-list, \"500\");\n$clr-blue-600: map-get($clr-blue-list, \"600\");\n$clr-blue-700: map-get($clr-blue-list, \"700\");\n$clr-blue-800: map-get($clr-blue-list, \"800\");\n$clr-blue-900: map-get($clr-blue-list, \"900\");\n$clr-blue-a100: map-get($clr-blue-list, \"a100\");\n$clr-blue-a200: map-get($clr-blue-list, \"a200\");\n$clr-blue-a400: map-get($clr-blue-list, \"a400\");\n$clr-blue-a700: map-get($clr-blue-list, \"a700\");\n\n\n//\n// Light Blue\n//\n\n$clr-light-blue-list: (\n \"base\": #03a9f4,\n \"50\": #e1f5fe,\n \"100\": #b3e5fc,\n \"200\": #81d4fa,\n \"300\": #4fc3f7,\n \"400\": #29b6f6,\n \"500\": #03a9f4,\n \"600\": #039be5,\n \"700\": #0288d1,\n \"800\": #0277bd,\n \"900\": #01579b,\n \"a100\": #80d8ff,\n \"a200\": #40c4ff,\n \"a400\": #00b0ff,\n \"a700\": #0091ea\n);\n\n$clr-light-blue: map-get($clr-light-blue-list, \"base\");\n\n$clr-light-blue-50: map-get($clr-light-blue-list, \"50\");\n$clr-light-blue-100: map-get($clr-light-blue-list, \"100\");\n$clr-light-blue-200: map-get($clr-light-blue-list, \"200\");\n$clr-light-blue-300: map-get($clr-light-blue-list, \"300\");\n$clr-light-blue-400: map-get($clr-light-blue-list, \"400\");\n$clr-light-blue-500: map-get($clr-light-blue-list, \"500\");\n$clr-light-blue-600: map-get($clr-light-blue-list, \"600\");\n$clr-light-blue-700: map-get($clr-light-blue-list, \"700\");\n$clr-light-blue-800: map-get($clr-light-blue-list, \"800\");\n$clr-light-blue-900: map-get($clr-light-blue-list, \"900\");\n$clr-light-blue-a100: map-get($clr-light-blue-list, \"a100\");\n$clr-light-blue-a200: map-get($clr-light-blue-list, \"a200\");\n$clr-light-blue-a400: map-get($clr-light-blue-list, \"a400\");\n$clr-light-blue-a700: map-get($clr-light-blue-list, \"a700\");\n\n\n//\n// Cyan\n//\n\n$clr-cyan-list: (\n \"base\": #00bcd4,\n \"50\": #e0f7fa,\n \"100\": #b2ebf2,\n \"200\": #80deea,\n \"300\": #4dd0e1,\n \"400\": #26c6da,\n \"500\": #00bcd4,\n \"600\": #00acc1,\n \"700\": #0097a7,\n \"800\": #00838f,\n \"900\": #006064,\n \"a100\": #84ffff,\n \"a200\": #18ffff,\n \"a400\": #00e5ff,\n \"a700\": #00b8d4\n);\n\n$clr-cyan: map-get($clr-cyan-list, \"base\");\n\n$clr-cyan-50: map-get($clr-cyan-list, \"50\");\n$clr-cyan-100: map-get($clr-cyan-list, \"100\");\n$clr-cyan-200: map-get($clr-cyan-list, \"200\");\n$clr-cyan-300: map-get($clr-cyan-list, \"300\");\n$clr-cyan-400: map-get($clr-cyan-list, \"400\");\n$clr-cyan-500: map-get($clr-cyan-list, \"500\");\n$clr-cyan-600: map-get($clr-cyan-list, \"600\");\n$clr-cyan-700: map-get($clr-cyan-list, \"700\");\n$clr-cyan-800: map-get($clr-cyan-list, \"800\");\n$clr-cyan-900: map-get($clr-cyan-list, \"900\");\n$clr-cyan-a100: map-get($clr-cyan-list, \"a100\");\n$clr-cyan-a200: map-get($clr-cyan-list, \"a200\");\n$clr-cyan-a400: map-get($clr-cyan-list, \"a400\");\n$clr-cyan-a700: map-get($clr-cyan-list, \"a700\");\n\n\n//\n// Teal\n//\n\n$clr-teal-list: (\n \"base\": #009688,\n \"50\": #e0f2f1,\n \"100\": #b2dfdb,\n \"200\": #80cbc4,\n \"300\": #4db6ac,\n \"400\": #26a69a,\n \"500\": #009688,\n \"600\": #00897b,\n \"700\": #00796b,\n \"800\": #00695c,\n \"900\": #004d40,\n \"a100\": #a7ffeb,\n \"a200\": #64ffda,\n \"a400\": #1de9b6,\n \"a700\": #00bfa5\n);\n\n$clr-teal: map-get($clr-teal-list, \"base\");\n\n$clr-teal-50: map-get($clr-teal-list, \"50\");\n$clr-teal-100: map-get($clr-teal-list, \"100\");\n$clr-teal-200: map-get($clr-teal-list, \"200\");\n$clr-teal-300: map-get($clr-teal-list, \"300\");\n$clr-teal-400: map-get($clr-teal-list, \"400\");\n$clr-teal-500: map-get($clr-teal-list, \"500\");\n$clr-teal-600: map-get($clr-teal-list, \"600\");\n$clr-teal-700: map-get($clr-teal-list, \"700\");\n$clr-teal-800: map-get($clr-teal-list, \"800\");\n$clr-teal-900: map-get($clr-teal-list, \"900\");\n$clr-teal-a100: map-get($clr-teal-list, \"a100\");\n$clr-teal-a200: map-get($clr-teal-list, \"a200\");\n$clr-teal-a400: map-get($clr-teal-list, \"a400\");\n$clr-teal-a700: map-get($clr-teal-list, \"a700\");\n\n\n//\n// Green\n//\n\n$clr-green-list: (\n \"base\": #4caf50,\n \"50\": #e8f5e9,\n \"100\": #c8e6c9,\n \"200\": #a5d6a7,\n \"300\": #81c784,\n \"400\": #66bb6a,\n \"500\": #4caf50,\n \"600\": #43a047,\n \"700\": #388e3c,\n \"800\": #2e7d32,\n \"900\": #1b5e20,\n \"a100\": #b9f6ca,\n \"a200\": #69f0ae,\n \"a400\": #00e676,\n \"a700\": #00c853\n);\n\n$clr-green: map-get($clr-green-list, \"base\");\n\n$clr-green-50: map-get($clr-green-list, \"50\");\n$clr-green-100: map-get($clr-green-list, \"100\");\n$clr-green-200: map-get($clr-green-list, \"200\");\n$clr-green-300: map-get($clr-green-list, \"300\");\n$clr-green-400: map-get($clr-green-list, \"400\");\n$clr-green-500: map-get($clr-green-list, \"500\");\n$clr-green-600: map-get($clr-green-list, \"600\");\n$clr-green-700: map-get($clr-green-list, \"700\");\n$clr-green-800: map-get($clr-green-list, \"800\");\n$clr-green-900: map-get($clr-green-list, \"900\");\n$clr-green-a100: map-get($clr-green-list, \"a100\");\n$clr-green-a200: map-get($clr-green-list, \"a200\");\n$clr-green-a400: map-get($clr-green-list, \"a400\");\n$clr-green-a700: map-get($clr-green-list, \"a700\");\n\n\n//\n// Light green\n//\n\n$clr-light-green-list: (\n \"base\": #8bc34a,\n \"50\": #f1f8e9,\n \"100\": #dcedc8,\n \"200\": #c5e1a5,\n \"300\": #aed581,\n \"400\": #9ccc65,\n \"500\": #8bc34a,\n \"600\": #7cb342,\n \"700\": #689f38,\n \"800\": #558b2f,\n \"900\": #33691e,\n \"a100\": #ccff90,\n \"a200\": #b2ff59,\n \"a400\": #76ff03,\n \"a700\": #64dd17\n);\n\n$clr-light-green: map-get($clr-light-green-list, \"base\");\n\n$clr-light-green-50: map-get($clr-light-green-list, \"50\");\n$clr-light-green-100: map-get($clr-light-green-list, \"100\");\n$clr-light-green-200: map-get($clr-light-green-list, \"200\");\n$clr-light-green-300: map-get($clr-light-green-list, \"300\");\n$clr-light-green-400: map-get($clr-light-green-list, \"400\");\n$clr-light-green-500: map-get($clr-light-green-list, \"500\");\n$clr-light-green-600: map-get($clr-light-green-list, \"600\");\n$clr-light-green-700: map-get($clr-light-green-list, \"700\");\n$clr-light-green-800: map-get($clr-light-green-list, \"800\");\n$clr-light-green-900: map-get($clr-light-green-list, \"900\");\n$clr-light-green-a100: map-get($clr-light-green-list, \"a100\");\n$clr-light-green-a200: map-get($clr-light-green-list, \"a200\");\n$clr-light-green-a400: map-get($clr-light-green-list, \"a400\");\n$clr-light-green-a700: map-get($clr-light-green-list, \"a700\");\n\n\n//\n// Lime\n//\n\n$clr-lime-list: (\n \"base\": #cddc39,\n \"50\": #f9fbe7,\n \"100\": #f0f4c3,\n \"200\": #e6ee9c,\n \"300\": #dce775,\n \"400\": #d4e157,\n \"500\": #cddc39,\n \"600\": #c0ca33,\n \"700\": #afb42b,\n \"800\": #9e9d24,\n \"900\": #827717,\n \"a100\": #f4ff81,\n \"a200\": #eeff41,\n \"a400\": #c6ff00,\n \"a700\": #aeea00\n);\n\n$clr-lime: map-get($clr-lime-list, \"base\");\n\n$clr-lime-50: map-get($clr-lime-list, \"50\");\n$clr-lime-100: map-get($clr-lime-list, \"100\");\n$clr-lime-200: map-get($clr-lime-list, \"200\");\n$clr-lime-300: map-get($clr-lime-list, \"300\");\n$clr-lime-400: map-get($clr-lime-list, \"400\");\n$clr-lime-500: map-get($clr-lime-list, \"500\");\n$clr-lime-600: map-get($clr-lime-list, \"600\");\n$clr-lime-700: map-get($clr-lime-list, \"700\");\n$clr-lime-800: map-get($clr-lime-list, \"800\");\n$clr-lime-900: map-get($clr-lime-list, \"900\");\n$clr-lime-a100: map-get($clr-lime-list, \"a100\");\n$clr-lime-a200: map-get($clr-lime-list, \"a200\");\n$clr-lime-a400: map-get($clr-lime-list, \"a400\");\n$clr-lime-a700: map-get($clr-lime-list, \"a700\");\n\n\n//\n// Yellow\n//\n\n$clr-yellow-list: (\n \"base\": #ffeb3b,\n \"50\": #fffde7,\n \"100\": #fff9c4,\n \"200\": #fff59d,\n \"300\": #fff176,\n \"400\": #ffee58,\n \"500\": #ffeb3b,\n \"600\": #fdd835,\n \"700\": #fbc02d,\n \"800\": #f9a825,\n \"900\": #f57f17,\n \"a100\": #ffff8d,\n \"a200\": #ffff00,\n \"a400\": #ffea00,\n \"a700\": #ffd600\n);\n\n$clr-yellow: map-get($clr-yellow-list, \"base\");\n\n$clr-yellow-50: map-get($clr-yellow-list, \"50\");\n$clr-yellow-100: map-get($clr-yellow-list, \"100\");\n$clr-yellow-200: map-get($clr-yellow-list, \"200\");\n$clr-yellow-300: map-get($clr-yellow-list, \"300\");\n$clr-yellow-400: map-get($clr-yellow-list, \"400\");\n$clr-yellow-500: map-get($clr-yellow-list, \"500\");\n$clr-yellow-600: map-get($clr-yellow-list, \"600\");\n$clr-yellow-700: map-get($clr-yellow-list, \"700\");\n$clr-yellow-800: map-get($clr-yellow-list, \"800\");\n$clr-yellow-900: map-get($clr-yellow-list, \"900\");\n$clr-yellow-a100: map-get($clr-yellow-list, \"a100\");\n$clr-yellow-a200: map-get($clr-yellow-list, \"a200\");\n$clr-yellow-a400: map-get($clr-yellow-list, \"a400\");\n$clr-yellow-a700: map-get($clr-yellow-list, \"a700\");\n\n\n//\n// amber\n//\n\n$clr-amber-list: (\n \"base\": #ffc107,\n \"50\": #fff8e1,\n \"100\": #ffecb3,\n \"200\": #ffe082,\n \"300\": #ffd54f,\n \"400\": #ffca28,\n \"500\": #ffc107,\n \"600\": #ffb300,\n \"700\": #ffa000,\n \"800\": #ff8f00,\n \"900\": #ff6f00,\n \"a100\": #ffe57f,\n \"a200\": #ffd740,\n \"a400\": #ffc400,\n \"a700\": #ffab00\n);\n\n$clr-amber: map-get($clr-amber-list, \"base\");\n\n$clr-amber-50: map-get($clr-amber-list, \"50\");\n$clr-amber-100: map-get($clr-amber-list, \"100\");\n$clr-amber-200: map-get($clr-amber-list, \"200\");\n$clr-amber-300: map-get($clr-amber-list, \"300\");\n$clr-amber-400: map-get($clr-amber-list, \"400\");\n$clr-amber-500: map-get($clr-amber-list, \"500\");\n$clr-amber-600: map-get($clr-amber-list, \"600\");\n$clr-amber-700: map-get($clr-amber-list, \"700\");\n$clr-amber-800: map-get($clr-amber-list, \"800\");\n$clr-amber-900: map-get($clr-amber-list, \"900\");\n$clr-amber-a100: map-get($clr-amber-list, \"a100\");\n$clr-amber-a200: map-get($clr-amber-list, \"a200\");\n$clr-amber-a400: map-get($clr-amber-list, \"a400\");\n$clr-amber-a700: map-get($clr-amber-list, \"a700\");\n\n\n//\n// Orange\n//\n\n$clr-orange-list: (\n \"base\": #ff9800,\n \"50\": #fff3e0,\n \"100\": #ffe0b2,\n \"200\": #ffcc80,\n \"300\": #ffb74d,\n \"400\": #ffa726,\n \"500\": #ff9800,\n \"600\": #fb8c00,\n \"700\": #f57c00,\n \"800\": #ef6c00,\n \"900\": #e65100,\n \"a100\": #ffd180,\n \"a200\": #ffab40,\n \"a400\": #ff9100,\n \"a700\": #ff6d00\n);\n\n$clr-orange: map-get($clr-orange-list, \"base\");\n\n$clr-orange-50: map-get($clr-orange-list, \"50\");\n$clr-orange-100: map-get($clr-orange-list, \"100\");\n$clr-orange-200: map-get($clr-orange-list, \"200\");\n$clr-orange-300: map-get($clr-orange-list, \"300\");\n$clr-orange-400: map-get($clr-orange-list, \"400\");\n$clr-orange-500: map-get($clr-orange-list, \"500\");\n$clr-orange-600: map-get($clr-orange-list, \"600\");\n$clr-orange-700: map-get($clr-orange-list, \"700\");\n$clr-orange-800: map-get($clr-orange-list, \"800\");\n$clr-orange-900: map-get($clr-orange-list, \"900\");\n$clr-orange-a100: map-get($clr-orange-list, \"a100\");\n$clr-orange-a200: map-get($clr-orange-list, \"a200\");\n$clr-orange-a400: map-get($clr-orange-list, \"a400\");\n$clr-orange-a700: map-get($clr-orange-list, \"a700\");\n\n\n//\n// Deep orange\n//\n\n$clr-deep-orange-list: (\n \"base\": #ff5722,\n \"50\": #fbe9e7,\n \"100\": #ffccbc,\n \"200\": #ffab91,\n \"300\": #ff8a65,\n \"400\": #ff7043,\n \"500\": #ff5722,\n \"600\": #f4511e,\n \"700\": #e64a19,\n \"800\": #d84315,\n \"900\": #bf360c,\n \"a100\": #ff9e80,\n \"a200\": #ff6e40,\n \"a400\": #ff3d00,\n \"a700\": #dd2c00\n);\n\n$clr-deep-orange: map-get($clr-deep-orange-list, \"base\");\n\n$clr-deep-orange-50: map-get($clr-deep-orange-list, \"50\");\n$clr-deep-orange-100: map-get($clr-deep-orange-list, \"100\");\n$clr-deep-orange-200: map-get($clr-deep-orange-list, \"200\");\n$clr-deep-orange-300: map-get($clr-deep-orange-list, \"300\");\n$clr-deep-orange-400: map-get($clr-deep-orange-list, \"400\");\n$clr-deep-orange-500: map-get($clr-deep-orange-list, \"500\");\n$clr-deep-orange-600: map-get($clr-deep-orange-list, \"600\");\n$clr-deep-orange-700: map-get($clr-deep-orange-list, \"700\");\n$clr-deep-orange-800: map-get($clr-deep-orange-list, \"800\");\n$clr-deep-orange-900: map-get($clr-deep-orange-list, \"900\");\n$clr-deep-orange-a100: map-get($clr-deep-orange-list, \"a100\");\n$clr-deep-orange-a200: map-get($clr-deep-orange-list, \"a200\");\n$clr-deep-orange-a400: map-get($clr-deep-orange-list, \"a400\");\n$clr-deep-orange-a700: map-get($clr-deep-orange-list, \"a700\");\n\n\n//\n// Brown\n//\n\n$clr-brown-list: (\n \"base\": #795548,\n \"50\": #efebe9,\n \"100\": #d7ccc8,\n \"200\": #bcaaa4,\n \"300\": #a1887f,\n \"400\": #8d6e63,\n \"500\": #795548,\n \"600\": #6d4c41,\n \"700\": #5d4037,\n \"800\": #4e342e,\n \"900\": #3e2723,\n);\n\n$clr-brown: map-get($clr-brown-list, \"base\");\n\n$clr-brown-50: map-get($clr-brown-list, \"50\");\n$clr-brown-100: map-get($clr-brown-list, \"100\");\n$clr-brown-200: map-get($clr-brown-list, \"200\");\n$clr-brown-300: map-get($clr-brown-list, \"300\");\n$clr-brown-400: map-get($clr-brown-list, \"400\");\n$clr-brown-500: map-get($clr-brown-list, \"500\");\n$clr-brown-600: map-get($clr-brown-list, \"600\");\n$clr-brown-700: map-get($clr-brown-list, \"700\");\n$clr-brown-800: map-get($clr-brown-list, \"800\");\n$clr-brown-900: map-get($clr-brown-list, \"900\");\n\n\n//\n// Grey\n//\n\n$clr-grey-list: (\n \"base\": #9e9e9e,\n \"50\": #fafafa,\n \"100\": #f5f5f5,\n \"200\": #eeeeee,\n \"300\": #e0e0e0,\n \"400\": #bdbdbd,\n \"500\": #9e9e9e,\n \"600\": #757575,\n \"700\": #616161,\n \"800\": #424242,\n \"900\": #212121,\n);\n\n$clr-grey: map-get($clr-grey-list, \"base\");\n\n$clr-grey-50: map-get($clr-grey-list, \"50\");\n$clr-grey-100: map-get($clr-grey-list, \"100\");\n$clr-grey-200: map-get($clr-grey-list, \"200\");\n$clr-grey-300: map-get($clr-grey-list, \"300\");\n$clr-grey-400: map-get($clr-grey-list, \"400\");\n$clr-grey-500: map-get($clr-grey-list, \"500\");\n$clr-grey-600: map-get($clr-grey-list, \"600\");\n$clr-grey-700: map-get($clr-grey-list, \"700\");\n$clr-grey-800: map-get($clr-grey-list, \"800\");\n$clr-grey-900: map-get($clr-grey-list, \"900\");\n\n\n//\n// Blue grey\n//\n\n$clr-blue-grey-list: (\n \"base\": #607d8b,\n \"50\": #eceff1,\n \"100\": #cfd8dc,\n \"200\": #b0bec5,\n \"300\": #90a4ae,\n \"400\": #78909c,\n \"500\": #607d8b,\n \"600\": #546e7a,\n \"700\": #455a64,\n \"800\": #37474f,\n \"900\": #263238,\n);\n\n$clr-blue-grey: map-get($clr-blue-grey-list, \"base\");\n\n$clr-blue-grey-50: map-get($clr-blue-grey-list, \"50\");\n$clr-blue-grey-100: map-get($clr-blue-grey-list, \"100\");\n$clr-blue-grey-200: map-get($clr-blue-grey-list, \"200\");\n$clr-blue-grey-300: map-get($clr-blue-grey-list, \"300\");\n$clr-blue-grey-400: map-get($clr-blue-grey-list, \"400\");\n$clr-blue-grey-500: map-get($clr-blue-grey-list, \"500\");\n$clr-blue-grey-600: map-get($clr-blue-grey-list, \"600\");\n$clr-blue-grey-700: map-get($clr-blue-grey-list, \"700\");\n$clr-blue-grey-800: map-get($clr-blue-grey-list, \"800\");\n$clr-blue-grey-900: map-get($clr-blue-grey-list, \"900\");\n\n\n//\n// Black\n//\n\n$clr-black-list: (\n \"base\": #000\n);\n\n$clr-black: map-get($clr-black-list, \"base\");\n\n\n//\n// White\n//\n\n$clr-white-list: (\n \"base\": #fff\n);\n\n$clr-white: map-get($clr-white-list, \"base\");\n\n\n//\n// List for all Colors for looping\n//\n\n$clr-list-all: (\n \"red\": $clr-red-list,\n \"pink\": $clr-pink-list,\n \"purple\": $clr-purple-list,\n \"deep-purple\": $clr-deep-purple-list,\n \"indigo\": $clr-indigo-list,\n \"blue\": $clr-blue-list,\n \"light-blue\": $clr-light-blue-list,\n \"cyan\": $clr-cyan-list,\n \"teal\": $clr-teal-list,\n \"green\": $clr-green-list,\n \"light-green\": $clr-light-green-list,\n \"lime\": $clr-lime-list,\n \"yellow\": $clr-yellow-list,\n \"amber\": $clr-amber-list,\n \"orange\": $clr-orange-list,\n \"deep-orange\": $clr-deep-orange-list,\n \"brown\": $clr-brown-list,\n \"grey\": $clr-grey-list,\n \"blue-grey\": $clr-blue-grey-list,\n \"black\": $clr-black-list,\n \"white\": $clr-white-list\n);\n\n\n//\n// Typography\n//\n\n$clr-ui-display-4: $clr-grey-600;\n$clr-ui-display-3: $clr-grey-600;\n$clr-ui-display-2: $clr-grey-600;\n$clr-ui-display-1: $clr-grey-600;\n$clr-ui-headline: $clr-grey-900;\n$clr-ui-title: $clr-grey-900;\n$clr-ui-subhead-1: $clr-grey-900;\n$clr-ui-body-2: $clr-grey-900;\n$clr-ui-body-1: $clr-grey-900;\n$clr-ui-caption: $clr-grey-600;\n$clr-ui-menu: $clr-grey-900;\n$clr-ui-button: $clr-grey-900;\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Footnote reference\n [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n }\n\n // Footnote\n [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n }\n\n // Footnote container\n .footnote {\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n\n // Footnote list - omit left indentation\n > ol {\n margin-left: 0;\n\n // Footnote item - footnote items can contain lists, so we need to scope\n // the spacing adjustments to the top-level footnote item.\n > li {\n transition: color 125ms;\n\n // Darken color on target\n &:target {\n color: var(--md-default-fg-color);\n }\n\n // Show backreferences on footnote hover\n &:hover .footnote-backref,\n &:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0;\n }\n }\n }\n }\n\n // Footnote backreference\n .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n // Hack: omit Unicode arrow for replacement with icon\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(px2rem(5px));\n opacity: 0;\n transition:\n color 250ms,\n transform 250ms 250ms,\n opacity 125ms 250ms;\n\n // [print]: Show footnote backreferences\n @media print {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-5px));\n }\n\n // Adjust color on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Footnote backreference icon\n &::before {\n display: inline-block;\n width: px2rem(16px);\n height: px2rem(16px);\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Headerlink\n .headerlink {\n display: inline-block;\n margin-left: px2rem(10px);\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition:\n color 250ms,\n opacity 125ms;\n\n // [print]: Hide headerlinks\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(10px);\n margin-left: initial;\n }\n }\n\n // Show headerlinks on parent hover\n :hover > .headerlink,\n :target > .headerlink,\n .headerlink:focus {\n opacity: 1;\n transition:\n color 250ms,\n opacity 125ms;\n }\n\n // Adjust color on parent target or focus/hover\n :target > .headerlink,\n .headerlink:focus,\n .headerlink:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Adjust scroll offset for all elements with `id` attributes - general scroll\n // margin offset for anything that can be targeted. Browser support is pretty\n // decent by now, but Edge <79 and Safari (iOS and macOS) still don't support\n // it properly, so we settle with a cross-browser anchor correction solution.\n :target {\n scroll-margin-top: px2rem(48px + 24px);\n }\n\n // Adjust scroll offset for headlines of level 1-3\n h1:target,\n h2:target,\n h3:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n content: \"\";\n }\n }\n\n // Adjust scroll offset for headlines of level 4\n h4:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n content: \"\";\n }\n }\n\n // Adjust scroll offset for headlines of level 5-6\n h5:target,\n h6:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px);\n padding-top: px2rem(48px + 24px);\n content: \"\";\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Arithmatex container\n div.arithmatex {\n overflow: auto;\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n margin: 0 px2rem(-16px);\n }\n\n // Arithmatex content\n > * {\n width: min-content;\n // stylelint-disable-next-line declaration-no-important\n margin: 1em auto !important;\n padding: 0 px2rem(16px);\n touch-action: auto;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Deletion, addition or comment\n del.critic,\n ins.critic,\n .critic.comment {\n box-decoration-break: clone;\n }\n\n // Deletion\n del.critic {\n background-color: var(--md-typeset-del-color);\n }\n\n // Addition\n ins.critic {\n background-color: var(--md-typeset-ins-color);\n }\n\n // Comment\n .critic.comment {\n color: var(--md-code-hl-comment-color);\n\n // Comment opening mark\n &::before {\n content: \"/* \";\n }\n\n // Comment closing mark\n &::after {\n content: \" */\";\n }\n }\n\n // Critic block\n .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: px2rem(16px);\n padding-left: px2rem(16px);\n overflow: auto;\n box-shadow: none;\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0.5em;\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0.5em;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Details\n details {\n @extend .admonition;\n\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n\n // Details title icon - rotate icon on transition to open state\n &[open] > summary::after {\n transform: rotate(90deg);\n }\n\n // Adjust spacing for details in closed state\n &:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n\n // Hack: we cannot set `overflow: hidden` on the `details` element (which\n // is why we set it to `overflow: visible`, as the outline would not be\n // visible when focusing. Therefore, we must set the border radius on the\n // summary explicitly.\n > summary {\n border-radius: px2rem(2px);\n }\n }\n\n // Hack: omit margin collapse\n &::after {\n display: table;\n content: \"\";\n }\n }\n\n // Details title\n summary {\n @extend .admonition-title;\n\n display: block;\n min-height: px2rem(20px);\n padding: px2rem(8px) px2rem(36px) px2rem(8px) px2rem(40px);\n border-top-left-radius: px2rem(2px);\n border-top-right-radius: px2rem(2px);\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: px2rem(8px) px2rem(44px) px2rem(8px) px2rem(36px);\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Details marker\n &::after {\n position: absolute;\n top: px2rem(8px);\n right: px2rem(8px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(8px);\n transform: rotate(180deg);\n }\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Emoji and icon container\n .emojione,\n .twemoji,\n .gemoji {\n display: inline-flex;\n height: px2em(18px);\n vertical-align: text-top;\n\n // Icon - inlined via mkdocs-material-extensions\n svg {\n width: px2em(18px);\n max-height: 100%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: syntax highlighting\n// ----------------------------------------------------------------------------\n\n// Code block\n.highlight {\n .o, // Operator\n .ow { // Operator, word\n color: var(--md-code-hl-operator-color);\n }\n\n .p { // Punctuation\n color: var(--md-code-hl-punctuation-color);\n }\n\n .cpf, // Comment, preprocessor file\n .l, // Literal\n .s, // Literal, string\n .sb, // Literal, string backticks\n .sc, // Literal, string char\n .s2, // Literal, string double\n .si, // Literal, string interpol\n .s1, // Literal, string single\n .ss { // Literal, string symbol\n color: var(--md-code-hl-string-color);\n }\n\n .cp, // Comment, pre-processor\n .se, // Literal, string escape\n .sh, // Literal, string heredoc\n .sr, // Literal, string regex\n .sx { // Literal, string other\n color: var(--md-code-hl-special-color);\n }\n\n .m, // Number\n .mb, // Number, binary\n .mf, // Number, float\n .mh, // Number, hex\n .mi, // Number, integer\n .il, // Number, integer long\n .mo { // Number, octal\n color: var(--md-code-hl-number-color);\n }\n\n .k, // Keyword,\n .kd, // Keyword, declaration\n .kn, // Keyword, namespace\n .kp, // Keyword, pseudo\n .kr, // Keyword, reserved\n .kt { // Keyword, type\n color: var(--md-code-hl-keyword-color);\n }\n\n .kc, // Keyword, constant\n .n { // Name\n color: var(--md-code-hl-name-color);\n }\n\n .no, // Name, constant\n .nb, // Name, builtin\n .bp { // Name, builtin pseudo\n color: var(--md-code-hl-constant-color);\n }\n\n .nc, // Name, class\n .ne, // Name, exception\n .nf, // Name, function\n .nn { // Name, namespace\n color: var(--md-code-hl-function-color);\n }\n\n .nd, // Name, decorator\n .ni, // Name, entity\n .nl, // Name, label\n .nt { // Name, tag\n color: var(--md-code-hl-keyword-color);\n }\n\n .c, // Comment\n .cm, // Comment, multiline\n .c1, // Comment, single\n .ch, // Comment, shebang\n .cs, // Comment, special\n .sd { // Literal, string doc\n color: var(--md-code-hl-comment-color);\n }\n\n .na, // Name, attribute\n .nv, // Variable,\n .vc, // Variable, class\n .vg, // Variable, global\n .vi { // Variable, instance\n color: var(--md-code-hl-variable-color);\n }\n\n .ge, // Generic, emph\n .gr, // Generic, error\n .gh, // Generic, heading\n .go, // Generic, output\n .gp, // Generic, prompt\n .gs, // Generic, strong\n .gu, // Generic, subheading\n .gt { // Generic, traceback\n color: var(--md-code-hl-generic-color);\n }\n\n .gd, // Diff, delete\n .gi { // Diff, insert\n margin: 0 px2em(-2px);\n padding: 0 px2em(2px);\n border-radius: px2rem(2px);\n }\n\n .gd { // Diff, delete\n background-color: var(--md-typeset-del-color);\n }\n\n .gi { // Diff, insert\n background-color: var(--md-typeset-ins-color);\n }\n\n // Highlighted line\n .hll {\n display: block;\n margin: 0 px2em(-16px, 13.6px);\n padding: 0 px2em(16px, 13.6px);\n background-color: var(--md-code-hl-color);\n }\n\n // Code block line numbers (inline)\n [data-linenos]::before {\n position: sticky;\n left: px2em(-16px, 13.6px);\n float: left;\n margin-right: px2em(16px, 13.6px);\n margin-left: px2em(-16px, 13.6px);\n padding-left: px2em(16px, 13.6px);\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Code block with line numbers\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n\n // Set table elements to block layout, because otherwise the whole flexbox\n // hacking won't work correctly\n tbody,\n td {\n display: block;\n padding: 0;\n }\n\n // We need to use flexbox layout, because otherwise it's not possible to\n // make the code container scroll while keeping the line numbers static\n tr {\n display: flex;\n }\n\n // The pre tags are nested inside a table, so we need to omit the margin\n // because it collapses below all the overflows\n pre {\n margin: 0;\n }\n\n // Code block line numbers - disable user selection, so code can be easily\n // copied without accidentally also copying the line numbers\n .linenos {\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n padding-right: 0;\n font-size: px2em(13.6px);\n background-color: var(--md-code-bg-color);\n user-select: none;\n }\n\n // Code block line numbers container\n .linenodiv {\n padding-right: px2em(8px, 13.6px);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n\n // Adjust colors and alignment\n pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n }\n }\n\n // Code block container - stretch to remaining space\n .code {\n flex: 1;\n overflow: hidden;\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Code block with line numbers\n .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: px2rem(2px);\n\n // Omit rounded borders on contained code block\n code {\n border-radius: 0;\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Top-level code block\n > .highlight {\n margin: 1em px2rem(-16px);\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n\n // Omit rounded borders\n code {\n border-radius: 0;\n }\n }\n\n // Top-level code block with line numbers\n > .highlighttable {\n margin: 1em px2rem(-16px);\n border-radius: 0;\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tabbed block content\n .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 px2rem(-1px) var(--md-default-fg-color--lightest);\n\n // [print]: Show all tabs (even hidden ones) when printing\n @media print {\n display: block;\n order: initial;\n }\n\n // Code block is the only child of a tab - remove margin and mirror\n // previous (now deprecated) SuperFences code block grouping behavior\n > pre:only-child,\n > .highlight:only-child pre,\n > .highlighttable:only-child {\n margin: 0;\n\n // Omit rounded borders\n > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n\n // Adjust spacing for nested tab\n > .tabbed-set {\n margin: 0;\n }\n }\n\n // Tabbed block container\n .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: px2rem(2px);\n\n // Tab radio button - the Tabbed extension will generate radio buttons with\n // labels, so tabs can be triggered without the necessity for JavaScript.\n // This is pretty cool, as it has great accessibility out-of-the box, so\n // we just hide the radio button and toggle the label color for indication.\n > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Tab label for checked radio button\n &:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n\n // Show tabbed block content\n + .tabbed-content {\n display: block;\n }\n }\n\n // Tab label on focus\n &:focus + label {\n outline-style: auto;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Tab label\n > label {\n z-index: 1;\n width: auto;\n padding: px2em(12px, 12.8px) 1.25em px2em(10px, 12.8px);\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2rem(12.8px);\n border-bottom: px2rem(2px) solid transparent;\n cursor: pointer;\n transition: color 250ms;\n\n // Tab label on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tasklist item\n .task-list-item {\n position: relative;\n list-style-type: none;\n\n // Make checkbox items align with normal list items, but position\n // everything in ems for correct layout at smaller font sizes\n [type=\"checkbox\"] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: -2em;\n left: initial;\n }\n }\n }\n\n // Hide native checkbox, when custom classes are enabled\n .task-list-control [type=\"checkbox\"] {\n z-index: -1;\n opacity: 0;\n }\n\n // Tasklist indicator in unchecked state\n .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: px2em(-24px);\n width: px2em(20px);\n height: px2em(20px);\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2em(-24px);\n left: initial;\n }\n }\n\n // Tasklist indicator in checked state\n [type=\"checkbox\"]:checked + .task-list-indicator::before {\n background-color: $clr-green-a400;\n mask-image: var(--md-tasklist-icon--checked);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // [tablet +]: Allow for rendering content as sidebars\n @include break-from-device(tablet) {\n\n // Modifier to float block elements\n .inline {\n float: left;\n width: px2rem(234px);\n margin-top: 0;\n margin-right: px2rem(16px);\n margin-bottom: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n }\n\n // Modifier to move to end (ltr: right, rtl: left)\n &.end {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(16px);\n margin-left: 0;\n }\n }\n }\n }\n}\n"]} \ No newline at end of file diff --git a/assets/stylesheets/main.e35208c4.min.css b/assets/stylesheets/main.e35208c4.min.css new file mode 100644 index 00000000..d4856195 --- /dev/null +++ b/assets/stylesheets/main.e35208c4.min.css @@ -0,0 +1,2 @@ +@charset "UTF-8";html{box-sizing:border-box;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}*,:after,:before{box-sizing:inherit}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{display:block;box-sizing:initial;height:.05rem;padding:0;overflow:visible;border:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{margin:0;padding:0;font-size:inherit;font-family:inherit;background:transparent;border:0}input{border:0;outline:none}:root{--md-default-fg-color:rgba(0,0,0,0.87);--md-default-fg-color--light:rgba(0,0,0,0.54);--md-default-fg-color--lighter:rgba(0,0,0,0.32);--md-default-fg-color--lightest:rgba(0,0,0,0.07);--md-default-bg-color:#fff;--md-default-bg-color--light:hsla(0,0%,100%,0.7);--md-default-bg-color--lighter:hsla(0,0%,100%,0.3);--md-default-bg-color--lightest:hsla(0,0%,100%,0.12);--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:hsla(0,0%,100%,0.7);--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:rgba(83,108,254,0.1);--md-accent-bg-color:#fff;--md-accent-bg-color--light:hsla(0,0%,100%,0.7)}:root>*{--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:rgba(255,255,0,0.5);--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-mark-color:rgba(255,255,0,0.5);--md-typeset-del-color:rgba(245,80,61,0.15);--md-typeset-ins-color:rgba(11,213,112,0.15);--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-fg-color:#fff;--md-footer-fg-color--light:hsla(0,0%,100%,0.7);--md-footer-fg-color--lighter:hsla(0,0%,100%,0.3);--md-footer-bg-color:rgba(0,0,0,0.87);--md-footer-bg-color--dark:rgba(0,0,0,0.32)}.md-icon svg{display:block;width:1.2rem;height:1.2rem;fill:currentColor}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{font-feature-settings:"kern","liga";font-family:var(--md-text-font-family,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif}body,code,input,kbd,pre{color:var(--md-typeset-color)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family,_),SFMono-Regular,Consolas,Menlo,monospace}:root{--md-typeset-table--ascending:url("data:image/svg+xml;charset=utf-8,");--md-typeset-table--descending:url("data:image/svg+xml;charset=utf-8,")}.md-typeset{font-size:.8rem;line-height:1.6;-webkit-print-color-adjust:exact;color-adjust:exact}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin:1em 0}.md-typeset h1{margin:0 0 1.25em;color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{margin:1.6em 0 .64em;font-size:1.5625em;line-height:1.4}.md-typeset h3{margin:1.6em 0 .8em;font-weight:400;font-size:1.25em;line-height:1.5;letter-spacing:-.01em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{margin:1em 0;font-weight:700;letter-spacing:-.01em}.md-typeset h5,.md-typeset h6{margin:1.25em 0;color:var(--md-default-fg-color--light);font-weight:700;font-size:.8em;letter-spacing:-.01em}.md-typeset h5{text-transform:uppercase}.md-typeset hr{display:flow-root;margin:1.5em 0;border-bottom:.05rem solid var(--md-default-fg-color--lightest)}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{padding:0 .2941176471em;font-size:.85em;word-break:break-word;background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset code:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:initial;padding:initial;background-color:initial;box-shadow:none}.md-typeset a code{color:currentColor}.md-typeset pre{position:relative;display:flow-root;line-height:1.4}.md-typeset pre>code{display:block;margin:0;padding:.7720588235em 1.1764705882em;overflow:auto;word-break:normal;box-shadow:none;-webkit-box-decoration-break:slice;box-decoration-break:slice;touch-action:auto;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-typeset pre>code::-webkit-scrollbar{width:.2rem;height:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:44.9375em){.md-typeset>pre{margin:1em -.8rem}.md-typeset>pre code{border-radius:0}}.md-typeset kbd{display:inline-block;padding:0 .6666666667em;color:var(--md-default-fg-color);font-size:.75em;vertical-align:text-top;word-break:break-word;background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset}.md-typeset mark{color:inherit;word-break:break-word;background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{text-decoration:none;border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help}@media (hover:none){.md-typeset abbr{position:relative}.md-typeset abbr[title]:focus:after,.md-typeset abbr[title]:hover:after{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:absolute;left:0;display:inline-block;width:auto;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;max-width:80%;margin-top:2em;padding:.2rem .3rem;color:var(--md-default-bg-color);font-size:.7rem;background-color:var(--md-default-fg-color);border-radius:.1rem;content:attr(title)}}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em;margin-left:0}.md-typeset blockquote{display:flow-root;padding-left:.6rem;color:var(--md-default-fg-color--light);border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{padding-right:.6rem;padding-left:0;border-right:.2rem solid var(--md-default-fg-color--lighter);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{display:flow-root;margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em;margin-left:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em;margin-left:0}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em;margin-left:0}.md-typeset dd{margin:1em 0 1.5em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:0}.md-typeset img,.md-typeset svg{max-width:100%;height:auto}.md-typeset img[align=left],.md-typeset svg[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right],.md-typeset svg[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child,.md-typeset svg[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;margin:0 auto;text-align:center}.md-typeset figure img{display:block}.md-typeset figcaption{max-width:24rem;margin:1em auto 2em;font-style:italic}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){display:inline-block;max-width:100%;overflow:auto;font-size:.64rem;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .05rem rgba(0,0,0,.1);touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:5rem;padding:.9375em 1.25em;color:var(--md-default-bg-color);vertical-align:top;background-color:var(--md-default-fg-color--light)}.md-typeset table:not([class]) th a{color:inherit}.md-typeset table:not([class]) td{padding:.9375em 1.25em;vertical-align:top;border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-typeset table:not([class]) tr{transition:background-color 125ms}.md-typeset table:not([class]) tr:hover{background-color:rgba(0,0,0,.035);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}.md-typeset table th[role=columnheader]:after{display:inline-block;width:1.2em;height:1.2em;margin-left:.5em;vertical-align:sub;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:currentColor;-webkit-mask-image:var(--md-typeset-table--ascending);mask-image:var(--md-typeset-table--ascending)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:currentColor;-webkit-mask-image:var(--md-typeset-table--descending);mask-image:var(--md-typeset-table--descending)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{height:100%;overflow-x:hidden;font-size:125%}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{position:relative;display:flex;flex-direction:column;width:100%;min-height:100%;font-size:.5rem;background-color:var(--md-default-bg-color)}@media print{body{display:block}}@media screen and (max-width:59.9375em){body[data-md-state=lock]{position:fixed}}.md-grid{max-width:61rem;margin-right:auto;margin-left:auto}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.md-toggle{display:none}.md-option{position:absolute;width:0;height:0;opacity:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-style:auto;outline-color:var(--md-accent-fg-color)}.md-skip{position:fixed;z-index:-1;margin:.5rem;padding:.3rem .5rem;color:var(--md-default-bg-color);font-size:.64rem;background-color:var(--md-default-fg-color);border-radius:.1rem;outline-color:var(--md-accent-fg-color);transform:translateY(.4rem);opacity:0}.md-skip:focus{z-index:10;transform:translateY(0);opacity:1;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms}@page{margin:25mm}.md-announce{overflow:auto;background-color:var(--md-footer-bg-color)}@media print{.md-announce{display:none}}.md-announce__inner{margin:.6rem auto;padding:0 .8rem;color:var(--md-footer-fg-color);font-size:.7rem}:root{--md-clipboard-icon:url("data:image/svg+xml;charset=utf-8,")}.md-clipboard{position:absolute;top:.5em;right:.5em;z-index:1;width:1.5em;height:1.5em;color:var(--md-default-fg-color--lightest);border-radius:.1rem;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;cursor:pointer;transition:color .25s}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{display:block;width:1.125em;height:1.125em;margin:0 auto;background-color:currentColor;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{color:var(--md-accent-fg-color);background-color:var(--md-accent-fg-color--transparent)}.md-content{flex-grow:1;overflow:hidden;scroll-padding-top:51.2rem}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){.md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem;margin-left:.8rem}.md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-right:.8rem;margin-left:1.2rem}}.md-content__inner:before{display:block;height:.4rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__button{float:right;margin:.4rem 0 .4rem .4rem;padding:0}@media print{.md-content__button{display:none}}[dir=rtl] .md-content__button{float:left;margin-right:.4rem;margin-left:0}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}.md-dialog{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:fixed;right:.8rem;bottom:.8rem;left:auto;z-index:2;min-width:11.1rem;padding:.4rem .6rem;background-color:var(--md-default-fg-color);border-radius:.1rem;transform:translateY(100%);opacity:0;transition:transform 0ms .4s,opacity .4s;pointer-events:none}@media print{.md-dialog{display:none}}[dir=rtl] .md-dialog{right:auto;left:.8rem}.md-dialog[data-md-state=open]{transform:translateY(0);opacity:1;transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s;pointer-events:auto}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-typeset .md-button{display:inline-block;padding:.625em 2em;color:var(--md-primary-fg-color);font-weight:700;border:.1rem solid;border-radius:.1rem;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{color:var(--md-accent-bg-color);background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}.md-typeset .md-input{height:1.8rem;padding:0 .6rem;font-size:.8rem;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 .025rem .05rem rgba(0,0,0,.1);transition:box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{box-shadow:0 .4rem 1rem rgba(0,0,0,.15),0 .025rem .05rem rgba(0,0,0,.15)}.md-typeset .md-input--stretch{width:100%}.md-header{position:-webkit-sticky;position:sticky;top:0;right:0;left:0;z-index:2;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem transparent,0 .2rem .4rem transparent}@media print{.md-header{display:none}}.md-header[data-md-state=shadow]{box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2);transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header[data-md-state=hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header__inner{display:flex;align-items:center;padding:0 .2rem}.md-header__button{position:relative;z-index:1;margin:.2rem;padding:.4rem;color:currentColor;vertical-align:middle;outline-color:var(--md-accent-fg-color);cursor:pointer;transition:opacity .25s}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.1875em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{display:block;width:1.2rem;height:1.2rem;fill:currentColor}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{position:absolute;display:flex;max-width:100%;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-header__topic+.md-header__topic{z-index:-1;transform:translateX(1.25rem);opacity:0;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;pointer-events:none}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__title{flex-grow:1;height:2.4rem;margin-right:.4rem;margin-left:1rem;font-size:.9rem;line-height:2.4rem}.md-header__title[data-md-state=active] .md-header__topic{z-index:-1;transform:translateX(-1.25rem);opacity:0;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;pointer-events:none}[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic{transform:translateX(1.25rem)}.md-header__title[data-md-state=active] .md-header__topic+.md-header__topic{z-index:0;transform:translateX(0);opacity:1;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;pointer-events:auto}.md-header__title>.md-header__ellipsis{position:relative;width:100%;height:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;white-space:nowrap;transition:max-width 0ms .25s,opacity .25s .25s}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__source{display:none}@media screen and (min-width:60em){.md-header__source{display:block;width:11.7rem;max-width:11.7rem;margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem;margin-left:0}}@media screen and (min-width:76.25em){.md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-footer{color:var(--md-footer-fg-color);background-color:var(--md-footer-bg-color)}@media print{.md-footer{display:none}}.md-footer__inner{padding:.2rem;overflow:auto}.md-footer__link{display:flex;padding-top:1.4rem;padding-bottom:.4rem;outline-color:var(--md-accent-fg-color);transition:opacity .25s}@media screen and (min-width:45em){.md-footer__link{width:50%}}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}.md-footer__link--prev{float:left}@media screen and (max-width:44.9375em){.md-footer__link--prev{width:25%}.md-footer__link--prev .md-footer__title{display:none}}[dir=rtl] .md-footer__link--prev{float:right}[dir=rtl] .md-footer__link--prev svg{transform:scaleX(-1)}.md-footer__link--next{float:right;text-align:right}@media screen and (max-width:44.9375em){.md-footer__link--next{width:75%}}[dir=rtl] .md-footer__link--next{float:left;text-align:left}[dir=rtl] .md-footer__link--next svg{transform:scaleX(-1)}.md-footer__title{position:relative;flex-grow:1;max-width:calc(100% - 2.4rem);padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{position:absolute;right:0;left:0;margin-top:-1rem;padding:0 1rem;font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-footer-copyright{width:100%;margin:auto .6rem;padding:.4rem 0;color:var(--md-footer-fg-color--lighter);font-size:.64rem}@media screen and (min-width:45em){.md-footer-copyright{width:auto}}.md-footer-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-footer-social{margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-footer-social{padding:.6rem 0}}.md-footer-social__link{display:inline-block;width:1.6rem;height:1.6rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-footer-social__link svg{max-height:.8rem;vertical-align:-25%;fill:currentColor}:root{--md-nav-icon--prev:url("data:image/svg+xml;charset=utf-8,");--md-nav-icon--next:url("data:image/svg+xml;charset=utf-8,");--md-toc-icon:url("data:image/svg+xml;charset=utf-8,")}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{display:block;padding:0 .6rem;overflow:hidden;font-weight:700;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{width:auto;height:100%}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{display:block;width:2.4rem;height:2.4rem;fill:currentColor}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 .6rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:.6rem;padding-left:0}.md-nav__link{display:block;margin-top:.625em;overflow:hidden;text-overflow:ellipsis;cursor:pointer;transition:color 125ms;scroll-snap-align:start}.md-nav__link[data-md-state=blur]{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active{color:var(--md-typeset-a-color)}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{display:block;width:100%;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);background-color:currentColor}.md-nav--primary .md-nav__link[for=__toc]~.md-nav,.md-nav__source{display:none}@media screen and (max-width:76.1875em){.md-nav--primary,.md-nav--primary .md-nav{position:absolute;top:0;right:0;left:0;z-index:1;display:flex;flex-direction:column;height:100%;background-color:var(--md-default-bg-color)}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{position:relative;height:5.6rem;padding:3rem .8rem .2rem;color:var(--md-default-fg-color--light);font-weight:400;line-height:2.4rem;white-space:nowrap;background-color:var(--md-default-fg-color--lightest);cursor:pointer}.md-nav--primary .md-nav__title .md-nav__icon{position:absolute;top:.4rem;left:.4rem;display:block;width:1.2rem;height:1.2rem;margin:.2rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem;left:auto}.md-nav--primary .md-nav__title .md-nav__icon:after{display:block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-nav--primary .md-nav__title~.md-nav__list{overflow-y:auto;background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color)}.md-nav--primary .md-nav__title .md-logo{position:absolute;top:.2rem;left:.2rem;display:block;margin:.2rem;padding:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-logo{right:.2rem;left:auto}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{padding:0;border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link .md-nav__icon{position:absolute;top:50%;right:.6rem;width:1.2rem;height:1.2rem;margin-top:-.6rem;color:inherit;font-size:1.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{right:auto;left:.6rem}.md-nav--primary .md-nav__link .md-nav__icon:after{display:block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem;padding-left:0}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;transform:translateX(100%);opacity:0;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{transform:translateX(0);opacity:1;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.9375em){.md-nav--primary .md-nav__link[for=__toc]{display:block;padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__link[for=__toc]{padding-right:.8rem;padding-left:2.4rem}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{display:block;padding:0 .2rem;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color--dark)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-nav--integrated .md-nav__link[for=__toc]{display:block;padding-right:2.4rem;scroll-snap-align:none}[dir=rtl] .md-nav--integrated .md-nav__link[for=__toc]{padding-right:.8rem;padding-left:2.4rem}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width:76.25em){.md-nav{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon,.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav,.md-nav__toggle:indeterminate~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{display:none}.md-nav__item--section>.md-nav{display:block}.md-nav__item--section>.md-nav>.md-nav__title{display:block;padding:0;pointer-events:none;scroll-snap-align:start}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{float:right;width:.9rem;height:.9rem;transition:transform .25s}[dir=rtl] .md-nav__icon{float:left;transform:rotate(180deg)}.md-nav__icon:after{display:inline-block;width:100%;height:100%;vertical-align:-.1rem;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon,.md-nav__item--nested .md-nav__toggle:indeterminate~.md-nav__link .md-nav__icon{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__list>.md-nav__item--nested,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav>.md-nav__title{display:block;padding:0 .6rem;pointer-events:none;scroll-snap-align:start}.md-nav--lifted .md-nav[data-md-level="1"]{display:block}.md-nav--lifted .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-right:.6rem}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:block;margin-bottom:1.25em;border-left:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav>.md-nav__title{display:none}}:root{--md-search-result-icon:url("data:image/svg+xml;charset=utf-8,")}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{z-index:1;opacity:0}@media screen and (max-width:59.9375em){.md-search__overlay{position:absolute;top:.2rem;left:-2.2rem;width:2rem;height:2rem;overflow:hidden;background-color:var(--md-default-bg-color);border-radius:1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;pointer-events:none}[dir=rtl] .md-search__overlay{right:-2.2rem;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;background-color:rgba(0,0,0,.54);cursor:pointer;transition:width 0ms .25s,height 0ms .25s,opacity .25s}[dir=rtl] .md-search__overlay{right:0;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s}}@media screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.9375em){.md-search__inner{position:fixed;top:0;left:100%;z-index:2;width:100%;height:100%;transform:translateX(5%);opacity:0;transition:right 0ms .3s,left 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;transform:translateX(0);opacity:1;transition:right 0ms 0ms,left 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:auto}html [dir=rtl] .md-search__inner{right:100%;left:auto;transform:translateX(-5%)}}@media screen and (min-width:60em){.md-search__inner{position:relative;float:right;width:11.7rem;padding:.1rem 0;transition:width .25s cubic-bezier(.1,.7,.1,1)}[dir=rtl] .md-search__inner{float:left}}@media screen and (min-width:60em) and (max-width:76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{position:relative}@media screen and (min-width:60em){.md-search__form{border-radius:.1rem}}.md-search__input{position:relative;z-index:2;padding:0 2.2rem 0 3.6rem;text-overflow:ellipsis;background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem transparent;transition:color .25s,background-color .25s,box-shadow .25s}[dir=rtl] .md-search__input{padding:0 3.6rem 0 2.2rem}.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s;transition:color .25s}.md-search__input::-moz-placeholder{-moz-transition:color .25s;transition:color .25s}.md-search__input::-ms-input-placeholder{-ms-transition:color .25s;transition:color .25s}.md-search__input::placeholder{transition:color .25s}.md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}[data-md-toggle=search]:checked~.md-header .md-search__input{box-shadow:0 0 .6rem rgba(0,0,0,.07)}@media screen and (max-width:59.9375em){.md-search__input{width:100%;height:2.4rem;font-size:.9rem}}@media screen and (min-width:60em){.md-search__input{width:100%;height:1.8rem;padding-left:2.2rem;color:inherit;font-size:.8rem;background-color:rgba(0,0,0,.26);border-radius:.1rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}.md-search__input::-webkit-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{color:var(--md-default-fg-color);text-overflow:clip;background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0}[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:var(--md-default-fg-color--light)}}.md-search__icon{position:absolute;z-index:2;width:1.2rem;height:1.2rem;cursor:pointer;transition:color .25s,opacity .25s}.md-search__icon:hover{opacity:.7}.md-search__icon[for=__search]{top:.3rem;left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem;left:auto}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.9375em){.md-search__icon[for=__search]{top:.6rem;left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem;left:auto}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}.md-search__icon[type=reset]{top:.3rem;right:.5rem;transform:scale(.75);opacity:0;transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s;pointer-events:none}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:.5rem}@media screen and (max-width:59.9375em){.md-search__icon[type=reset]{top:.6rem;right:.8rem}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]{transform:scale(1);opacity:1;pointer-events:auto}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;z-index:1;width:100%;overflow:hidden;border-radius:0 0 .1rem .1rem}@media screen and (max-width:59.9375em){.md-search__output{top:2.4rem;bottom:0}}@media screen and (min-width:60em){.md-search__output{top:1.9rem;opacity:0;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}}.md-search__scrollwrap{height:100%;overflow-y:auto;background-color:var(--md-default-bg-color);-webkit-backface-visibility:hidden;backface-visibility:hidden;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1), (max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-search__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{padding:0 .8rem;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;background-color:var(--md-default-fg-color--lightest);scroll-snap-align:start}@media screen and (min-width:60em){.md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem;padding-left:0}}.md-search-result__list{margin:0;padding:0;list-style:none}.md-search-result__item{box-shadow:0 -.05rem 0 var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;transition:background-color .25s;scroll-snap-align:start}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more summary{display:block;padding:.75em .8rem;color:var(--md-typeset-a-color);font-size:.64rem;outline:none;cursor:pointer;transition:color .25s,background-color .25s;scroll-snap-align:start}@media screen and (min-width:60em){.md-search-result__more summary{padding-left:2.2rem}[dir=rtl] .md-search-result__more summary{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__more summary:focus,.md-search-result__more summary:hover{color:var(--md-accent-fg-color);background-color:var(--md-accent-fg-color--transparent)}.md-search-result__more summary::-webkit-details-marker,.md-search-result__more summary::marker{display:none}.md-search-result__more summary~*>*{opacity:.65}.md-search-result__article{position:relative;padding:0 .8rem;overflow:hidden}@media screen and (min-width:60em){.md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__article--document .md-search-result__title{margin:.55rem 0;font-weight:400;font-size:.8rem;line-height:1.4}.md-search-result__icon{position:absolute;left:0;width:1.2rem;height:1.2rem;margin:.5rem;color:var(--md-default-fg-color--light)}@media screen and (max-width:59.9375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{display:inline-block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-search-result__icon{right:0;left:auto}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result__title{margin:.5em 0;font-weight:700;font-size:.64rem;line-height:1.6}.md-search-result__teaser{display:-webkit-box;max-height:2rem;margin:.5em 0;overflow:hidden;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}@media screen and (max-width:44.9375em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}.md-search-result__teaser mark{text-decoration:underline;background-color:initial}.md-search-result__terms{margin:.5em 0;font-size:.64rem;font-style:italic}.md-search-result mark{color:var(--md-accent-fg-color);background-color:initial}.md-select{position:relative;z-index:1}.md-select__inner{position:absolute;top:calc(100% - .2rem);left:50%;max-height:0;margin-top:.2rem;color:var(--md-default-fg-color);background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);transform:translate3d(-50%,.3rem,0);opacity:0;transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;transform:translate3d(-50%,0,0);opacity:1;transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{position:absolute;top:0;left:50%;width:0;height:0;margin-top:-.2rem;margin-left:-.2rem;border-left:.2rem solid transparent;border-right:.2rem solid transparent;border-top:0;border-bottom:.2rem solid transparent;border-bottom-color:var(--md-default-bg-color);content:""}.md-select__list{max-height:inherit;margin:0;padding:0;overflow:auto;font-size:.8rem;list-style-type:none;border-radius:.1rem}.md-select__item{line-height:1.8rem}.md-select__link{display:block;width:100%;padding-right:1.2rem;padding-left:.6rem;outline:none;cursor:pointer;transition:background-color .25s,color .25s;scroll-snap-align:start}[dir=rtl] .md-select__link{padding-right:.6rem;padding-left:1.2rem}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{position:-webkit-sticky;position:sticky;top:2.4rem;flex-shrink:0;align-self:flex-start;width:12.1rem;padding:1.2rem 0}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.1875em){.md-sidebar--primary{position:fixed;top:0;left:-12.1rem;z-index:3;display:block;width:12.1rem;height:100%;background-color:var(--md-default-bg-color);transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s}[dir=rtl] .md-sidebar--primary{right:-12.1rem;left:auto}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{position:absolute;top:0;right:0;bottom:0;left:0;margin:0;-ms-scroll-snap-type:none;scroll-snap-type:none;overflow:hidden}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{margin:0 .2rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:76.1875em){.md-overlay{position:fixed;top:0;z-index:3;width:0;height:0;background-color:rgba(0,0,0,.54);opacity:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s}[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s}}@-webkit-keyframes md-source__facts--done{0%{height:0}to{height:.65rem}}@keyframes md-source__facts--done{0%{height:0}to{height:.65rem}}@-webkit-keyframes md-source__fact--done{0%{transform:translateY(100%);opacity:0}50%{opacity:0}to{transform:translateY(0);opacity:1}}@keyframes md-source__fact--done{0%{transform:translateY(100%);opacity:0}50%{opacity:0}to{transform:translateY(0);opacity:1}}:root{--md-source-forks-icon:url("data:image/svg+xml;charset=utf-8,");--md-source-repositories-icon:url("data:image/svg+xml;charset=utf-8,");--md-source-stars-icon:url("data:image/svg+xml;charset=utf-8,");--md-source-version-icon:url("data:image/svg+xml;charset=utf-8,")}.md-source{display:block;font-size:.65rem;line-height:1.2;white-space:nowrap;outline-color:var(--md-accent-fg-color);-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:opacity .25s}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;width:2rem;height:2.4rem;vertical-align:middle}.md-source__icon svg{margin-top:.6rem;margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem;margin-left:0}.md-source__icon+.md-source__repository{margin-left:-2rem;padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem;margin-left:0;padding-right:2rem;padding-left:0}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);margin-left:.6rem;overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{margin:.1rem 0 0;padding:0;overflow:hidden;font-size:.55rem;list-style-type:none;opacity:.75}[data-md-state=done] .md-source__facts{-webkit-animation:md-source__facts--done .25s ease-in;animation:md-source__facts--done .25s ease-in}.md-source__fact{display:inline-block}[data-md-state=done] .md-source__fact{-webkit-animation:md-source__fact--done .4s ease-out;animation:md-source__fact--done .4s ease-out}.md-source__fact:before{display:inline-block;width:.6rem;height:.6rem;margin-right:.1rem;vertical-align:text-top;background-color:currentColor;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-source__fact:nth-child(1n+2):before{margin-left:.4rem}[dir=rtl] .md-source__fact{margin-right:0;margin-left:.1rem}[dir=rtl] .md-source__fact:nth-child(1n+2):before{margin-right:.4rem;margin-left:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-tabs{width:100%;overflow:auto;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color)}@media print{.md-tabs{display:none}}@media screen and (max-width:76.1875em){.md-tabs{display:none}}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs__list{margin:0 0 0 .2rem;padding:0;white-space:nowrap;list-style:none;contain:content}[dir=rtl] .md-tabs__list{margin-right:.2rem;margin-left:0}.md-tabs__item{display:inline-block;height:2.4rem;padding-right:.6rem;padding-left:.6rem}.md-tabs__link{display:block;margin-top:.8rem;font-size:.7rem;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;-webkit-backface-visibility:hidden;backface-visibility:hidden;opacity:.7;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link--active,.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[data-md-state=hidden] .md-tabs__link{transform:translateY(50%);opacity:0;transition:transform 0ms .1s,opacity .1s}.md-top{position:-webkit-sticky;position:sticky;bottom:.4rem;z-index:1;float:right;margin:-2.8rem .4rem .4rem;padding:.4rem;color:var(--md-primary-bg-color);background:var(--md-primary-fg-color);border-radius:100%;outline:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 .025rem .05rem rgba(0,0,0,.1);transform:translateY(0);transition:opacity 125ms,transform 125ms cubic-bezier(.4,0,.2,1),background-color 125ms}[dir=rtl] .md-top{float:left}.md-top[data-md-state=hidden]{transform:translateY(-.2rem);opacity:0;pointer-events:none}.md-top:focus,.md-top:hover{background:var(--md-accent-fg-color);transform:scale(1.1)}:root{--md-version-icon:url("data:image/svg+xml;charset=utf-8,")}.md-version{flex-shrink:0;height:2.4rem;font-size:.8rem}.md-version__current{position:relative;top:.05rem;margin-right:.4rem;margin-left:1.4rem;color:inherit;outline:none;cursor:pointer}[dir=rtl] .md-version__current{margin-right:1.4rem;margin-left:.4rem}.md-version__current:after{display:inline-block;width:.4rem;height:.6rem;margin-left:.4rem;background-color:currentColor;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-version__current:after{margin-right:.4rem;margin-left:0}.md-version__list{position:absolute;top:.15rem;z-index:1;max-height:0;margin:.2rem .8rem;padding:0;overflow:auto;color:var(--md-default-fg-color);list-style-type:none;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);opacity:0;transition:max-height 0ms .5s,opacity .25s .25s;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}.md-version__item{line-height:1.8rem}.md-version__link{display:block;width:100%;padding-right:1.2rem;padding-left:.6rem;white-space:nowrap;outline:none;cursor:pointer;transition:color .25s,background-color .25s;scroll-snap-align:start}[dir=rtl] .md-version__link{padding-right:.6rem;padding-left:1.2rem}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--abstract:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--info:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--tip:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--success:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--question:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--warning:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--failure:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--danger:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--bug:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--example:url("data:image/svg+xml;charset=utf-8,");--md-admonition-icon--quote:url("data:image/svg+xml;charset=utf-8,")}.md-typeset .admonition,.md-typeset details{margin:1.5625em 0;padding:0 .6rem;overflow:hidden;color:var(--md-admonition-fg-color);font-size:.64rem;page-break-inside:avoid;background-color:var(--md-admonition-bg-color);border-left:.2rem solid #448aff;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 .025rem .05rem rgba(0,0,0,.05)}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.2rem solid #448aff;border-left:none}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-top:1em;margin-bottom:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}.md-typeset .admonition-title,.md-typeset summary{position:relative;margin:0 -.6rem 0 -.8rem;padding:.4rem .6rem .4rem 2rem;font-weight:700;background-color:rgba(68,138,255,.1);border-left:.2rem solid #448aff}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{margin:0 -.8rem 0 -.6rem;padding:.4rem 2rem .4rem .6rem;border-right:.2rem solid #448aff;border-left:none}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}.md-typeset .admonition-title:before,.md-typeset summary:before{position:absolute;left:.6rem;width:1rem;height:1rem;background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem;left:auto}.md-typeset .admonition-title+.tabbed-set:last-child,.md-typeset summary+.tabbed-set:last-child{margin-top:0}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:rgba(68,138,255,.1);border-color:#448aff}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-color:#00b0ff}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary,.md-typeset .summary>.admonition-title,.md-typeset .summary>summary,.md-typeset .tldr>.admonition-title,.md-typeset .tldr>summary{background-color:rgba(0,176,255,.1);border-color:#00b0ff}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before,.md-typeset .summary>.admonition-title:before,.md-typeset .summary>summary:before,.md-typeset .tldr>.admonition-title:before,.md-typeset .tldr>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-color:#00b8d4}.md-typeset .info>.admonition-title,.md-typeset .info>summary,.md-typeset .todo>.admonition-title,.md-typeset .todo>summary{background-color:rgba(0,184,212,.1);border-color:#00b8d4}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before,.md-typeset .todo>.admonition-title:before,.md-typeset .todo>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .hint>.admonition-title,.md-typeset .hint>summary,.md-typeset .important>.admonition-title,.md-typeset .important>summary,.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:rgba(0,191,165,.1);border-color:#00bfa5}.md-typeset .hint>.admonition-title:before,.md-typeset .hint>summary:before,.md-typeset .important>.admonition-title:before,.md-typeset .important>summary:before,.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-color:#00c853}.md-typeset .check>.admonition-title,.md-typeset .check>summary,.md-typeset .done>.admonition-title,.md-typeset .done>summary,.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:rgba(0,200,83,.1);border-color:#00c853}.md-typeset .check>.admonition-title:before,.md-typeset .check>summary:before,.md-typeset .done>.admonition-title:before,.md-typeset .done>summary:before,.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-color:#64dd17}.md-typeset .faq>.admonition-title,.md-typeset .faq>summary,.md-typeset .help>.admonition-title,.md-typeset .help>summary,.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:rgba(100,221,23,.1);border-color:#64dd17}.md-typeset .faq>.admonition-title:before,.md-typeset .faq>summary:before,.md-typeset .help>.admonition-title:before,.md-typeset .help>summary:before,.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-color:#ff9100}.md-typeset .attention>.admonition-title,.md-typeset .attention>summary,.md-typeset .caution>.admonition-title,.md-typeset .caution>summary,.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:rgba(255,145,0,.1);border-color:#ff9100}.md-typeset .attention>.admonition-title:before,.md-typeset .attention>summary:before,.md-typeset .caution>.admonition-title:before,.md-typeset .caution>summary:before,.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-color:#ff5252}.md-typeset .fail>.admonition-title,.md-typeset .fail>summary,.md-typeset .failure>.admonition-title,.md-typeset .failure>summary,.md-typeset .missing>.admonition-title,.md-typeset .missing>summary{background-color:rgba(255,82,82,.1);border-color:#ff5252}.md-typeset .fail>.admonition-title:before,.md-typeset .fail>summary:before,.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before,.md-typeset .missing>.admonition-title:before,.md-typeset .missing>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-color:#ff1744}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary,.md-typeset .error>.admonition-title,.md-typeset .error>summary{background-color:rgba(255,23,68,.1);border-color:#ff1744}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before,.md-typeset .error>.admonition-title:before,.md-typeset .error>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:rgba(245,0,87,.1);border-color:#f50057}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:rgba(124,77,255,.1);border-color:#7c4dff}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .cite>.admonition-title,.md-typeset .cite>summary,.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:hsla(0,0%,62%,.1);border-color:#9e9e9e}.md-typeset .cite>.admonition-title:before,.md-typeset .cite>summary:before,.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}:root{--md-footnotes-icon:url("data:image/svg+xml;charset=utf-8,")}.md-typeset [id^="fnref:"]:target{scroll-margin-top:0;margin-top:-3.4rem;padding-top:3.4rem}.md-typeset [id^="fn:"]:target{scroll-margin-top:0;margin-top:-3.45rem;padding-top:3.45rem}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}.md-typeset .footnote>ol{margin-left:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{transform:translateX(0);opacity:1}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-weight:700;font-size:.75em}.md-typeset .footnote-backref{display:inline-block;color:var(--md-typeset-a-color);font-size:0;vertical-align:text-bottom;transform:translateX(.25rem);opacity:0;transition:color .25s,transform .25s .25s,opacity 125ms .25s}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);transform:translateX(0);opacity:1}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{display:inline-block;width:.8rem;height:.8rem;background-color:currentColor;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}.md-typeset .headerlink{display:inline-block;margin-left:.5rem;color:var(--md-default-fg-color--lighter);opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem;margin-left:0}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{scroll-margin-top:3.6rem}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{scroll-margin-top:0}.md-typeset h1:target:before,.md-typeset h2:target:before,.md-typeset h3:target:before{display:block;margin-top:-3.4rem;padding-top:3.4rem;content:""}.md-typeset h4:target{scroll-margin-top:0}.md-typeset h4:target:before{display:block;margin-top:-3.45rem;padding-top:3.45rem;content:""}.md-typeset h5:target,.md-typeset h6:target{scroll-margin-top:0}.md-typeset h5:target:before,.md-typeset h6:target:before{display:block;margin-top:-3.6rem;padding-top:3.6rem;content:""}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.9375em){.md-typeset div.arithmatex{margin:0 -.8rem}}.md-typeset div.arithmatex>*{width:-webkit-min-content;width:-moz-min-content;width:min-content;margin:1em auto!important;padding:0 .8rem;touch-action:auto}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:.8rem;padding-left:.8rem;overflow:auto;box-shadow:none}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url("data:image/svg+xml;charset=utf-8,")}.md-typeset details{display:flow-root;padding-top:0;overflow:visible}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){padding-bottom:0;box-shadow:none}.md-typeset details:not([open])>summary{border-radius:.1rem}.md-typeset details:after{display:table;content:""}.md-typeset summary{display:block;min-height:1rem;padding:.4rem 1.8rem .4rem 2rem;border-top-left-radius:.1rem;border-top-right-radius:.1rem;cursor:pointer}[dir=rtl] .md-typeset summary{padding:.4rem 2.2rem .4rem 1.8rem}.md-typeset summary:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}.md-typeset summary:after{position:absolute;top:.4rem;right:.4rem;width:1rem;height:1rem;background-color:currentColor;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transform:rotate(0deg);transition:transform .25s;content:""}[dir=rtl] .md-typeset summary:after{right:auto;left:.4rem;transform:rotate(180deg)}.md-typeset summary::-webkit-details-marker,.md-typeset summary::marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{display:inline-flex;height:1.125em;vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{width:1.125em;max-height:100%;fill:currentColor}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{margin:0 -.125em;padding:0 .125em;border-radius:.1rem}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em;background-color:var(--md-code-hl-color)}.highlight [data-linenos]:before{position:-webkit-sticky;position:sticky;left:-1.1764705882em;float:left;margin-right:1.1764705882em;margin-left:-1.1764705882em;padding-left:1.1764705882em;color:var(--md-default-fg-color--light);background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;content:attr(data-linenos);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable{display:flow-root;overflow:hidden}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable .linenos{padding:.7720588235em 0 .7720588235em 1.1764705882em;font-size:.85em;background-color:var(--md-code-bg-color);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable .linenodiv{padding-right:.5882352941em;box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;overflow:hidden}.md-typeset .highlighttable{margin:1em 0;direction:ltr;border-radius:.1rem}.md-typeset .highlighttable code{border-radius:0}@media screen and (max-width:44.9375em){.md-typeset>.highlight{margin:1em -.8rem}.md-typeset>.highlight .hll{margin:0 -.8rem;padding:0 .8rem}.md-typeset>.highlight code{border-radius:0}.md-typeset>.highlighttable{margin:1em -.8rem;border-radius:0}.md-typeset>.highlighttable .hll{margin:0 -.8rem;padding:0 .8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{position:relative;margin:0;color:inherit;-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.md-typeset .keys span{padding:0 .2em;color:var(--md-default-fg-color--light)}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{padding-right:.4em;content:"⎇"}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{padding-right:.4em;content:"⌘"}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{padding-right:.4em;content:"⌃"}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{padding-right:.4em;content:"◆"}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{padding-right:.4em;content:"⌥"}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{padding-right:.4em;content:"⇧"}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{padding-right:.4em;content:"❖"}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{padding-right:.4em;content:"⊞"}.md-typeset .keys .key-arrow-down:before{padding-right:.4em;content:"↓"}.md-typeset .keys .key-arrow-left:before{padding-right:.4em;content:"←"}.md-typeset .keys .key-arrow-right:before{padding-right:.4em;content:"→"}.md-typeset .keys .key-arrow-up:before{padding-right:.4em;content:"↑"}.md-typeset .keys .key-backspace:before{padding-right:.4em;content:"⌫"}.md-typeset .keys .key-backtab:before{padding-right:.4em;content:"⇤"}.md-typeset .keys .key-caps-lock:before{padding-right:.4em;content:"⇪"}.md-typeset .keys .key-clear:before{padding-right:.4em;content:"⌧"}.md-typeset .keys .key-context-menu:before{padding-right:.4em;content:"☰"}.md-typeset .keys .key-delete:before{padding-right:.4em;content:"⌦"}.md-typeset .keys .key-eject:before{padding-right:.4em;content:"⏏"}.md-typeset .keys .key-end:before{padding-right:.4em;content:"⤓"}.md-typeset .keys .key-escape:before{padding-right:.4em;content:"⎋"}.md-typeset .keys .key-home:before{padding-right:.4em;content:"⤒"}.md-typeset .keys .key-insert:before{padding-right:.4em;content:"⎀"}.md-typeset .keys .key-page-down:before{padding-right:.4em;content:"⇟"}.md-typeset .keys .key-page-up:before{padding-right:.4em;content:"⇞"}.md-typeset .keys .key-print-screen:before{padding-right:.4em;content:"⎙"}.md-typeset .keys .key-tab:after{padding-left:.4em;content:"⇥"}.md-typeset .keys .key-num-enter:after{padding-left:.4em;content:"⌤"}.md-typeset .keys .key-enter:after{padding-left:.4em;content:"⏎"}.md-typeset .tabbed-content{display:none;order:99;width:100%;box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}@media print{.md-typeset .tabbed-content{display:block;order:0}}.md-typeset .tabbed-content>.highlight:only-child pre,.md-typeset .tabbed-content>.highlighttable:only-child,.md-typeset .tabbed-content>pre:only-child{margin:0}.md-typeset .tabbed-content>.highlight:only-child pre>code,.md-typeset .tabbed-content>.highlighttable:only-child>code,.md-typeset .tabbed-content>pre:only-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-content>.tabbed-set{margin:0}.md-typeset .tabbed-set{position:relative;display:flex;flex-wrap:wrap;margin:1em 0;border-radius:.1rem}.md-typeset .tabbed-set>input{position:absolute;width:0;height:0;opacity:0}.md-typeset .tabbed-set>input:checked+label{color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:checked+label+.tabbed-content{display:block}.md-typeset .tabbed-set>input:focus+label{outline-style:auto;outline-color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.md-typeset .tabbed-set>label{z-index:1;width:auto;padding:.9375em 1.25em .78125em;color:var(--md-default-fg-color--light);font-weight:700;font-size:.64rem;border-bottom:.1rem solid transparent;cursor:pointer;transition:color .25s}.md-typeset .tabbed-set>label:hover{color:var(--md-accent-fg-color)}:root{--md-tasklist-icon:url("data:image/svg+xml;charset=utf-8,");--md-tasklist-icon--checked:url("data:image/svg+xml;charset=utf-8,")}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:auto}.md-typeset .task-list-control [type=checkbox]{z-index:-1;opacity:0}.md-typeset .task-list-indicator:before{position:absolute;top:.15em;left:-1.5em;width:1.25em;height:1.25em;background-color:var(--md-default-fg-color--lightest);-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em;left:auto}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media screen and (min-width:45em){.md-typeset .inline{float:left;width:11.7rem;margin-top:0;margin-right:.8rem;margin-bottom:.8rem}.md-typeset .inline.end,[dir=rtl] .md-typeset .inline{float:right;margin-right:0;margin-left:.8rem}[dir=rtl] .md-typeset .inline.end{float:left;margin-right:.8rem;margin-left:0}} +/*# sourceMappingURL=main.e35208c4.min.css.map */ \ No newline at end of file diff --git a/assets/stylesheets/main.e35208c4.min.css.map b/assets/stylesheets/main.e35208c4.min.css.map new file mode 100644 index 00000000..7dccdc97 --- /dev/null +++ b/assets/stylesheets/main.e35208c4.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["src/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/assets/stylesheets/main.scss","src/assets/stylesheets/main/_reset.scss","src/assets/stylesheets/main/_colors.scss","src/assets/stylesheets/main/_icons.scss","src/assets/stylesheets/main/_typeset.scss","src/assets/stylesheets/utilities/_break.scss","node_modules/material-shadows/material-shadows.scss","src/assets/stylesheets/main/layout/_base.scss","src/assets/stylesheets/main/layout/_announce.scss","src/assets/stylesheets/main/layout/_clipboard.scss","src/assets/stylesheets/main/layout/_content.scss","src/assets/stylesheets/main/layout/_dialog.scss","src/assets/stylesheets/main/layout/_form.scss","src/assets/stylesheets/main/layout/_header.scss","src/assets/stylesheets/main/layout/_footer.scss","src/assets/stylesheets/main/layout/_nav.scss","src/assets/stylesheets/main/layout/_search.scss","src/assets/stylesheets/main/layout/_select.scss","src/assets/stylesheets/main/layout/_sidebar.scss","src/assets/stylesheets/main/layout/_source.scss","src/assets/stylesheets/main/layout/_tabs.scss","src/assets/stylesheets/main/layout/_top.scss","src/assets/stylesheets/main/layout/_version.scss","src/assets/stylesheets/main/extensions/markdown/_admonition.scss","node_modules/material-design-color/material-color.scss","src/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/assets/stylesheets/main/extensions/markdown/_toc.scss","src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/assets/stylesheets/main/_modifiers.scss"],"names":[],"mappings":"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","file":"src/assets/stylesheets/main.scss","sourcesContent":["////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Keyboard key\n .keys {\n\n // Keyboard key icon\n kbd::before,\n kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n }\n\n // Surrounding text\n span {\n padding: 0 px2em(3.2px);\n color: var(--md-default-fg-color--light);\n }\n\n // Define keyboard keys with left icon\n @each $name, $code in (\n\n // Modifiers\n \"alt\": \"\\2387\",\n \"left-alt\": \"\\2387\",\n \"right-alt\": \"\\2387\",\n \"command\": \"\\2318\",\n \"left-command\": \"\\2318\",\n \"right-command\": \"\\2318\",\n \"control\": \"\\2303\",\n \"left-control\": \"\\2303\",\n \"right-control\": \"\\2303\",\n \"meta\": \"\\25C6\",\n \"left-meta\": \"\\25C6\",\n \"right-meta\": \"\\25C6\",\n \"option\": \"\\2325\",\n \"left-option\": \"\\2325\",\n \"right-option\": \"\\2325\",\n \"shift\": \"\\21E7\",\n \"left-shift\": \"\\21E7\",\n \"right-shift\": \"\\21E7\",\n \"super\": \"\\2756\",\n \"left-super\": \"\\2756\",\n \"right-super\": \"\\2756\",\n \"windows\": \"\\229E\",\n \"left-windows\": \"\\229E\",\n \"right-windows\": \"\\229E\",\n\n // Other keys\n \"arrow-down\": \"\\2193\",\n \"arrow-left\": \"\\2190\",\n \"arrow-right\": \"\\2192\",\n \"arrow-up\": \"\\2191\",\n \"backspace\": \"\\232B\",\n \"backtab\": \"\\21E4\",\n \"caps-lock\": \"\\21EA\",\n \"clear\": \"\\2327\",\n \"context-menu\": \"\\2630\",\n \"delete\": \"\\2326\",\n \"eject\": \"\\23CF\",\n \"end\": \"\\2913\",\n \"escape\": \"\\238B\",\n \"home\": \"\\2912\",\n \"insert\": \"\\2380\",\n \"page-down\": \"\\21DF\",\n \"page-up\": \"\\21DE\",\n \"print-screen\": \"\\2399\"\n ) {\n .key-#{$name} {\n &::before {\n padding-right: px2em(6.4px);\n content: $code;\n }\n }\n }\n\n // Define keyboard keys with right icon\n @each $name, $code in (\n \"tab\": \"\\21E5\",\n \"num-enter\": \"\\2324\",\n \"enter\": \"\\23CE\"\n ) {\n .key-#{$name} {\n &::after {\n padding-left: px2em(6.4px);\n content: $code;\n }\n }\n }\n }\n}\n","@charset \"UTF-8\";\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\nbody {\n margin: 0;\n}\n\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\na {\n color: inherit;\n text-decoration: none;\n}\n\nhr {\n display: block;\n box-sizing: content-box;\n height: 0.05rem;\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n line-height: 1em;\n}\n\nimg {\n border-style: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\ninput {\n border: 0;\n outline: none;\n}\n\n:root {\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n --md-primary-fg-color: hsla(231, 48%, 48%, 1);\n --md-primary-fg-color--light: hsla(231, 44%, 56%, 1);\n --md-primary-fg-color--dark: hsla(232, 54%, 41%, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-accent-fg-color: hsla(231, 99%, 66%, 1);\n --md-accent-fg-color--transparent: hsla(231, 99%, 66%, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n}\n:root > * {\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n --md-code-hl-color: hsla(60, 100%, 50%, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n --md-typeset-color: var(--md-default-fg-color);\n --md-typeset-a-color: var(--md-primary-fg-color);\n --md-typeset-mark-color: hsla(60, 100%, 50%, 0.5);\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n}\n\n.md-icon svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family: var(--md-text-font-family, _), -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family: var(--md-code-font-family, _), SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n:root {\n --md-typeset-table--ascending: svg-load(\"material/arrow-down.svg\");\n --md-typeset-table--descending: svg-load(\"material/arrow-up.svg\");\n}\n\n.md-typeset {\n font-size: 0.8rem;\n line-height: 1.6;\n color-adjust: exact;\n}\n@media print {\n .md-typeset {\n font-size: 0.68rem;\n }\n}\n.md-typeset ul,\n.md-typeset ol,\n.md-typeset dl,\n.md-typeset figure,\n.md-typeset blockquote,\n.md-typeset pre {\n margin: 1em 0;\n}\n.md-typeset h1 {\n margin: 0 0 1.25em;\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: 2em;\n line-height: 1.3;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 {\n margin: 1.6em 0 0.64em;\n font-weight: 300;\n font-size: 1.5625em;\n line-height: 1.4;\n letter-spacing: -0.01em;\n}\n.md-typeset h3 {\n margin: 1.6em 0 0.8em;\n font-weight: 400;\n font-size: 1.25em;\n line-height: 1.5;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 + h3 {\n margin-top: 0.8em;\n}\n.md-typeset h4 {\n margin: 1em 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n}\n.md-typeset h5,\n.md-typeset h6 {\n margin: 1.25em 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.8em;\n letter-spacing: -0.01em;\n}\n.md-typeset h5 {\n text-transform: uppercase;\n}\n.md-typeset hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: 0.05rem solid var(--md-default-fg-color--lightest);\n}\n.md-typeset a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n}\n.md-typeset a, .md-typeset a::before {\n transition: color 125ms;\n}\n.md-typeset a:focus, .md-typeset a:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset a.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n}\n@media print {\n .md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n white-space: pre-wrap;\n }\n}\n.md-typeset code {\n padding: 0 0.2941176471em;\n font-size: 0.85em;\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: 0.1rem;\n box-decoration-break: clone;\n}\n.md-typeset code:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset h1 code,\n.md-typeset h2 code,\n.md-typeset h3 code,\n.md-typeset h4 code,\n.md-typeset h5 code,\n.md-typeset h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n}\n.md-typeset a code {\n color: currentColor;\n}\n.md-typeset pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n}\n.md-typeset pre > code {\n display: block;\n margin: 0;\n padding: 0.7720588235em 1.1764705882em;\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-typeset pre > code:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-typeset pre > code::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > pre {\n margin: 1em -0.8rem;\n }\n .md-typeset > pre code {\n border-radius: 0;\n }\n}\n.md-typeset kbd {\n display: inline-block;\n padding: 0 0.6666666667em;\n color: var(--md-default-fg-color);\n font-size: 0.75em;\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.1rem 0 0.05rem var(--md-typeset-kbd-border-color), 0 0.1rem 0 var(--md-typeset-kbd-border-color), 0 -0.1rem 0.2rem var(--md-typeset-kbd-accent-color) inset;\n}\n.md-typeset mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n}\n.md-typeset abbr {\n text-decoration: none;\n border-bottom: 0.05rem dotted var(--md-default-fg-color--light);\n cursor: help;\n}\n@media (hover: none) {\n .md-typeset abbr {\n position: relative;\n }\n .md-typeset abbr[title]:focus::after, .md-typeset abbr[title]:hover::after {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: 0.2rem 0.3rem;\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n content: attr(title);\n }\n}\n.md-typeset small {\n opacity: 0.75;\n}\n.md-typeset sup,\n.md-typeset sub {\n margin-left: 0.078125em;\n}\n[dir=rtl] .md-typeset sup,\n[dir=rtl] .md-typeset sub {\n margin-right: 0.078125em;\n margin-left: initial;\n}\n.md-typeset blockquote {\n display: flow-root;\n padding-left: 0.6rem;\n color: var(--md-default-fg-color--light);\n border-left: 0.2rem solid var(--md-default-fg-color--lighter);\n}\n[dir=rtl] .md-typeset blockquote {\n padding-right: 0.6rem;\n padding-left: initial;\n border-right: 0.2rem solid var(--md-default-fg-color--lighter);\n border-left: initial;\n}\n.md-typeset ul {\n list-style-type: disc;\n}\n.md-typeset ul,\n.md-typeset ol {\n display: flow-root;\n margin-left: 0.625em;\n padding: 0;\n}\n[dir=rtl] .md-typeset ul,\n[dir=rtl] .md-typeset ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset ul ol,\n.md-typeset ol ol {\n list-style-type: lower-alpha;\n}\n.md-typeset ul ol ol,\n.md-typeset ol ol ol {\n list-style-type: lower-roman;\n}\n.md-typeset ul li,\n.md-typeset ol li {\n margin-bottom: 0.5em;\n margin-left: 1.25em;\n}\n[dir=rtl] .md-typeset ul li,\n[dir=rtl] .md-typeset ol li {\n margin-right: 1.25em;\n margin-left: initial;\n}\n.md-typeset ul li p,\n.md-typeset ul li blockquote,\n.md-typeset ol li p,\n.md-typeset ol li blockquote {\n margin: 0.5em 0;\n}\n.md-typeset ul li:last-child,\n.md-typeset ol li:last-child {\n margin-bottom: 0;\n}\n.md-typeset ul li ul,\n.md-typeset ul li ol,\n.md-typeset ol li ul,\n.md-typeset ol li ol {\n margin: 0.5em 0 0.5em 0.625em;\n}\n[dir=rtl] .md-typeset ul li ul,\n[dir=rtl] .md-typeset ul li ol,\n[dir=rtl] .md-typeset ol li ul,\n[dir=rtl] .md-typeset ol li ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset dd {\n margin: 1em 0 1.5em 1.875em;\n}\n[dir=rtl] .md-typeset dd {\n margin-right: 1.875em;\n margin-left: initial;\n}\n.md-typeset img,\n.md-typeset svg {\n max-width: 100%;\n height: auto;\n}\n.md-typeset img[align=left],\n.md-typeset svg[align=left] {\n margin: 1em;\n margin-left: 0;\n}\n.md-typeset img[align=right],\n.md-typeset svg[align=right] {\n margin: 1em;\n margin-right: 0;\n}\n.md-typeset img[align]:only-child,\n.md-typeset svg[align]:only-child {\n margin-top: 0;\n}\n.md-typeset figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n}\n.md-typeset figure img {\n display: block;\n}\n.md-typeset figcaption {\n max-width: 24rem;\n margin: 1em auto 2em;\n font-style: italic;\n}\n.md-typeset iframe {\n max-width: 100%;\n}\n.md-typeset table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: 0.64rem;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.05rem rgba(0, 0, 0, 0.1);\n touch-action: auto;\n}\n@media print {\n .md-typeset table:not([class]) {\n display: table;\n }\n}\n.md-typeset table:not([class]) + * {\n margin-top: 1.5em;\n}\n.md-typeset table:not([class]) th > *:first-child,\n.md-typeset table:not([class]) td > *:first-child {\n margin-top: 0;\n}\n.md-typeset table:not([class]) th > *:last-child,\n.md-typeset table:not([class]) td > *:last-child {\n margin-bottom: 0;\n}\n.md-typeset table:not([class]) th:not([align]),\n.md-typeset table:not([class]) td:not([align]) {\n text-align: left;\n}\n[dir=rtl] .md-typeset table:not([class]) th:not([align]),\n[dir=rtl] .md-typeset table:not([class]) td:not([align]) {\n text-align: right;\n}\n.md-typeset table:not([class]) th {\n min-width: 5rem;\n padding: 0.9375em 1.25em;\n color: var(--md-default-bg-color);\n vertical-align: top;\n background-color: var(--md-default-fg-color--light);\n}\n.md-typeset table:not([class]) th a {\n color: inherit;\n}\n.md-typeset table:not([class]) td {\n padding: 0.9375em 1.25em;\n vertical-align: top;\n border-top: 0.05rem solid var(--md-default-fg-color--lightest);\n}\n.md-typeset table:not([class]) tr {\n transition: background-color 125ms;\n}\n.md-typeset table:not([class]) tr:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 0.05rem 0 var(--md-default-bg-color) inset;\n}\n.md-typeset table:not([class]) tr:first-child td {\n border-top: 0;\n}\n.md-typeset table:not([class]) a {\n word-break: normal;\n}\n.md-typeset table th[role=columnheader] {\n cursor: pointer;\n}\n.md-typeset table th[role=columnheader]::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: sub;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-typeset table th[role=columnheader][aria-sort=ascending]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--ascending);\n}\n.md-typeset table th[role=columnheader][aria-sort=descending]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--descending);\n}\n.md-typeset__scrollwrap {\n margin: 1em -0.8rem;\n overflow-x: auto;\n touch-action: auto;\n}\n.md-typeset__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 0.8rem;\n}\n@media print {\n .md-typeset__table {\n display: block;\n }\n}\nhtml .md-typeset__table table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n}\n\nhtml {\n height: 100%;\n overflow-x: hidden;\n font-size: 125%;\n}\n@media screen and (min-width: 100em) {\n html {\n font-size: 137.5%;\n }\n}\n@media screen and (min-width: 125em) {\n html {\n font-size: 150%;\n }\n}\n\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n font-size: 0.5rem;\n background-color: var(--md-default-bg-color);\n}\n@media print {\n body {\n display: block;\n }\n}\n@media screen and (max-width: 59.9375em) {\n body[data-md-state=lock] {\n position: fixed;\n }\n}\n\n.md-grid {\n max-width: 61rem;\n margin-right: auto;\n margin-left: auto;\n}\n\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n}\n@media print {\n .md-container {\n display: block;\n }\n}\n\n.md-main {\n flex-grow: 1;\n}\n.md-main__inner {\n display: flex;\n height: 100%;\n margin-top: 1.5rem;\n}\n\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.md-toggle {\n display: none;\n}\n\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-option:checked + label:not([hidden]) {\n display: block;\n}\n.md-option.focus-visible + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n}\n\n.md-skip {\n position: fixed;\n z-index: -1;\n margin: 0.5rem;\n padding: 0.3rem 0.5rem;\n color: var(--md-default-bg-color);\n font-size: 0.64rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n outline-color: var(--md-accent-fg-color);\n transform: translateY(0.4rem);\n opacity: 0;\n}\n.md-skip:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 175ms 75ms;\n}\n\n@page {\n margin: 25mm;\n}\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-announce {\n display: none;\n }\n}\n.md-announce__inner {\n margin: 0.6rem auto;\n padding: 0 0.8rem;\n color: var(--md-footer-fg-color);\n font-size: 0.7rem;\n}\n\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n.md-clipboard {\n position: absolute;\n top: 0.5em;\n right: 0.5em;\n z-index: 1;\n width: 1.5em;\n height: 1.5em;\n color: var(--md-default-fg-color--lightest);\n border-radius: 0.1rem;\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.1rem;\n cursor: pointer;\n transition: color 250ms;\n}\n@media print {\n .md-clipboard {\n display: none;\n }\n}\n.md-clipboard:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n:hover > .md-clipboard {\n color: var(--md-default-fg-color--light);\n}\n.md-clipboard:focus, .md-clipboard:hover {\n color: var(--md-accent-fg-color);\n}\n.md-clipboard::after {\n display: block;\n width: 1.125em;\n height: 1.125em;\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-clipboard--inline {\n cursor: pointer;\n}\n.md-clipboard--inline code {\n transition: color 250ms, background-color 250ms;\n}\n.md-clipboard--inline:focus code, .md-clipboard--inline:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n\n.md-content {\n flex-grow: 1;\n overflow: hidden;\n scroll-padding-top: 51.2rem;\n}\n.md-content__inner {\n margin: 0 0.8rem 1.2rem;\n padding-top: 0.6rem;\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-left: 1.2rem;\n }\n [dir=rtl] .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n margin-left: 0.8rem;\n }\n .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n }\n [dir=rtl] .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 0.8rem;\n margin-left: 1.2rem;\n }\n}\n.md-content__inner::before {\n display: block;\n height: 0.4rem;\n content: \"\";\n}\n.md-content__inner > :last-child {\n margin-bottom: 0;\n}\n.md-content__button {\n float: right;\n margin: 0.4rem 0;\n margin-left: 0.4rem;\n padding: 0;\n}\n@media print {\n .md-content__button {\n display: none;\n }\n}\n[dir=rtl] .md-content__button {\n float: left;\n margin-right: 0.4rem;\n margin-left: initial;\n}\n[dir=rtl] .md-content__button svg {\n transform: scaleX(-1);\n}\n.md-typeset .md-content__button {\n color: var(--md-default-fg-color--lighter);\n}\n.md-content__button svg {\n display: inline;\n vertical-align: top;\n}\n\n.md-dialog {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: fixed;\n right: 0.8rem;\n bottom: 0.8rem;\n left: initial;\n z-index: 2;\n min-width: 11.1rem;\n padding: 0.4rem 0.6rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n transform: translateY(100%);\n opacity: 0;\n transition: transform 0ms 400ms, opacity 400ms;\n pointer-events: none;\n}\n@media print {\n .md-dialog {\n display: none;\n }\n}\n[dir=rtl] .md-dialog {\n right: initial;\n left: 0.8rem;\n}\n.md-dialog[data-md-state=open] {\n transform: translateY(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1), opacity 400ms;\n pointer-events: initial;\n}\n.md-dialog__inner {\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n}\n\n.md-typeset .md-button {\n display: inline-block;\n padding: 0.625em 2em;\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: 0.1rem solid currentColor;\n border-radius: 0.1rem;\n transition: color 125ms, background-color 125ms, border-color 125ms;\n}\n.md-typeset .md-button--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n}\n.md-typeset .md-button:focus, .md-typeset .md-button:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .md-input {\n height: 1.8rem;\n padding: 0 0.6rem;\n font-size: 0.8rem;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.1);\n transition: box-shadow 250ms;\n}\n.md-typeset .md-input:focus, .md-typeset .md-input:hover {\n box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.15), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.15);\n}\n.md-typeset .md-input--stretch {\n width: 100%;\n}\n\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 2;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0), 0 0.2rem 0.4rem rgba(0, 0, 0, 0);\n}\n@media print {\n .md-header {\n display: none;\n }\n}\n.md-header[data-md-state=shadow] {\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2);\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), box-shadow 250ms;\n}\n.md-header[data-md-state=hidden] {\n transform: translateY(-100%);\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), box-shadow 250ms;\n}\n.md-header__inner {\n display: flex;\n align-items: center;\n padding: 0 0.2rem;\n}\n.md-header__button {\n position: relative;\n z-index: 1;\n margin: 0.2rem;\n padding: 0.4rem;\n color: currentColor;\n vertical-align: middle;\n outline-color: var(--md-accent-fg-color);\n cursor: pointer;\n transition: opacity 250ms;\n}\n.md-header__button:hover {\n opacity: 0.7;\n}\n.md-header__button:not([hidden]) {\n display: inline-block;\n}\n.md-header__button:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-header__button.md-logo {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n@media screen and (max-width: 76.1875em) {\n .md-header__button.md-logo {\n display: none;\n }\n}\n.md-header__button.md-logo img,\n.md-header__button.md-logo svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n@media screen and (min-width: 60em) {\n .md-header__button[for=__search] {\n display: none;\n }\n}\n.no-js .md-header__button[for=__search] {\n display: none;\n}\n[dir=rtl] .md-header__button[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (min-width: 76.25em) {\n .md-header__button[for=__drawer] {\n display: none;\n }\n}\n.md-header__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n}\n.md-header__topic + .md-header__topic {\n z-index: -1;\n transform: translateX(1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__topic + .md-header__topic {\n transform: translateX(-1.25rem);\n}\n.md-header__title {\n flex-grow: 1;\n height: 2.4rem;\n margin-right: 0.4rem;\n margin-left: 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-header__title[data-md-state=active] .md-header__topic {\n z-index: -1;\n transform: translateX(-1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic {\n transform: translateX(1.25rem);\n}\n.md-header__title[data-md-state=active] .md-header__topic + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n pointer-events: initial;\n}\n.md-header__title > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n}\n.md-header__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition: max-width 0ms 250ms, opacity 250ms 250ms;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-header__option {\n max-width: 0;\n opacity: 0;\n transition: max-width 0ms, opacity 0ms;\n}\n.md-header__source {\n display: none;\n}\n@media screen and (min-width: 60em) {\n .md-header__source {\n display: block;\n width: 11.7rem;\n max-width: 11.7rem;\n margin-left: 1rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1rem;\n margin-left: initial;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-header__source {\n margin-left: 1.4rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1.4rem;\n }\n}\n\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-footer {\n display: none;\n }\n}\n.md-footer__inner {\n padding: 0.2rem;\n overflow: auto;\n}\n.md-footer__link {\n display: flex;\n padding-top: 1.4rem;\n padding-bottom: 0.4rem;\n outline-color: var(--md-accent-fg-color);\n transition: opacity 250ms;\n}\n@media screen and (min-width: 45em) {\n .md-footer__link {\n width: 50%;\n }\n}\n.md-footer__link:focus, .md-footer__link:hover {\n opacity: 0.7;\n}\n.md-footer__link--prev {\n float: left;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--prev {\n width: 25%;\n }\n .md-footer__link--prev .md-footer__title {\n display: none;\n }\n}\n[dir=rtl] .md-footer__link--prev {\n float: right;\n}\n[dir=rtl] .md-footer__link--prev svg {\n transform: scaleX(-1);\n}\n.md-footer__link--next {\n float: right;\n text-align: right;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--next {\n width: 75%;\n }\n}\n[dir=rtl] .md-footer__link--next {\n float: left;\n text-align: left;\n}\n[dir=rtl] .md-footer__link--next svg {\n transform: scaleX(-1);\n}\n.md-footer__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - 2.4rem);\n padding: 0 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-footer__button {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n.md-footer__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: -1rem;\n padding: 0 1rem;\n font-size: 0.64rem;\n opacity: 0.7;\n}\n\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n}\n.md-footer-meta__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: 0.2rem;\n}\nhtml .md-footer-meta.md-typeset a {\n color: var(--md-footer-fg-color--light);\n}\nhtml .md-footer-meta.md-typeset a:focus, html .md-footer-meta.md-typeset a:hover {\n color: var(--md-footer-fg-color);\n}\n\n.md-footer-copyright {\n width: 100%;\n margin: auto 0.6rem;\n padding: 0.4rem 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: 0.64rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-copyright {\n width: auto;\n }\n}\n.md-footer-copyright__highlight {\n color: var(--md-footer-fg-color--light);\n}\n\n.md-footer-social {\n margin: 0 0.4rem;\n padding: 0.2rem 0 0.6rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-social {\n padding: 0.6rem 0;\n }\n}\n.md-footer-social__link {\n display: inline-block;\n width: 1.6rem;\n height: 1.6rem;\n text-align: center;\n}\n.md-footer-social__link::before {\n line-height: 1.9;\n}\n.md-footer-social__link svg {\n max-height: 0.8rem;\n vertical-align: -25%;\n fill: currentColor;\n}\n\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n.md-nav {\n font-size: 0.7rem;\n line-height: 1.3;\n}\n.md-nav__title {\n display: block;\n padding: 0 0.6rem;\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n}\n.md-nav__title .md-nav__button {\n display: none;\n}\n.md-nav__title .md-nav__button img {\n width: auto;\n height: 100%;\n}\n.md-nav__title .md-nav__button.md-logo img,\n.md-nav__title .md-nav__button.md-logo svg {\n display: block;\n width: 2.4rem;\n height: 2.4rem;\n fill: currentColor;\n}\n.md-nav__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-nav__item {\n padding: 0 0.6rem;\n}\n.md-nav__item .md-nav__item {\n padding-right: 0;\n}\n[dir=rtl] .md-nav__item .md-nav__item {\n padding-right: 0.6rem;\n padding-left: 0;\n}\n.md-nav__link {\n display: block;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n}\n.md-nav__link[data-md-state=blur] {\n color: var(--md-default-fg-color--light);\n}\n.md-nav__item .md-nav__link--active {\n color: var(--md-typeset-a-color);\n}\n.md-nav__item--nested > .md-nav__link {\n color: inherit;\n}\n.md-nav__link:focus, .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-nav__link.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-nav--primary .md-nav__link[for=__toc] {\n display: none;\n}\n.md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n}\n.md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: none;\n}\n.md-nav__source {\n display: none;\n}\n@media screen and (max-width: 76.1875em) {\n .md-nav--primary, .md-nav--primary .md-nav {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n .md-nav--primary .md-nav__title,\n.md-nav--primary .md-nav__item {\n font-size: 0.8rem;\n line-height: 1.5;\n }\n .md-nav--primary .md-nav__title {\n position: relative;\n height: 5.6rem;\n padding: 3rem 0.8rem 0.2rem;\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: 2.4rem;\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n position: absolute;\n top: 0.4rem;\n left: 0.4rem;\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon {\n right: 0.4rem;\n left: initial;\n }\n .md-nav--primary .md-nav__title .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0.05rem 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child {\n border-top: 0;\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n .md-nav--primary .md-nav__title .md-logo {\n position: absolute;\n top: 0.2rem;\n left: 0.2rem;\n display: block;\n margin: 0.2rem;\n padding: 0.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-logo {\n right: 0.2rem;\n left: initial;\n }\n .md-nav--primary .md-nav__list {\n flex: 1;\n }\n .md-nav--primary .md-nav__item {\n padding: 0;\n border-top: 0.05rem solid var(--md-default-fg-color--lightest);\n }\n .md-nav--primary .md-nav__item--nested > .md-nav__link {\n padding-right: 2.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__item--nested > .md-nav__link {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link:focus, .md-nav--primary .md-nav__item--active > .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n }\n .md-nav--primary .md-nav__link {\n position: relative;\n margin-top: 0;\n padding: 0.6rem 0.8rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon {\n position: absolute;\n top: 50%;\n right: 0.6rem;\n width: 1.2rem;\n height: 1.2rem;\n margin-top: -0.6rem;\n color: inherit;\n font-size: 1.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon {\n right: initial;\n left: 0.6rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n [dir=rtl] .md-nav--primary .md-nav__icon::after {\n transform: scale(-1);\n }\n .md-nav--primary .md-nav--secondary .md-nav__link {\n position: static;\n }\n .md-nav--primary .md-nav--secondary .md-nav {\n position: static;\n background-color: transparent;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-left: 1.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-right: 1.4rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-left: 2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-right: 2rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 2.6rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 2.6rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 3.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 3.2rem;\n padding-left: initial;\n }\n .md-nav--secondary {\n background-color: transparent;\n }\n .md-nav__toggle ~ .md-nav {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), opacity 125ms 50ms;\n }\n [dir=rtl] .md-nav__toggle ~ .md-nav {\n transform: translateX(-100%);\n }\n .md-nav__toggle:checked ~ .md-nav {\n transform: translateX(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 125ms 125ms;\n }\n .md-nav__toggle:checked ~ .md-nav > .md-nav__list {\n backface-visibility: hidden;\n }\n}\n@media screen and (max-width: 59.9375em) {\n .md-nav--primary .md-nav__link[for=__toc] {\n display: block;\n padding-right: 2.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__link[for=__toc] {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--primary .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n .md-nav__source {\n display: block;\n padding: 0 0.2rem;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-nav--integrated .md-nav__link[for=__toc] {\n display: block;\n padding-right: 2.4rem;\n scroll-snap-align: initial;\n }\n [dir=rtl] .md-nav--integrated .md-nav__link[for=__toc] {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--integrated .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--integrated .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n}\n@media screen and (min-width: 60em) {\n .md-nav--secondary .md-nav__title[for=__toc] {\n scroll-snap-align: start;\n }\n .md-nav--secondary .md-nav__title .md-nav__icon {\n display: none;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-nav {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n scroll-snap-align: start;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n display: none;\n }\n .md-nav__toggle ~ .md-nav {\n display: none;\n }\n .md-nav__toggle:checked ~ .md-nav, .md-nav__toggle:indeterminate ~ .md-nav {\n display: block;\n }\n .md-nav__item--nested > .md-nav > .md-nav__title {\n display: none;\n }\n .md-nav__item--section {\n display: block;\n margin: 1.25em 0;\n }\n .md-nav__item--section:last-child {\n margin-bottom: 0;\n }\n .md-nav__item--section > .md-nav__link {\n display: none;\n }\n .md-nav__item--section > .md-nav {\n display: block;\n }\n .md-nav__item--section > .md-nav > .md-nav__title {\n display: block;\n padding: 0;\n pointer-events: none;\n scroll-snap-align: start;\n }\n .md-nav__item--section > .md-nav > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n .md-nav__icon {\n float: right;\n width: 0.9rem;\n height: 0.9rem;\n transition: transform 250ms;\n }\n [dir=rtl] .md-nav__icon {\n float: left;\n transform: rotate(180deg);\n }\n .md-nav__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: -0.1rem;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link .md-nav__icon, .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link .md-nav__icon {\n transform: rotate(90deg);\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--nested,\n.md-nav--lifted > .md-nav__title {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active {\n display: block;\n padding: 0;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav > .md-nav__title {\n display: block;\n padding: 0 0.6rem;\n pointer-events: none;\n scroll-snap-align: start;\n }\n .md-nav--lifted .md-nav[data-md-level=\"1\"] {\n display: block;\n }\n .md-nav--lifted .md-nav[data-md-level=\"1\"] > .md-nav__list > .md-nav__item {\n padding-right: 0.6rem;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: 0.05rem solid var(--md-primary-fg-color);\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav > .md-nav__title {\n display: none;\n }\n}\n\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n.md-search {\n position: relative;\n}\n@media screen and (min-width: 60em) {\n .md-search {\n padding: 0.2rem 0;\n }\n}\n.no-js .md-search {\n display: none;\n}\n.md-search__overlay {\n z-index: 1;\n opacity: 0;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__overlay {\n position: absolute;\n top: 0.2rem;\n left: -2.2rem;\n width: 2rem;\n height: 2rem;\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: 1rem;\n transform-origin: center;\n transition: transform 300ms 100ms, opacity 200ms 200ms;\n pointer-events: none;\n }\n [dir=rtl] .md-search__overlay {\n right: -2.2rem;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n opacity: 1;\n transition: transform 400ms, opacity 100ms;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n cursor: pointer;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [dir=rtl] .md-search__overlay {\n right: 0;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n width: 100%;\n height: 200vh;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@media screen and (max-width: 29.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(45);\n }\n}\n@media screen and (min-width: 30em) and (max-width: 44.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(60);\n }\n}\n@media screen and (min-width: 45em) and (max-width: 59.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(75);\n }\n}\n.md-search__inner {\n backface-visibility: hidden;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__inner {\n position: fixed;\n top: 0;\n left: 100%;\n z-index: 2;\n width: 100%;\n height: 100%;\n transform: translateX(5%);\n opacity: 0;\n transition: right 0ms 300ms, left 0ms 300ms, transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 150ms 150ms;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n left: 0;\n transform: translateX(0);\n opacity: 1;\n transition: right 0ms 0ms, left 0ms 0ms, transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms 150ms;\n }\n [dir=rtl] [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n right: 0;\n left: initial;\n }\n html [dir=rtl] .md-search__inner {\n right: 100%;\n left: initial;\n transform: translateX(-5%);\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__inner {\n position: relative;\n float: right;\n width: 11.7rem;\n padding: 0.1rem 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n }\n [dir=rtl] .md-search__inner {\n float: left;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 34.4rem;\n }\n}\n.md-search__form {\n position: relative;\n}\n@media screen and (min-width: 60em) {\n .md-search__form {\n border-radius: 0.1rem;\n }\n}\n.md-search__input {\n position: relative;\n z-index: 2;\n padding: 0 2.2rem 0 3.6rem;\n text-overflow: ellipsis;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 0.6rem transparent;\n transition: color 250ms, background-color 250ms, box-shadow 250ms;\n}\n[dir=rtl] .md-search__input {\n padding: 0 3.6rem 0 2.2rem;\n}\n.md-search__input::placeholder {\n transition: color 250ms;\n}\n.md-search__input ~ .md-search__icon, .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n}\n.md-search__input::-ms-clear {\n display: none;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input {\n box-shadow: 0 0 0.6rem rgba(0, 0, 0, 0.07);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__input {\n width: 100%;\n height: 2.4rem;\n font-size: 0.9rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__input {\n width: 100%;\n height: 1.8rem;\n padding-left: 2.2rem;\n color: inherit;\n font-size: 0.8rem;\n background-color: rgba(0, 0, 0, 0.26);\n border-radius: 0.1rem;\n }\n [dir=rtl] .md-search__input {\n padding-right: 2.2rem;\n }\n .md-search__input + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n .md-search__input::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n .md-search__input:hover {\n background-color: rgba(255, 255, 255, 0.12);\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input {\n color: var(--md-default-fg-color);\n text-overflow: clip;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem 0.1rem 0 0;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle=search]:checked ~ .md-header .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n }\n}\n.md-search__icon {\n position: absolute;\n z-index: 2;\n width: 1.2rem;\n height: 1.2rem;\n cursor: pointer;\n transition: color 250ms, opacity 250ms;\n}\n.md-search__icon:hover {\n opacity: 0.7;\n}\n.md-search__icon[for=__search] {\n top: 0.3rem;\n left: 0.5rem;\n}\n[dir=rtl] .md-search__icon[for=__search] {\n right: 0.5rem;\n left: initial;\n}\n[dir=rtl] .md-search__icon[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__icon[for=__search] {\n top: 0.6rem;\n left: 0.8rem;\n }\n [dir=rtl] .md-search__icon[for=__search] {\n right: 0.8rem;\n left: initial;\n }\n .md-search__icon[for=__search] svg:first-child {\n display: none;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__icon[for=__search] {\n pointer-events: none;\n }\n .md-search__icon[for=__search] svg:last-child {\n display: none;\n }\n}\n.md-search__icon[type=reset] {\n top: 0.3rem;\n right: 0.5rem;\n transform: scale(0.75);\n opacity: 0;\n transition: transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-search__icon[type=reset] {\n right: initial;\n left: 0.5rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__icon[type=reset] {\n top: 0.6rem;\n right: 0.8rem;\n }\n [dir=rtl] .md-search__icon[type=reset] {\n right: initial;\n left: 0.8rem;\n }\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__icon[type=reset] {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__icon[type=reset]:hover {\n opacity: 0.7;\n}\n.md-search__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 0.1rem 0.1rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__output {\n top: 2.4rem;\n bottom: 0;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__output {\n top: 1.9rem;\n opacity: 0;\n transition: opacity 400ms;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__output {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.4);\n opacity: 1;\n }\n}\n.md-search__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n backface-visibility: hidden;\n touch-action: pan-y;\n}\n@media (max-resolution: 1dppx) {\n .md-search__scrollwrap {\n transform: translateZ(0);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search__scrollwrap {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-search__scrollwrap {\n width: 34.4rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__scrollwrap {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__scrollwrap {\n max-height: 75vh;\n }\n .md-search__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n .md-search__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n }\n}\n\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n}\n.md-search-result__meta {\n padding: 0 0.8rem;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.8rem;\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__meta {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__meta {\n padding-right: 2.2rem;\n padding-left: initial;\n }\n}\n.md-search-result__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-search-result__item {\n box-shadow: 0 -0.05rem 0 var(--md-default-fg-color--lightest);\n}\n.md-search-result__item:first-child {\n box-shadow: none;\n}\n.md-search-result__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n}\n.md-search-result__link:focus, .md-search-result__link:hover {\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__link:last-child p:last-child {\n margin-bottom: 0.6rem;\n}\n.md-search-result__more summary {\n display: block;\n padding: 0.75em 0.8rem;\n color: var(--md-typeset-a-color);\n font-size: 0.64rem;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__more summary {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__more summary {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__more summary:focus, .md-search-result__more summary:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__more summary::marker, .md-search-result__more summary::-webkit-details-marker {\n display: none;\n}\n.md-search-result__more summary ~ * > * {\n opacity: 0.65;\n}\n.md-search-result__article {\n position: relative;\n padding: 0 0.8rem;\n overflow: hidden;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__article {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__article {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__article--document .md-search-result__title {\n margin: 0.55rem 0;\n font-weight: 400;\n font-size: 0.8rem;\n line-height: 1.4;\n}\n.md-search-result__icon {\n position: absolute;\n left: 0;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.5rem;\n color: var(--md-default-fg-color--light);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search-result__icon {\n display: none;\n }\n}\n.md-search-result__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-search-result__icon {\n right: 0;\n left: initial;\n}\n[dir=rtl] .md-search-result__icon::after {\n transform: scaleX(-1);\n}\n.md-search-result__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: 0.64rem;\n line-height: 1.6;\n}\n.md-search-result__teaser {\n display: -webkit-box;\n max-height: 2rem;\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n}\n@media screen and (max-width: 44.9375em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n.md-search-result__teaser mark {\n text-decoration: underline;\n background-color: transparent;\n}\n.md-search-result__terms {\n margin: 0.5em 0;\n font-size: 0.64rem;\n font-style: italic;\n}\n.md-search-result mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n}\n\n.md-select {\n position: relative;\n z-index: 1;\n}\n.md-select__inner {\n position: absolute;\n top: calc(100% - 0.2rem);\n left: 50%;\n max-height: 0;\n margin-top: 0.2rem;\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n transform: translate3d(-50%, 0.3rem, 0);\n opacity: 0;\n transition: transform 250ms 375ms, opacity 250ms 250ms, max-height 0ms 500ms;\n}\n.md-select:focus-within .md-select__inner, .md-select:hover .md-select__inner {\n max-height: 10rem;\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms, max-height 0ms;\n}\n.md-select__inner::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: -0.2rem;\n margin-left: -0.2rem;\n border: 0.2rem solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n}\n.md-select__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: 0.8rem;\n list-style-type: none;\n border-radius: 0.1rem;\n}\n.md-select__item {\n line-height: 1.8rem;\n}\n.md-select__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n outline: none;\n cursor: pointer;\n transition: background-color 250ms, color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-select__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-select__link:focus, .md-select__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-select__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n.md-sidebar {\n position: sticky;\n top: 2.4rem;\n flex-shrink: 0;\n align-self: flex-start;\n width: 12.1rem;\n padding: 1.2rem 0;\n}\n@media print {\n .md-sidebar {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-sidebar--primary {\n position: fixed;\n top: 0;\n left: -12.1rem;\n z-index: 3;\n display: block;\n width: 12.1rem;\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), box-shadow 250ms;\n }\n [dir=rtl] .md-sidebar--primary {\n right: -12.1rem;\n left: initial;\n }\n [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.4);\n transform: translateX(12.1rem);\n }\n [dir=rtl] [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n transform: translateX(-12.1rem);\n }\n .md-sidebar--primary .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar {\n height: 0;\n }\n .no-js .md-sidebar {\n height: auto;\n }\n}\n.md-sidebar--secondary {\n display: none;\n order: 2;\n}\n@media screen and (min-width: 60em) {\n .md-sidebar--secondary {\n height: 0;\n }\n .no-js .md-sidebar--secondary {\n height: auto;\n }\n .md-sidebar--secondary:not([hidden]) {\n display: block;\n }\n .md-sidebar--secondary .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n}\n.md-sidebar__scrollwrap {\n margin: 0 0.2rem;\n overflow-y: auto;\n backface-visibility: hidden;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-sidebar__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n\n@media screen and (max-width: 76.1875em) {\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 3;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n opacity: 0;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [data-md-toggle=drawer]:checked ~ .md-overlay {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@keyframes md-source__facts--done {\n 0% {\n height: 0;\n }\n 100% {\n height: 0.65rem;\n }\n}\n@keyframes md-source__fact--done {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n.md-source {\n display: block;\n font-size: 0.65rem;\n line-height: 1.2;\n white-space: nowrap;\n outline-color: var(--md-accent-fg-color);\n backface-visibility: hidden;\n transition: opacity 250ms;\n}\n.md-source:hover {\n opacity: 0.7;\n}\n.md-source__icon {\n display: inline-block;\n width: 2rem;\n height: 2.4rem;\n vertical-align: middle;\n}\n.md-source__icon svg {\n margin-top: 0.6rem;\n margin-left: 0.6rem;\n}\n[dir=rtl] .md-source__icon svg {\n margin-right: 0.6rem;\n margin-left: initial;\n}\n.md-source__icon + .md-source__repository {\n margin-left: -2rem;\n padding-left: 2rem;\n}\n[dir=rtl] .md-source__icon + .md-source__repository {\n margin-right: -2rem;\n margin-left: initial;\n padding-right: 2rem;\n padding-left: initial;\n}\n.md-source__repository {\n display: inline-block;\n max-width: calc(100% - 1.2rem);\n margin-left: 0.6rem;\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n}\n.md-source__facts {\n margin: 0.1rem 0 0;\n padding: 0;\n overflow: hidden;\n font-size: 0.55rem;\n list-style-type: none;\n opacity: 0.75;\n}\n[data-md-state=done] .md-source__facts {\n animation: md-source__facts--done 250ms ease-in;\n}\n.md-source__fact {\n display: inline-block;\n}\n[data-md-state=done] .md-source__fact {\n animation: md-source__fact--done 400ms ease-out;\n}\n.md-source__fact::before {\n display: inline-block;\n width: 0.6rem;\n height: 0.6rem;\n margin-right: 0.1rem;\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-source__fact:nth-child(1n+2)::before {\n margin-left: 0.4rem;\n}\n[dir=rtl] .md-source__fact {\n margin-right: initial;\n margin-left: 0.1rem;\n}\n[dir=rtl] .md-source__fact:nth-child(1n+2)::before {\n margin-right: 0.4rem;\n margin-left: initial;\n}\n.md-source__fact--version::before {\n mask-image: var(--md-source-version-icon);\n}\n.md-source__fact--stars::before {\n mask-image: var(--md-source-stars-icon);\n}\n.md-source__fact--forks::before {\n mask-image: var(--md-source-forks-icon);\n}\n.md-source__fact--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n}\n\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n}\n@media print {\n .md-tabs {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-tabs {\n display: none;\n }\n}\n.md-tabs[data-md-state=hidden] {\n pointer-events: none;\n}\n.md-tabs__list {\n margin: 0;\n margin-left: 0.2rem;\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n}\n[dir=rtl] .md-tabs__list {\n margin-right: 0.2rem;\n margin-left: initial;\n}\n.md-tabs__item {\n display: inline-block;\n height: 2.4rem;\n padding-right: 0.6rem;\n padding-left: 0.6rem;\n}\n.md-tabs__link {\n display: block;\n margin-top: 0.8rem;\n font-size: 0.7rem;\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n backface-visibility: hidden;\n opacity: 0.7;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms;\n}\n.md-tabs__link--active, .md-tabs__link:focus, .md-tabs__link:hover {\n color: inherit;\n opacity: 1;\n}\n.md-tabs__item:nth-child(2) .md-tabs__link {\n transition-delay: 20ms;\n}\n.md-tabs__item:nth-child(3) .md-tabs__link {\n transition-delay: 40ms;\n}\n.md-tabs__item:nth-child(4) .md-tabs__link {\n transition-delay: 60ms;\n}\n.md-tabs__item:nth-child(5) .md-tabs__link {\n transition-delay: 80ms;\n}\n.md-tabs__item:nth-child(6) .md-tabs__link {\n transition-delay: 100ms;\n}\n.md-tabs__item:nth-child(7) .md-tabs__link {\n transition-delay: 120ms;\n}\n.md-tabs__item:nth-child(8) .md-tabs__link {\n transition-delay: 140ms;\n}\n.md-tabs__item:nth-child(9) .md-tabs__link {\n transition-delay: 160ms;\n}\n.md-tabs__item:nth-child(10) .md-tabs__link {\n transition-delay: 180ms;\n}\n.md-tabs__item:nth-child(11) .md-tabs__link {\n transition-delay: 200ms;\n}\n.md-tabs__item:nth-child(12) .md-tabs__link {\n transition-delay: 220ms;\n}\n.md-tabs__item:nth-child(13) .md-tabs__link {\n transition-delay: 240ms;\n}\n.md-tabs__item:nth-child(14) .md-tabs__link {\n transition-delay: 260ms;\n}\n.md-tabs__item:nth-child(15) .md-tabs__link {\n transition-delay: 280ms;\n}\n.md-tabs__item:nth-child(16) .md-tabs__link {\n transition-delay: 300ms;\n}\n.md-tabs[data-md-state=hidden] .md-tabs__link {\n transform: translateY(50%);\n opacity: 0;\n transition: transform 0ms 100ms, opacity 100ms;\n}\n\n.md-top {\n position: sticky;\n bottom: 0.4rem;\n z-index: 1;\n float: right;\n margin: -2.8rem 0.4rem 0.4rem;\n padding: 0.4rem;\n color: var(--md-primary-bg-color);\n background: var(--md-primary-fg-color);\n border-radius: 100%;\n outline: none;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.1);\n transform: translateY(0);\n transition: opacity 125ms, transform 125ms cubic-bezier(0.4, 0, 0.2, 1), background-color 125ms;\n}\n[dir=rtl] .md-top {\n float: left;\n}\n.md-top[data-md-state=hidden] {\n transform: translateY(-0.2rem);\n opacity: 0;\n pointer-events: none;\n}\n.md-top:focus, .md-top:hover {\n background: var(--md-accent-fg-color);\n transform: scale(1.1);\n}\n\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n.md-version {\n flex-shrink: 0;\n height: 2.4rem;\n font-size: 0.8rem;\n}\n.md-version__current {\n position: relative;\n top: 0.05rem;\n margin-right: 0.4rem;\n margin-left: 1.4rem;\n color: inherit;\n outline: none;\n cursor: pointer;\n}\n[dir=rtl] .md-version__current {\n margin-right: 1.4rem;\n margin-left: 0.4rem;\n}\n.md-version__current::after {\n display: inline-block;\n width: 0.4rem;\n height: 0.6rem;\n margin-left: 0.4rem;\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n}\n[dir=rtl] .md-version__current::after {\n margin-right: 0.4rem;\n margin-left: initial;\n}\n.md-version__list {\n position: absolute;\n top: 0.15rem;\n z-index: 1;\n max-height: 0;\n margin: 0.2rem 0.8rem;\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n opacity: 0;\n transition: max-height 0ms 500ms, opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n}\n.md-version:focus-within .md-version__list, .md-version:hover .md-version__list {\n max-height: 10rem;\n opacity: 1;\n transition: max-height 0ms, opacity 250ms;\n}\n.md-version__item {\n line-height: 1.8rem;\n}\n.md-version__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-version__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-version__link:focus, .md-version__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-version__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n:root {\n --md-admonition-icon--note:\n svg-load(\"material/pencil.svg\");\n --md-admonition-icon--abstract:\n svg-load(\"material/text-subject.svg\");\n --md-admonition-icon--info:\n svg-load(\"material/information.svg\");\n --md-admonition-icon--tip:\n svg-load(\"material/fire.svg\");\n --md-admonition-icon--success:\n svg-load(\"material/check-circle.svg\");\n --md-admonition-icon--question:\n svg-load(\"material/help-circle.svg\");\n --md-admonition-icon--warning:\n svg-load(\"material/alert.svg\");\n --md-admonition-icon--failure:\n svg-load(\"material/close-circle.svg\");\n --md-admonition-icon--danger:\n svg-load(\"material/flash-circle.svg\");\n --md-admonition-icon--bug:\n svg-load(\"material/bug.svg\");\n --md-admonition-icon--example:\n svg-load(\"material/format-list-numbered.svg\");\n --md-admonition-icon--quote:\n svg-load(\"material/format-quote-close.svg\");\n}\n\n.md-typeset .admonition, .md-typeset details {\n margin: 1.5625em 0;\n padding: 0 0.6rem;\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: 0.64rem;\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: 0.2rem solid #448aff;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.05);\n}\n@media print {\n .md-typeset .admonition, .md-typeset details {\n box-shadow: none;\n }\n}\n[dir=rtl] .md-typeset .admonition, [dir=rtl] .md-typeset details {\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\n.md-typeset .admonition .admonition, .md-typeset details .admonition, .md-typeset .admonition details, .md-typeset details details {\n margin-top: 1em;\n margin-bottom: 1em;\n}\n.md-typeset .admonition .md-typeset__scrollwrap, .md-typeset details .md-typeset__scrollwrap {\n margin: 1em -0.6rem;\n}\n.md-typeset .admonition .md-typeset__table, .md-typeset details .md-typeset__table {\n padding: 0 0.6rem;\n}\n.md-typeset .admonition > .tabbed-set:only-child, .md-typeset details > .tabbed-set:only-child {\n margin-top: 0;\n}\nhtml .md-typeset .admonition > :last-child, html .md-typeset details > :last-child {\n margin-bottom: 0.6rem;\n}\n.md-typeset .admonition-title, .md-typeset summary {\n position: relative;\n margin: 0 -0.6rem 0 -0.8rem;\n padding: 0.4rem 0.6rem 0.4rem 2rem;\n font-weight: 700;\n background-color: rgba(68, 138, 255, 0.1);\n border-left: 0.2rem solid #448aff;\n}\n[dir=rtl] .md-typeset .admonition-title, [dir=rtl] .md-typeset summary {\n margin: 0 -0.8rem 0 -0.6rem;\n padding: 0.4rem 2rem 0.4rem 0.6rem;\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\nhtml .md-typeset .admonition-title:last-child, html .md-typeset summary:last-child {\n margin-bottom: 0;\n}\n.md-typeset .admonition-title::before, .md-typeset summary::before {\n position: absolute;\n left: 0.6rem;\n width: 1rem;\n height: 1rem;\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .admonition-title::before, [dir=rtl] .md-typeset summary::before {\n right: 0.6rem;\n left: initial;\n}\n.md-typeset .admonition-title + .tabbed-set:last-child, .md-typeset summary + .tabbed-set:last-child {\n margin-top: 0;\n}\n\n.md-typeset .admonition.note, .md-typeset details.note {\n border-color: #448aff;\n}\n\n.md-typeset .note > .admonition-title, .md-typeset .note > summary {\n background-color: rgba(68, 138, 255, 0.1);\n border-color: #448aff;\n}\n.md-typeset .note > .admonition-title::before, .md-typeset .note > summary::before {\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.abstract, .md-typeset details.abstract, .md-typeset .admonition.tldr, .md-typeset details.tldr, .md-typeset .admonition.summary, .md-typeset details.summary {\n border-color: #00b0ff;\n}\n\n.md-typeset .abstract > .admonition-title, .md-typeset .abstract > summary, .md-typeset .tldr > .admonition-title, .md-typeset .tldr > summary, .md-typeset .summary > .admonition-title, .md-typeset .summary > summary {\n background-color: rgba(0, 176, 255, 0.1);\n border-color: #00b0ff;\n}\n.md-typeset .abstract > .admonition-title::before, .md-typeset .abstract > summary::before, .md-typeset .tldr > .admonition-title::before, .md-typeset .tldr > summary::before, .md-typeset .summary > .admonition-title::before, .md-typeset .summary > summary::before {\n background-color: #00b0ff;\n mask-image: var(--md-admonition-icon--abstract);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.info, .md-typeset details.info, .md-typeset .admonition.todo, .md-typeset details.todo {\n border-color: #00b8d4;\n}\n\n.md-typeset .info > .admonition-title, .md-typeset .info > summary, .md-typeset .todo > .admonition-title, .md-typeset .todo > summary {\n background-color: rgba(0, 184, 212, 0.1);\n border-color: #00b8d4;\n}\n.md-typeset .info > .admonition-title::before, .md-typeset .info > summary::before, .md-typeset .todo > .admonition-title::before, .md-typeset .todo > summary::before {\n background-color: #00b8d4;\n mask-image: var(--md-admonition-icon--info);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.tip, .md-typeset details.tip, .md-typeset .admonition.important, .md-typeset details.important, .md-typeset .admonition.hint, .md-typeset details.hint {\n border-color: #00bfa5;\n}\n\n.md-typeset .tip > .admonition-title, .md-typeset .tip > summary, .md-typeset .important > .admonition-title, .md-typeset .important > summary, .md-typeset .hint > .admonition-title, .md-typeset .hint > summary {\n background-color: rgba(0, 191, 165, 0.1);\n border-color: #00bfa5;\n}\n.md-typeset .tip > .admonition-title::before, .md-typeset .tip > summary::before, .md-typeset .important > .admonition-title::before, .md-typeset .important > summary::before, .md-typeset .hint > .admonition-title::before, .md-typeset .hint > summary::before {\n background-color: #00bfa5;\n mask-image: var(--md-admonition-icon--tip);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.success, .md-typeset details.success, .md-typeset .admonition.done, .md-typeset details.done, .md-typeset .admonition.check, .md-typeset details.check {\n border-color: #00c853;\n}\n\n.md-typeset .success > .admonition-title, .md-typeset .success > summary, .md-typeset .done > .admonition-title, .md-typeset .done > summary, .md-typeset .check > .admonition-title, .md-typeset .check > summary {\n background-color: rgba(0, 200, 83, 0.1);\n border-color: #00c853;\n}\n.md-typeset .success > .admonition-title::before, .md-typeset .success > summary::before, .md-typeset .done > .admonition-title::before, .md-typeset .done > summary::before, .md-typeset .check > .admonition-title::before, .md-typeset .check > summary::before {\n background-color: #00c853;\n mask-image: var(--md-admonition-icon--success);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.question, .md-typeset details.question, .md-typeset .admonition.faq, .md-typeset details.faq, .md-typeset .admonition.help, .md-typeset details.help {\n border-color: #64dd17;\n}\n\n.md-typeset .question > .admonition-title, .md-typeset .question > summary, .md-typeset .faq > .admonition-title, .md-typeset .faq > summary, .md-typeset .help > .admonition-title, .md-typeset .help > summary {\n background-color: rgba(100, 221, 23, 0.1);\n border-color: #64dd17;\n}\n.md-typeset .question > .admonition-title::before, .md-typeset .question > summary::before, .md-typeset .faq > .admonition-title::before, .md-typeset .faq > summary::before, .md-typeset .help > .admonition-title::before, .md-typeset .help > summary::before {\n background-color: #64dd17;\n mask-image: var(--md-admonition-icon--question);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.warning, .md-typeset details.warning, .md-typeset .admonition.attention, .md-typeset details.attention, .md-typeset .admonition.caution, .md-typeset details.caution {\n border-color: #ff9100;\n}\n\n.md-typeset .warning > .admonition-title, .md-typeset .warning > summary, .md-typeset .attention > .admonition-title, .md-typeset .attention > summary, .md-typeset .caution > .admonition-title, .md-typeset .caution > summary {\n background-color: rgba(255, 145, 0, 0.1);\n border-color: #ff9100;\n}\n.md-typeset .warning > .admonition-title::before, .md-typeset .warning > summary::before, .md-typeset .attention > .admonition-title::before, .md-typeset .attention > summary::before, .md-typeset .caution > .admonition-title::before, .md-typeset .caution > summary::before {\n background-color: #ff9100;\n mask-image: var(--md-admonition-icon--warning);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.failure, .md-typeset details.failure, .md-typeset .admonition.missing, .md-typeset details.missing, .md-typeset .admonition.fail, .md-typeset details.fail {\n border-color: #ff5252;\n}\n\n.md-typeset .failure > .admonition-title, .md-typeset .failure > summary, .md-typeset .missing > .admonition-title, .md-typeset .missing > summary, .md-typeset .fail > .admonition-title, .md-typeset .fail > summary {\n background-color: rgba(255, 82, 82, 0.1);\n border-color: #ff5252;\n}\n.md-typeset .failure > .admonition-title::before, .md-typeset .failure > summary::before, .md-typeset .missing > .admonition-title::before, .md-typeset .missing > summary::before, .md-typeset .fail > .admonition-title::before, .md-typeset .fail > summary::before {\n background-color: #ff5252;\n mask-image: var(--md-admonition-icon--failure);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.danger, .md-typeset details.danger, .md-typeset .admonition.error, .md-typeset details.error {\n border-color: #ff1744;\n}\n\n.md-typeset .danger > .admonition-title, .md-typeset .danger > summary, .md-typeset .error > .admonition-title, .md-typeset .error > summary {\n background-color: rgba(255, 23, 68, 0.1);\n border-color: #ff1744;\n}\n.md-typeset .danger > .admonition-title::before, .md-typeset .danger > summary::before, .md-typeset .error > .admonition-title::before, .md-typeset .error > summary::before {\n background-color: #ff1744;\n mask-image: var(--md-admonition-icon--danger);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.bug, .md-typeset details.bug {\n border-color: #f50057;\n}\n\n.md-typeset .bug > .admonition-title, .md-typeset .bug > summary {\n background-color: rgba(245, 0, 87, 0.1);\n border-color: #f50057;\n}\n.md-typeset .bug > .admonition-title::before, .md-typeset .bug > summary::before {\n background-color: #f50057;\n mask-image: var(--md-admonition-icon--bug);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.example, .md-typeset details.example {\n border-color: #7c4dff;\n}\n\n.md-typeset .example > .admonition-title, .md-typeset .example > summary {\n background-color: rgba(124, 77, 255, 0.1);\n border-color: #7c4dff;\n}\n.md-typeset .example > .admonition-title::before, .md-typeset .example > summary::before {\n background-color: #7c4dff;\n mask-image: var(--md-admonition-icon--example);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.quote, .md-typeset details.quote, .md-typeset .admonition.cite, .md-typeset details.cite {\n border-color: #9e9e9e;\n}\n\n.md-typeset .quote > .admonition-title, .md-typeset .quote > summary, .md-typeset .cite > .admonition-title, .md-typeset .cite > summary {\n background-color: rgba(158, 158, 158, 0.1);\n border-color: #9e9e9e;\n}\n.md-typeset .quote > .admonition-title::before, .md-typeset .quote > summary::before, .md-typeset .cite > .admonition-title::before, .md-typeset .cite > summary::before {\n background-color: #9e9e9e;\n mask-image: var(--md-admonition-icon--quote);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n.md-typeset [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n}\n.md-typeset [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n}\n.md-typeset .footnote {\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n}\n.md-typeset .footnote > ol {\n margin-left: 0;\n}\n.md-typeset .footnote > ol > li {\n transition: color 125ms;\n}\n.md-typeset .footnote > ol > li:target {\n color: var(--md-default-fg-color);\n}\n.md-typeset .footnote > ol > li:hover .footnote-backref, .md-typeset .footnote > ol > li:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n}\n.md-typeset .footnote > ol > li > :first-child {\n margin-top: 0;\n}\n.md-typeset .footnote-ref {\n font-weight: 700;\n font-size: 0.75em;\n}\n.md-typeset .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(0.25rem);\n opacity: 0;\n transition: color 250ms, transform 250ms 250ms, opacity 125ms 250ms;\n}\n@media print {\n .md-typeset .footnote-backref {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n}\n[dir=rtl] .md-typeset .footnote-backref {\n transform: translateX(-0.25rem);\n}\n.md-typeset .footnote-backref:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset .footnote-backref::before {\n display: inline-block;\n width: 0.8rem;\n height: 0.8rem;\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .footnote-backref::before svg {\n transform: scaleX(-1);\n}\n\n.md-typeset .headerlink {\n display: inline-block;\n margin-left: 0.5rem;\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition: color 250ms, opacity 125ms;\n}\n@media print {\n .md-typeset .headerlink {\n display: none;\n }\n}\n[dir=rtl] .md-typeset .headerlink {\n margin-right: 0.5rem;\n margin-left: initial;\n}\n.md-typeset :hover > .headerlink,\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus {\n opacity: 1;\n transition: color 250ms, opacity 125ms;\n}\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus,\n.md-typeset .headerlink:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset :target {\n scroll-margin-top: 3.6rem;\n}\n.md-typeset h1:target,\n.md-typeset h2:target,\n.md-typeset h3:target {\n scroll-margin-top: initial;\n}\n.md-typeset h1:target::before,\n.md-typeset h2:target::before,\n.md-typeset h3:target::before {\n display: block;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n content: \"\";\n}\n.md-typeset h4:target {\n scroll-margin-top: initial;\n}\n.md-typeset h4:target::before {\n display: block;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n content: \"\";\n}\n.md-typeset h5:target,\n.md-typeset h6:target {\n scroll-margin-top: initial;\n}\n.md-typeset h5:target::before,\n.md-typeset h6:target::before {\n display: block;\n margin-top: -3.6rem;\n padding-top: 3.6rem;\n content: \"\";\n}\n\n.md-typeset div.arithmatex {\n overflow: auto;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset div.arithmatex {\n margin: 0 -0.8rem;\n }\n}\n.md-typeset div.arithmatex > * {\n width: min-content;\n margin: 1em auto !important;\n padding: 0 0.8rem;\n touch-action: auto;\n}\n\n.md-typeset del.critic,\n.md-typeset ins.critic,\n.md-typeset .critic.comment {\n box-decoration-break: clone;\n}\n.md-typeset del.critic {\n background-color: var(--md-typeset-del-color);\n}\n.md-typeset ins.critic {\n background-color: var(--md-typeset-ins-color);\n}\n.md-typeset .critic.comment {\n color: var(--md-code-hl-comment-color);\n}\n.md-typeset .critic.comment::before {\n content: \"/* \";\n}\n.md-typeset .critic.comment::after {\n content: \" */\";\n}\n.md-typeset .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: 0.8rem;\n padding-left: 0.8rem;\n overflow: auto;\n box-shadow: none;\n}\n.md-typeset .critic.block > :first-child {\n margin-top: 0.5em;\n}\n.md-typeset .critic.block > :last-child {\n margin-bottom: 0.5em;\n}\n\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n.md-typeset details {\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n}\n.md-typeset details[open] > summary::after {\n transform: rotate(90deg);\n}\n.md-typeset details:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n}\n.md-typeset details:not([open]) > summary {\n border-radius: 0.1rem;\n}\n.md-typeset details::after {\n display: table;\n content: \"\";\n}\n.md-typeset summary {\n display: block;\n min-height: 1rem;\n padding: 0.4rem 1.8rem 0.4rem 2rem;\n border-top-left-radius: 0.1rem;\n border-top-right-radius: 0.1rem;\n cursor: pointer;\n}\n[dir=rtl] .md-typeset summary {\n padding: 0.4rem 2.2rem 0.4rem 1.8rem;\n}\n.md-typeset summary:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset summary::after {\n position: absolute;\n top: 0.4rem;\n right: 0.4rem;\n width: 1rem;\n height: 1rem;\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n}\n[dir=rtl] .md-typeset summary::after {\n right: initial;\n left: 0.4rem;\n transform: rotate(180deg);\n}\n.md-typeset summary::marker, .md-typeset summary::-webkit-details-marker {\n display: none;\n}\n\n.md-typeset .emojione,\n.md-typeset .twemoji,\n.md-typeset .gemoji {\n display: inline-flex;\n height: 1.125em;\n vertical-align: text-top;\n}\n.md-typeset .emojione svg,\n.md-typeset .twemoji svg,\n.md-typeset .gemoji svg {\n width: 1.125em;\n max-height: 100%;\n fill: currentColor;\n}\n\n.highlight .o,\n.highlight .ow {\n color: var(--md-code-hl-operator-color);\n}\n.highlight .p {\n color: var(--md-code-hl-punctuation-color);\n}\n.highlight .cpf,\n.highlight .l,\n.highlight .s,\n.highlight .sb,\n.highlight .sc,\n.highlight .s2,\n.highlight .si,\n.highlight .s1,\n.highlight .ss {\n color: var(--md-code-hl-string-color);\n}\n.highlight .cp,\n.highlight .se,\n.highlight .sh,\n.highlight .sr,\n.highlight .sx {\n color: var(--md-code-hl-special-color);\n}\n.highlight .m,\n.highlight .mb,\n.highlight .mf,\n.highlight .mh,\n.highlight .mi,\n.highlight .il,\n.highlight .mo {\n color: var(--md-code-hl-number-color);\n}\n.highlight .k,\n.highlight .kd,\n.highlight .kn,\n.highlight .kp,\n.highlight .kr,\n.highlight .kt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .kc,\n.highlight .n {\n color: var(--md-code-hl-name-color);\n}\n.highlight .no,\n.highlight .nb,\n.highlight .bp {\n color: var(--md-code-hl-constant-color);\n}\n.highlight .nc,\n.highlight .ne,\n.highlight .nf,\n.highlight .nn {\n color: var(--md-code-hl-function-color);\n}\n.highlight .nd,\n.highlight .ni,\n.highlight .nl,\n.highlight .nt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .c,\n.highlight .cm,\n.highlight .c1,\n.highlight .ch,\n.highlight .cs,\n.highlight .sd {\n color: var(--md-code-hl-comment-color);\n}\n.highlight .na,\n.highlight .nv,\n.highlight .vc,\n.highlight .vg,\n.highlight .vi {\n color: var(--md-code-hl-variable-color);\n}\n.highlight .ge,\n.highlight .gr,\n.highlight .gh,\n.highlight .go,\n.highlight .gp,\n.highlight .gs,\n.highlight .gu,\n.highlight .gt {\n color: var(--md-code-hl-generic-color);\n}\n.highlight .gd,\n.highlight .gi {\n margin: 0 -0.125em;\n padding: 0 0.125em;\n border-radius: 0.1rem;\n}\n.highlight .gd {\n background-color: var(--md-typeset-del-color);\n}\n.highlight .gi {\n background-color: var(--md-typeset-ins-color);\n}\n.highlight .hll {\n display: block;\n margin: 0 -1.1764705882em;\n padding: 0 1.1764705882em;\n background-color: var(--md-code-hl-color);\n}\n.highlight [data-linenos]::before {\n position: sticky;\n left: -1.1764705882em;\n float: left;\n margin-right: 1.1764705882em;\n margin-left: -1.1764705882em;\n padding-left: 1.1764705882em;\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n}\n\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n}\n.highlighttable tbody,\n.highlighttable td {\n display: block;\n padding: 0;\n}\n.highlighttable tr {\n display: flex;\n}\n.highlighttable pre {\n margin: 0;\n}\n.highlighttable .linenos {\n padding: 0.7720588235em 1.1764705882em;\n padding-right: 0;\n font-size: 0.85em;\n background-color: var(--md-code-bg-color);\n user-select: none;\n}\n.highlighttable .linenodiv {\n padding-right: 0.5882352941em;\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n}\n.highlighttable .linenodiv pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n}\n.highlighttable .code {\n flex: 1;\n overflow: hidden;\n}\n\n.md-typeset .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: 0.1rem;\n}\n.md-typeset .highlighttable code {\n border-radius: 0;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > .highlight {\n margin: 1em -0.8rem;\n }\n .md-typeset > .highlight .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n .md-typeset > .highlight code {\n border-radius: 0;\n }\n .md-typeset > .highlighttable {\n margin: 1em -0.8rem;\n border-radius: 0;\n }\n .md-typeset > .highlighttable .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n}\n\n.md-typeset .keys kbd::before,\n.md-typeset .keys kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n}\n.md-typeset .keys span {\n padding: 0 0.2em;\n color: var(--md-default-fg-color--light);\n}\n.md-typeset .keys .key-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-left-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-right-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-left-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-right-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-left-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-right-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-left-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-right-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-left-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-right-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-left-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-right-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-left-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-right-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-left-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-right-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-arrow-down::before {\n padding-right: 0.4em;\n content: \"↓\";\n}\n.md-typeset .keys .key-arrow-left::before {\n padding-right: 0.4em;\n content: \"←\";\n}\n.md-typeset .keys .key-arrow-right::before {\n padding-right: 0.4em;\n content: \"→\";\n}\n.md-typeset .keys .key-arrow-up::before {\n padding-right: 0.4em;\n content: \"↑\";\n}\n.md-typeset .keys .key-backspace::before {\n padding-right: 0.4em;\n content: \"⌫\";\n}\n.md-typeset .keys .key-backtab::before {\n padding-right: 0.4em;\n content: \"⇤\";\n}\n.md-typeset .keys .key-caps-lock::before {\n padding-right: 0.4em;\n content: \"⇪\";\n}\n.md-typeset .keys .key-clear::before {\n padding-right: 0.4em;\n content: \"⌧\";\n}\n.md-typeset .keys .key-context-menu::before {\n padding-right: 0.4em;\n content: \"☰\";\n}\n.md-typeset .keys .key-delete::before {\n padding-right: 0.4em;\n content: \"⌦\";\n}\n.md-typeset .keys .key-eject::before {\n padding-right: 0.4em;\n content: \"⏏\";\n}\n.md-typeset .keys .key-end::before {\n padding-right: 0.4em;\n content: \"⤓\";\n}\n.md-typeset .keys .key-escape::before {\n padding-right: 0.4em;\n content: \"⎋\";\n}\n.md-typeset .keys .key-home::before {\n padding-right: 0.4em;\n content: \"⤒\";\n}\n.md-typeset .keys .key-insert::before {\n padding-right: 0.4em;\n content: \"⎀\";\n}\n.md-typeset .keys .key-page-down::before {\n padding-right: 0.4em;\n content: \"⇟\";\n}\n.md-typeset .keys .key-page-up::before {\n padding-right: 0.4em;\n content: \"⇞\";\n}\n.md-typeset .keys .key-print-screen::before {\n padding-right: 0.4em;\n content: \"⎙\";\n}\n.md-typeset .keys .key-tab::after {\n padding-left: 0.4em;\n content: \"⇥\";\n}\n.md-typeset .keys .key-num-enter::after {\n padding-left: 0.4em;\n content: \"⌤\";\n}\n.md-typeset .keys .key-enter::after {\n padding-left: 0.4em;\n content: \"⏎\";\n}\n\n.md-typeset .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 -0.05rem var(--md-default-fg-color--lightest);\n}\n@media print {\n .md-typeset .tabbed-content {\n display: block;\n order: initial;\n }\n}\n.md-typeset .tabbed-content > pre:only-child,\n.md-typeset .tabbed-content > .highlight:only-child pre,\n.md-typeset .tabbed-content > .highlighttable:only-child {\n margin: 0;\n}\n.md-typeset .tabbed-content > pre:only-child > code,\n.md-typeset .tabbed-content > .highlight:only-child pre > code,\n.md-typeset .tabbed-content > .highlighttable:only-child > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.md-typeset .tabbed-content > .tabbed-set {\n margin: 0;\n}\n.md-typeset .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: 0.1rem;\n}\n.md-typeset .tabbed-set > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-typeset .tabbed-set > input:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .tabbed-set > input:checked + label + .tabbed-content {\n display: block;\n}\n.md-typeset .tabbed-set > input:focus + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n}\n.md-typeset .tabbed-set > input:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset .tabbed-set > label {\n z-index: 1;\n width: auto;\n padding: 0.9375em 1.25em 0.78125em;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.64rem;\n border-bottom: 0.1rem solid transparent;\n cursor: pointer;\n transition: color 250ms;\n}\n.md-typeset .tabbed-set > label:hover {\n color: var(--md-accent-fg-color);\n}\n\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n.md-typeset .task-list-item {\n position: relative;\n list-style-type: none;\n}\n.md-typeset .task-list-item [type=checkbox] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n}\n[dir=rtl] .md-typeset .task-list-item [type=checkbox] {\n right: -2em;\n left: initial;\n}\n.md-typeset .task-list-control [type=checkbox] {\n z-index: -1;\n opacity: 0;\n}\n.md-typeset .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: -1.5em;\n width: 1.25em;\n height: 1.25em;\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .task-list-indicator::before {\n right: -1.5em;\n left: initial;\n}\n.md-typeset [type=checkbox]:checked + .task-list-indicator::before {\n background-color: #00e676;\n mask-image: var(--md-tasklist-icon--checked);\n}\n\n@media screen and (min-width: 45em) {\n .md-typeset .inline {\n float: left;\n width: 11.7rem;\n margin-top: 0;\n margin-right: 0.8rem;\n margin-bottom: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n .md-typeset .inline.end {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline.end {\n float: left;\n margin-right: 0.8rem;\n margin-left: 0;\n }\n}\n\n/*# sourceMappingURL=main.css.map */","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Enforce correct box model and prevent adjustments of font size after\n// orientation changes in IE and iOS\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n// All elements shall inherit the document default\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n// Remove margin in all browsers\nbody {\n margin: 0;\n}\n\n// Reset tap outlines on iOS and Android\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\n// Reset link styles\na {\n color: inherit;\n text-decoration: none;\n}\n\n// Normalize horizontal separator styles\nhr {\n display: block;\n box-sizing: content-box;\n height: px2rem(1px);\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\n// Normalize font-size in all browsers\nsmall {\n font-size: 80%;\n}\n\n// Prevent subscript and superscript from affecting line-height\nsub,\nsup {\n line-height: 1em;\n}\n\n// Remove border on image\nimg {\n border-style: none;\n}\n\n// Reset table styles\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\n// Reset table cell styles\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\n// Reset button styles\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\n// Reset input styles\ninput {\n border: 0;\n outline: none;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Color definitions\n:root {\n\n // Default color shades\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n\n // Primary color shades\n --md-primary-fg-color: hsla(#{hex2hsl($clr-indigo-500)}, 1);\n --md-primary-fg-color--light: hsla(#{hex2hsl($clr-indigo-400)}, 1);\n --md-primary-fg-color--dark: hsla(#{hex2hsl($clr-indigo-700)}, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Accent color shades\n --md-accent-fg-color: hsla(#{hex2hsl($clr-indigo-a200)}, 1);\n --md-accent-fg-color--transparent: hsla(#{hex2hsl($clr-indigo-a200)}, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Light theme (default)\n > * {\n\n // Code color shades\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n\n // Code highlighting color shades\n --md-code-hl-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n\n // Typeset color shades\n --md-typeset-color: var(--md-default-fg-color);\n --md-typeset-a-color: var(--md-primary-fg-color);\n\n // Typeset `mark` color shades\n --md-typeset-mark-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n\n // Typeset `del` and `ins` color shades\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n\n // Typeset `kbd` color shades\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n\n // Admonition color shades\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n\n // Footer color shades\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon\n.md-icon {\n\n // SVG defaults\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: font definitions\n// ----------------------------------------------------------------------------\n\n// Enable font-smoothing in Webkit and FF\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Define default fonts\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family:\n var(--md-text-font-family, _),\n -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\n// Define monospaced fonts\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family:\n var(--md-code-font-family, _),\n SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: typesetted content\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-typeset-table--ascending: svg-load(\"material/arrow-down.svg\");\n --md-typeset-table--descending: svg-load(\"material/arrow-up.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Content that is typeset - if possible, all margins, paddings and font sizes\n// should be set in ems, so nested blocks (e.g. admonitions) render correctly.\n.md-typeset {\n font-size: px2rem(16px);\n line-height: 1.6;\n color-adjust: exact;\n\n // [print]: We'll use a smaller `font-size` for printing, so code examples\n // don't break too early, and `16px` looks too big anyway.\n @media print {\n font-size: px2rem(13.6px);\n }\n\n // Default spacing\n ul,\n ol,\n dl,\n figure,\n blockquote,\n pre {\n margin: 1em 0;\n }\n\n // Headline on level 1\n h1 {\n margin: 0 0 px2em(40px, 32px);\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: px2em(32px);\n line-height: 1.3;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 2\n h2 {\n margin: px2em(40px, 25px) 0 px2em(16px, 25px);\n font-weight: 300;\n font-size: px2em(25px);\n line-height: 1.4;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3\n h3 {\n margin: px2em(32px, 20px) 0 px2em(16px, 20px);\n font-weight: 400;\n font-size: px2em(20px);\n line-height: 1.5;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3 following level 2\n h2 + h3 {\n margin-top: px2em(16px, 20px);\n }\n\n // Headline on level 4\n h4 {\n margin: px2em(16px) 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5-6\n h5,\n h6 {\n margin: px2em(16px, 12.8px) 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2em(12.8px);\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5\n h5 {\n text-transform: uppercase;\n }\n\n // Horizontal separator\n hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: px2rem(1px) solid var(--md-default-fg-color--lightest);\n }\n\n // Text link\n a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n\n // Also enable color transition on pseudo elements\n &,\n &::before {\n transition: color 125ms;\n }\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Text link on keyboard focus\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n }\n\n // Code block\n code,\n pre,\n kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n\n // [print]: Wrap text and hide scollbars\n @media print {\n white-space: pre-wrap;\n }\n }\n\n // Inline code block\n code {\n padding: 0 px2em(4px, 13.6px);\n font-size: px2em(13.6px);\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: px2rem(2px);\n box-decoration-break: clone;\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Code block in headline\n h1 code,\n h2 code,\n h3 code,\n h4 code,\n h5 code,\n h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n }\n\n // Ensure link color in code blocks\n a code {\n color: currentColor;\n }\n\n // Unformatted content\n pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n\n // Code block\n > code {\n display: block;\n margin: 0;\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Code block on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Unformatted text\n > pre {\n margin: 1em px2rem(-16px);\n\n // Code block\n code {\n border-radius: 0;\n }\n }\n }\n\n // Keyboard key\n kbd {\n display: inline-block;\n padding: 0 px2em(8px, 12px);\n color: var(--md-default-fg-color);\n font-size: px2em(12px);\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(2px) 0 px2rem(1px) var(--md-typeset-kbd-border-color),\n 0 px2rem(2px) 0 var(--md-typeset-kbd-border-color),\n 0 px2rem(-2px) px2rem(4px) var(--md-typeset-kbd-accent-color) inset;\n }\n\n // Text highlighting marker\n mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n }\n\n // Abbreviation\n abbr {\n text-decoration: none;\n border-bottom: px2rem(1px) dotted var(--md-default-fg-color--light);\n cursor: help;\n\n // Show tooltip for touch devices\n @media (hover: none) {\n position: relative;\n\n // Tooltip\n &[title]:focus::after,\n &[title]:hover::after {\n @include z-depth(2);\n\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: px2rem(4px) px2rem(6px);\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n content: attr(title);\n }\n }\n }\n\n // Small text\n small {\n opacity: 0.75;\n }\n\n // Superscript and subscript\n sup,\n sub {\n margin-left: px2em(1px, 12.8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(1px, 12.8px);\n margin-left: initial;\n }\n }\n\n // Blockquotes, possibly nested\n blockquote {\n display: flow-root;\n padding-left: px2rem(12px);\n color: var(--md-default-fg-color--light);\n border-left: px2rem(4px) solid var(--md-default-fg-color--lighter);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: initial;\n border-right: px2rem(4px) solid var(--md-default-fg-color--lighter);\n border-left: initial;\n }\n }\n\n // Unordered list\n ul {\n list-style-type: disc;\n }\n\n // Unordered and ordered list\n ul,\n ol {\n display: flow-root;\n margin-left: px2em(10px);\n padding: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n\n // Nested ordered list\n ol {\n list-style-type: lower-alpha;\n\n // Triply nested ordered list\n ol {\n list-style-type: lower-roman;\n }\n }\n\n // List element\n li {\n margin-bottom: 0.5em;\n margin-left: px2em(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(20px);\n margin-left: initial;\n }\n\n // Adjust spacing\n p,\n blockquote {\n margin: 0.5em 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Nested list\n ul,\n ol {\n margin: 0.5em 0 0.5em px2em(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n }\n }\n }\n\n // Definition list\n dd {\n margin: 1em 0 1.5em px2em(30px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(30px);\n margin-left: initial;\n }\n }\n\n // Image or icon\n img,\n svg {\n max-width: 100%;\n height: auto;\n\n // Adjust spacing when left-aligned\n &[align=\"left\"] {\n margin: 1em;\n margin-left: 0;\n }\n\n // Adjust spacing when right-aligned\n &[align=\"right\"] {\n margin: 1em;\n margin-right: 0;\n }\n\n // Adjust spacing when sole children\n &[align]:only-child {\n margin-top: 0;\n }\n }\n\n // Figure\n figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n\n // Figure images\n img {\n display: block;\n }\n }\n\n // Figure caption\n figcaption {\n max-width: px2rem(480px);\n margin: 1em auto 2em;\n font-style: italic;\n }\n\n // Limit width to container\n iframe {\n max-width: 100%;\n }\n\n // Data table\n table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: px2rem(12.8px);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.1);\n touch-action: auto;\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: table;\n }\n\n // Due to margin collapse because of the necessary inline-block hack, we\n // cannot increase the bottom margin on the table, so we just increase the\n // top margin on the following element\n + * {\n margin-top: 1.5em;\n }\n\n // Elements in table heading and cell\n th > *,\n td > * {\n\n // Adjust spacing on first child\n &:first-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Table heading and cell\n th:not([align]),\n td:not([align]) {\n text-align: left;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n text-align: right;\n }\n }\n\n // Table heading\n th {\n min-width: px2rem(100px);\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n color: var(--md-default-bg-color);\n vertical-align: top;\n background-color: var(--md-default-fg-color--light);\n\n // Links in table headings\n a {\n color: inherit;\n }\n }\n\n // Table cell\n td {\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n vertical-align: top;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n }\n\n // Table row\n tr {\n transition: background-color 125ms;\n\n // Table row on hover\n &:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 px2rem(1px) 0 var(--md-default-bg-color) inset;\n }\n\n // Hide border on first table row\n &:first-child td {\n border-top: 0;\n }\n }\n\n // Text link in table\n a {\n word-break: normal;\n }\n }\n\n // Sortable table\n table th[role=\"columnheader\"] {\n cursor: pointer;\n\n // Sort icon\n &::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: sub;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Sort ascending\n &[aria-sort=\"ascending\"]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--ascending);\n }\n\n // Sort descending\n &[aria-sort=\"descending\"]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--descending);\n }\n }\n\n // Data table scroll wrapper\n &__scrollwrap {\n margin: 1em px2rem(-16px);\n overflow-x: auto;\n touch-action: auto;\n }\n\n // Data table wrapper\n &__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 px2rem(16px);\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: block;\n }\n\n // Data table\n html & table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Device-specific breakpoints\n///\n/// @example\n/// $break-devices: (\n/// mobile: (\n/// portrait: 220px 479px,\n/// landscape: 480px 719px\n/// ),\n/// tablet: (\n/// portrait: 720px 959px,\n/// landscape: 960px 1219px\n/// ),\n/// screen: (\n/// small: 1220px 1599px,\n/// medium: 1600px 1999px,\n/// large: 2000px\n/// )\n/// );\n///\n$break-devices: () !default;\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\n///\n/// Choose minimum and maximum device widths\n///\n@function break-select-min-max($devices) {\n $min: 1000000;\n $max: 0;\n @each $key, $value in $devices {\n @while type-of($value) == map {\n $value: break-select-min-max($value);\n }\n @if type-of($value) == list {\n @each $number in $value {\n @if type-of($number) == number {\n $min: min($number, $min);\n @if $max {\n $max: max($number, $max);\n }\n } @else {\n @error \"Invalid number: #{$number}\";\n }\n }\n } @else if type-of($value) == number {\n $min: min($value, $min);\n $max: null;\n } @else {\n @error \"Invalid value: #{$value}\";\n }\n }\n @return $min, $max;\n}\n\n///\n/// Select minimum and maximum widths for a device breakpoint\n///\n@function break-select-device($device) {\n $current: $break-devices;\n @for $n from 1 through length($device) {\n @if type-of($current) == map {\n $current: map-get($current, nth($device, $n));\n } @else {\n @error \"Invalid device map: #{$devices}\";\n }\n }\n @if type-of($current) == list or type-of($current) == number {\n $current: (default: $current);\n }\n @return break-select-min-max($current);\n}\n\n// ----------------------------------------------------------------------------\n// Mixins\n// ----------------------------------------------------------------------------\n\n///\n/// A minimum-maximum media query breakpoint\n///\n@mixin break-at($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (min-width: $breakpoint) {\n @content;\n }\n } @else if type-of($breakpoint) == list {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @if type-of($min) == number and type-of($max) == number {\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// An orientation media query breakpoint\n///\n@mixin break-at-orientation($breakpoint) {\n @if type-of($breakpoint) == string {\n @media screen and (orientation: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A maximum-aspect-ratio media query breakpoint\n///\n@mixin break-at-ratio($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (max-aspect-ratio: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A minimum-maximum media query device breakpoint\n///\n@mixin break-at-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n @if nth($breakpoint, 2) {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A minimum media query device breakpoint\n///\n@mixin break-from-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $min: nth($breakpoint, 1);\n\n @media screen and (min-width: $min) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A maximum media query device breakpoint\n///\n@mixin break-to-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $max: nth($breakpoint, 2);\n\n @media screen and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n","//\n// Name: Material Shadows\n// Description: Mixins for Material Design Shadows.\n// Version: 3.0.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-shadows\n//\n// twitter: @mrmlnc\n//\n// ------------------------------------\n\n\n// Mixins\n// ------------------------------------\n\n@mixin z-depth-transition() {\n transition: box-shadow .28s cubic-bezier(.4, 0, .2, 1);\n}\n\n@mixin z-depth-focus() {\n box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);\n}\n\n@mixin z-depth-2dp() {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14),\n 0 1px 5px 0 rgba(0, 0, 0, .12),\n 0 3px 1px -2px rgba(0, 0, 0, .2);\n}\n\n@mixin z-depth-3dp() {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, .14),\n 0 1px 8px 0 rgba(0, 0, 0, .12),\n 0 3px 3px -2px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-4dp() {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14),\n 0 1px 10px 0 rgba(0, 0, 0, .12),\n 0 2px 4px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-6dp() {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, .14),\n 0 1px 18px 0 rgba(0, 0, 0, .12),\n 0 3px 5px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-8dp() {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14),\n 0 3px 14px 2px rgba(0, 0, 0, .12),\n 0 5px 5px -3px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-16dp() {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14),\n 0 6px 30px 5px rgba(0, 0, 0, .12),\n 0 8px 10px -5px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-24dp() {\n box-shadow: 0 9px 46px 8px rgba(0, 0, 0, .14),\n 0 24px 38px 3px rgba(0, 0, 0, .12),\n 0 11px 15px -7px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth($dp: 2) {\n @if $dp == 2 {\n @include z-depth-2dp();\n } @else if $dp == 3 {\n @include z-depth-3dp();\n } @else if $dp == 4 {\n @include z-depth-4dp();\n } @else if $dp == 6 {\n @include z-depth-6dp();\n } @else if $dp == 8 {\n @include z-depth-8dp();\n } @else if $dp == 16 {\n @include z-depth-16dp();\n } @else if $dp == 24 {\n @include z-depth-24dp();\n }\n}\n\n\n// Class generator\n// ------------------------------------\n\n@mixin z-depth-classes($transition: false, $focus: false) {\n @if $transition == true {\n &-transition {\n @include z-depth-transition();\n }\n }\n\n @if $focus == true {\n &-focus {\n @include z-depth-focus();\n }\n }\n\n // The available values for the shadow depth\n @each $depth in 2, 3, 4, 6, 8, 16, 24 {\n &-#{$depth}dp {\n @include z-depth($depth);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: base grid and containers\n// ----------------------------------------------------------------------------\n\n// Stretch container to viewport and set base `font-size`\nhtml {\n height: 100%;\n overflow-x: hidden;\n // Hack: normally, we would set the base `font-size` to `62.5%`, so we can\n // base all calculations on `10px`, but Chromium and Chrome define a minimal\n // `font-size` of `12px` if the system language is set to Chinese. For this\n // reason we just double the `font-size` and set it to `20px`.\n //\n // See https://github.com/squidfunk/mkdocs-material/issues/911\n font-size: 125%;\n\n // [screen medium +]: Set base `font-size` to `11px`\n @include break-from-device(screen medium) {\n font-size: 137.5%;\n }\n\n // [screen large +]: Set base `font-size` to `12px`\n @include break-from-device(screen large) {\n font-size: 150%;\n }\n}\n\n// Stretch body to container - flexbox is used, so the footer will always be\n// aligned to the bottom of the viewport\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n // Hack: reset `font-size` to `10px`, so the spacing for all inline elements\n // is correct again. Otherwise the spacing would be based on `20px`.\n font-size: px2rem(10px);\n background-color: var(--md-default-bg-color);\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n\n // Body in locked state\n &[data-md-state=\"lock\"] {\n\n // [tablet portrait -]: Omit scroll bubbling\n @include break-to-device(tablet portrait) {\n position: fixed;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Grid container - this class is applied to wrapper elements within the\n// header, content area and footer, and makes sure that their width is limited\n// to `1220px`, and they are rendered centered if the screen is larger.\n.md-grid {\n max-width: px2rem(1220px);\n margin-right: auto;\n margin-left: auto;\n}\n\n// Main container\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n}\n\n// Main area - stretch to remaining space of container\n.md-main {\n flex-grow: 1;\n\n // Main area wrapper\n &__inner {\n display: flex;\n height: 100%;\n margin-top: px2rem(24px + 6px);\n }\n}\n\n// Add ellipsis in case of overflowing text\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: navigational elements\n// ----------------------------------------------------------------------------\n\n// Toggle - this class is applied to checkbox elements, which are used to\n// implement the CSS-only drawer and navigation, as well as the search\n.md-toggle {\n display: none;\n}\n\n// Option - this class is applied to radio elements, which are used to\n// implement the color palette toggle\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Option label for checked radio button\n &:checked + label:not([hidden]) {\n display: block;\n }\n\n // Show outline for pointer devices\n &.focus-visible + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n }\n}\n\n// Skip link\n.md-skip {\n position: fixed;\n // Hack: if we don't set the negative `z-index`, the skip link will force the\n // creation of new layers when code blocks are near the header on scrolling\n z-index: -1;\n margin: px2rem(10px);\n padding: px2rem(6px) px2rem(10px);\n color: var(--md-default-bg-color);\n font-size: px2rem(12.8px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n outline-color: var(--md-accent-fg-color);\n transform: translateY(px2rem(8px));\n opacity: 0;\n\n // Show skip link on focus\n &:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 175ms 75ms;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: print styles\n// ----------------------------------------------------------------------------\n\n// Add margins to page\n@page {\n margin: 25mm;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Announcement bar\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide announcement bar\n @media print {\n display: none;\n }\n\n // Announcement wrapper\n &__inner {\n margin: px2rem(12px) auto;\n padding: 0 px2rem(16px);\n color: var(--md-footer-fg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Button to copy to clipboard\n.md-clipboard {\n position: absolute;\n top: px2em(8px);\n right: px2em(8px);\n z-index: 1;\n width: px2em(24px);\n height: px2em(24px);\n color: var(--md-default-fg-color--lightest);\n border-radius: px2rem(2px);\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(2px);\n cursor: pointer;\n transition: color 250ms;\n\n // [print]: Hide button\n @media print {\n display: none;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Darken color on code block hover\n :hover > & {\n color: var(--md-default-fg-color--light);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Button icon - the width and height are defined in `em`, so the size is\n // automatically adjusted for nested code blocks (e.g. in admonitions)\n &::after {\n display: block;\n width: px2em(18px);\n height: px2em(18px);\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Inline button\n &--inline {\n cursor: pointer;\n\n // Code block\n code {\n transition:\n color 250ms,\n background-color 250ms;\n }\n\n // Code block on focus/hover\n &:focus code,\n &:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Content area\n.md-content {\n flex-grow: 1;\n // Hack: we must use `overflow: hidden`, so the content area is capped by\n // the dimensions of its parent. Otherwise, long code blocks might lead to\n // a wider content area which will break everything. This, however, induces\n // margin collapse, which will break scroll margins. Adding a large enough\n // scroll padding seems to do the trick, at least in Chrome and Firefox.\n overflow: hidden;\n scroll-padding-top: px2rem(1024px);\n\n // Content wrapper\n &__inner {\n margin: 0 px2rem(16px) px2rem(24px);\n padding-top: px2rem(12px);\n\n // [screen +]: Adjust spacing between content area and sidebars\n @include break-from-device(screen) {\n\n // Sidebar with navigation is visible\n .md-sidebar--primary:not([hidden]) ~ .md-content > & {\n margin-left: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(24px);\n margin-left: px2rem(16px);\n }\n }\n\n // Sidebar with table of contents is visible\n .md-sidebar--secondary:not([hidden]) ~ .md-content > & {\n margin-right: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(16px);\n margin-left: px2rem(24px);\n }\n }\n }\n\n // Hack: add pseudo element for spacing, as the overflow of the content\n // container may not be hidden due to an imminent offset error on targets\n &::before {\n display: block;\n height: px2rem(8px);\n content: \"\";\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0;\n }\n }\n\n // Button inside of the content area - these buttons are meant for actions on\n // a document-level, i.e. linking to related source code files, printing etc.\n &__button {\n float: right;\n margin: px2rem(8px) 0;\n margin-left: px2rem(8px);\n padding: 0;\n\n // [print]: Hide buttons\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(8px);\n margin-left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // Adjust default link color for icons\n .md-typeset & {\n color: var(--md-default-fg-color--lighter);\n }\n\n // Align with body copy located next to icon\n svg {\n display: inline;\n vertical-align: top;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Dialog\n.md-dialog {\n @include z-depth(2);\n\n position: fixed;\n right: px2rem(16px);\n bottom: px2rem(16px);\n left: initial;\n z-index: 2;\n min-width: px2rem(222px);\n padding: px2rem(8px) px2rem(12px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n transform: translateY(100%);\n opacity: 0;\n transition:\n transform 0ms 400ms,\n opacity 400ms;\n pointer-events: none;\n\n // [print]: Hide dialog\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n\n // Dialog in open state\n &[data-md-state=\"open\"] {\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1),\n opacity 400ms;\n pointer-events: initial;\n }\n\n // Dialog wrapper\n &__inner {\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Form button\n .md-button {\n display: inline-block;\n padding: px2em(10px) px2em(32px);\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: px2rem(2px) solid currentColor;\n border-radius: px2rem(2px);\n transition:\n color 125ms,\n background-color 125ms,\n border-color 125ms;\n\n // Primary button\n &--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n }\n }\n\n // Form input\n .md-input {\n height: px2rem(36px);\n padding: 0 px2rem(12px);\n font-size: px2rem(16px);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);\n transition: box-shadow 250ms;\n\n // Input on focus/hover\n &:focus,\n &:hover {\n box-shadow:\n 0 px2rem(8px) px2rem(20px) hsla(0, 0%, 0%, 0.15),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.15);\n }\n\n // Stretch to full width\n &--stretch {\n width: 100%;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Header - by default, the header will be sticky and stay always on top of the\n// viewport. If this behavior is not desired, just set `position: static`.\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 2;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n // Hack: reduce jitter by adding a transparent box shadow of the same size\n // so the size of the layer doesn't change during animation\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0);\n\n // [print]: Hide header\n @media print {\n display: none;\n }\n\n // Header in shadow state, i.e. shadow is visible\n &[data-md-state=\"shadow\"] {\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0.1),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0.2);\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n box-shadow 250ms;\n }\n\n // Header in hidden state, i.e. moved out of sight\n &[data-md-state=\"hidden\"] {\n transform: translateY(-100%);\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n box-shadow 250ms;\n }\n\n // Header wrapper\n &__inner {\n display: flex;\n align-items: center;\n padding: 0 px2rem(4px);\n }\n\n // Header button\n &__button {\n position: relative;\n z-index: 1;\n margin: px2rem(4px);\n padding: px2rem(8px);\n color: currentColor;\n vertical-align: middle;\n outline-color: var(--md-accent-fg-color);\n cursor: pointer;\n transition: opacity 250ms;\n\n // Button on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Header button is visible\n &:not([hidden]) {\n display: inline-block;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // [tablet -]: Hide button\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n }\n\n // Button for search\n &[for=\"__search\"] {\n\n // [tablet landscape +]: Hide button\n @include break-from-device(tablet landscape) {\n display: none;\n }\n\n // [no-js]: Hide button\n .no-js & {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Button for drawer\n &[for=\"__drawer\"] {\n\n // [screen +]: Hide button\n @include break-from-device(screen) {\n display: none;\n }\n }\n }\n\n // Header topic\n &__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n\n // Second header topic - title of the current page\n & + & {\n z-index: -1;\n transform: translateX(px2rem(25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-25px));\n }\n }\n }\n\n // Header title\n &__title {\n flex-grow: 1;\n height: px2rem(48px);\n margin-right: px2rem(8px);\n margin-left: px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n\n // Header title in active state, i.e. page title is visible\n &[data-md-state=\"active\"] .md-header__topic {\n z-index: -1;\n transform: translateX(px2rem(-25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(25px));\n }\n\n // Second header topic - title of the current page\n + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: initial;\n }\n }\n\n // Add ellipsis in case of overflowing text\n > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n }\n }\n\n // Header option\n &__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition:\n max-width 0ms 250ms,\n opacity 250ms 250ms;\n\n // Hide toggle when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-width: 0;\n opacity: 0;\n transition:\n max-width 0ms,\n opacity 0ms;\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n\n // [tablet landscape +]: Show repository information\n @include break-from-device(tablet landscape) {\n display: block;\n width: px2rem(234px);\n max-width: px2rem(234px);\n margin-left: px2rem(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(20px);\n margin-left: initial;\n }\n }\n\n // [screen +]: Adjust spacing of search bar\n @include break-from-device(screen) {\n margin-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Footer\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide footer\n @media print {\n display: none;\n }\n\n // Footer wrapper\n &__inner {\n padding: px2rem(4px);\n overflow: auto;\n }\n\n // Footer link to previous and next page\n &__link {\n display: flex;\n padding-top: px2rem(28px);\n padding-bottom: px2rem(8px);\n outline-color: var(--md-accent-fg-color);\n transition: opacity 250ms;\n\n // [tablet +]: Adjust width to 50/50\n @include break-from-device(tablet) {\n width: 50%;\n }\n\n // Footer link on focus/hover\n &:focus,\n &:hover {\n opacity: 0.7;\n }\n\n // Footer link to previous page\n &--prev {\n float: left;\n\n // [mobile -]: Adjust width to 25/75 and hide title\n @include break-to-device(mobile) {\n width: 25%;\n\n // Hide footer title\n .md-footer__title {\n display: none;\n }\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Footer link to next page\n &--next {\n float: right;\n text-align: right;\n\n // [mobile -]: Adjust width to 25/75\n @include break-to-device(mobile) {\n width: 75%;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n text-align: left;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n\n // Footer title\n &__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - #{px2rem(48px)});\n padding: 0 px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n }\n\n // Footer link button\n &__button {\n margin: px2rem(4px);\n padding: px2rem(8px);\n }\n\n // Footer link direction (i.e. prev and next)\n &__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: px2rem(-20px);\n padding: 0 px2rem(20px);\n font-size: px2rem(12.8px);\n opacity: 0.7;\n }\n}\n\n// Footer metadata\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n\n // Footer metadata wrapper\n &__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: px2rem(4px);\n }\n\n // Lighten color for non-hovered text links\n html &.md-typeset a {\n color: var(--md-footer-fg-color--light);\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-footer-fg-color);\n }\n }\n}\n\n// Footer copyright and theme information\n.md-footer-copyright {\n width: 100%;\n margin: auto px2rem(12px);\n padding: px2rem(8px) 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: px2rem(12.8px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n width: auto;\n }\n\n // Footer copyright highlight - this is the upper part of the copyright and\n // theme information, which will include a darker color than the theme link\n &__highlight {\n color: var(--md-footer-fg-color--light);\n }\n}\n\n// Footer social links\n.md-footer-social {\n margin: 0 px2rem(8px);\n padding: px2rem(4px) 0 px2rem(12px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n padding: px2rem(12px) 0;\n }\n\n // Footer social link\n &__link {\n display: inline-block;\n width: px2rem(32px);\n height: px2rem(32px);\n text-align: center;\n\n // Adjust line-height to match height for correct alignment\n &::before {\n line-height: 1.9;\n }\n\n // Fill icon with current color\n svg {\n max-height: px2rem(16px);\n vertical-align: -25%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Navigation\n.md-nav {\n font-size: px2rem(14px);\n line-height: 1.3;\n\n // Navigation title\n &__title {\n display: block;\n padding: 0 px2rem(12px);\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n\n // Navigaton button\n .md-nav__button {\n display: none;\n\n // Stretch images based on height, as it's the smaller dimension\n img {\n width: auto;\n height: 100%;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(48px);\n height: px2rem(48px);\n fill: currentColor;\n }\n }\n }\n }\n\n // Navigation list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Navigation item\n &__item {\n padding: 0 px2rem(12px);\n\n // Navigation item on level 2\n & & {\n padding-right: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: 0;\n }\n }\n }\n\n // Navigation link\n &__link {\n display: block;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n\n // Link in blurred state\n &[data-md-state=\"blur\"] {\n color: var(--md-default-fg-color--light);\n }\n\n // Active link\n .md-nav__item &--active {\n color: var(--md-typeset-a-color);\n }\n\n // Navigation link in nested list\n .md-nav__item--nested > & {\n color: inherit;\n }\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Navigation link on keyboard focus\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n\n // Navigation link to table of contents\n .md-nav--primary &[for=\"__toc\"] {\n display: none;\n\n // Table of contents icon\n .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n }\n\n // Hide table of contents\n ~ .md-nav {\n display: none;\n }\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n }\n\n // [tablet -]: Layered navigation\n @include break-to-device(tablet) {\n\n // Primary and nested navigation\n &--primary,\n &--primary & {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n\n // Primary navigation\n &--primary {\n\n // Navigation title and item\n .md-nav__title,\n .md-nav__item {\n font-size: px2rem(16px);\n line-height: 1.5;\n }\n\n // Navigation title\n .md-nav__title {\n position: relative;\n height: px2rem(112px);\n padding: px2rem(60px) px2rem(16px) px2rem(4px);\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: px2rem(48px);\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: px2rem(8px);\n left: px2rem(8px);\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(4px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(8px);\n left: initial;\n }\n\n // Navigation icon in link to previous level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n\n // Navigation list\n ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow:\n 0 px2rem(1px) 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Omit border on first child\n > :first-child {\n border-top: 0;\n }\n }\n\n // Top-level navigation title\n &[for=\"__drawer\"] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n\n // Button with logo, pointing to `config.site_url`\n .md-logo {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(4px);\n display: block;\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(4px);\n left: initial;\n }\n }\n }\n\n // Navigation list\n .md-nav__list {\n flex: 1;\n }\n\n // Navigation item\n .md-nav__item {\n padding: 0;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n\n // Navigation link in nested navigation\n &--nested > .md-nav__link {\n padding-right: px2rem(48px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n }\n\n // Navigation link in active navigation\n &--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n\n // Navigation link\n .md-nav__link {\n position: relative;\n margin-top: 0;\n padding: px2rem(12px) px2rem(16px);\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: 50%;\n right: px2rem(12px);\n width: px2rem(24px);\n height: px2rem(24px);\n margin-top: px2rem(-12px);\n color: inherit;\n font-size: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(12px);\n }\n\n // Navigation icon in link to next level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n }\n\n // Flip icon vertically\n .md-nav__icon {\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] &::after {\n transform: scale(-1);\n }\n }\n\n // Table of contents contained in primary navigation\n .md-nav--secondary {\n\n // Navigation link - omit unnecessary layering\n .md-nav__link {\n position: static;\n }\n\n // Navigation on level 2-6\n .md-nav {\n position: static;\n background-color: transparent;\n\n // Navigation link on level 3\n .md-nav__link {\n padding-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(28px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 4\n .md-nav .md-nav__link {\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 5\n .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(52px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(52px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 6\n .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(64px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(64px);\n padding-left: initial;\n }\n }\n }\n }\n }\n\n // Table of contents\n &--secondary {\n background-color: transparent;\n }\n\n // Toggle for nested navigation\n &__toggle ~ & {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n opacity 125ms 50ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(-100%);\n }\n }\n\n // Show nested navigation when toggle is active\n &__toggle:checked ~ & {\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 125ms 125ms;\n\n // Navigation list\n > .md-nav__list {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n }\n }\n }\n\n // [tablet portrait -]: Layered navigation with table of contents\n @include break-to-device(tablet portrait) {\n\n // Show link to table of contents\n &--primary &__link[for=\"__toc\"] {\n display: block;\n padding-right: px2rem(48px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n\n // Repository information container\n &__source {\n display: block;\n padding: 0 px2rem(4px);\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n }\n\n // [tablet landscape]: Layered navigation with table of contents\n @include break-at-device(tablet landscape) {\n\n // Show link to integrated table of contents\n &--integrated &__link[for=\"__toc\"] {\n display: block;\n padding-right: px2rem(48px);\n scroll-snap-align: initial;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n }\n\n // [tablet landscape +]: Tree-like table of contents\n @include break-from-device(tablet landscape) {\n\n // Navigation title\n &--secondary &__title {\n\n // Adjust snapping behavior\n &[for=\"__toc\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n }\n\n // [screen +]: Tree-like navigation\n @include break-from-device(screen) {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n\n // Navigation title\n &--primary &__title {\n\n // Adjust snapping behavior\n &[for=\"__drawer\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n\n // Hide toggle for nested navigation\n &__toggle ~ & {\n display: none;\n }\n\n // Show nested navigation when toggle is active or indeterminate\n &__toggle:checked ~ &,\n &__toggle:indeterminate ~ & {\n display: block;\n }\n\n // Hide navigation title in nested navigation\n &__item--nested > & > &__title {\n display: none;\n }\n\n // Navigation section\n &__item--section {\n display: block;\n margin: 1.25em 0;\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Hide navigation link, as sections are always expanded\n > .md-nav__link {\n display: none;\n }\n\n // Navigation\n > .md-nav {\n display: block;\n\n // Navigation title\n > .md-nav__title {\n display: block;\n padding: 0;\n pointer-events: none;\n scroll-snap-align: start;\n }\n\n // Adjust spacing on next level item\n > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n }\n }\n\n // Navigation icon\n &__icon {\n float: right;\n width: px2rem(18px);\n height: px2rem(18px);\n transition: transform 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n transform: rotate(180deg);\n }\n\n // Navigation icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: px2rem(-2px);\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Navigation icon - rotate icon when toggle is active or indeterminate\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link &,\n .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link & {\n transform: rotate(90deg);\n }\n }\n\n // Modifier for when navigation tabs are rendered\n &--lifted {\n\n // Hide nested level 0 items and site title\n > .md-nav__list > .md-nav__item--nested,\n > .md-nav__title {\n display: none;\n }\n\n // Hide level 0 items\n > .md-nav__list > .md-nav__item {\n display: none;\n\n // Active parent navigation item\n &--active {\n display: block;\n padding: 0;\n\n // Hide nested links\n > .md-nav__link {\n display: none;\n }\n\n // Show title and adjust spacing\n > .md-nav > .md-nav__title {\n display: block;\n padding: 0 px2rem(12px);\n pointer-events: none;\n scroll-snap-align: start;\n }\n }\n }\n\n // Hack: Always show active navigation tab on breakpoint screen, despite\n // of checkbox being checked or not. Fixes #1655.\n .md-nav[data-md-level=\"1\"] {\n display: block;\n\n // Adjust spacing for level 1 items\n > .md-nav__list > .md-nav__item {\n padding-right: px2rem(12px);\n }\n }\n }\n\n // Modifier for when table of contents is rendered in primary navigation\n &--integrated &__link[for=\"__toc\"] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: px2rem(1px) solid var(--md-primary-fg-color);\n\n // Hide navigation title\n > .md-nav__title {\n display: none;\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Search\n.md-search {\n position: relative;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding: px2rem(4px) 0;\n }\n\n // [no-js]: Hide search\n .no-js & {\n display: none;\n }\n\n // Search overlay\n &__overlay {\n z-index: 1;\n opacity: 0;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(-44px);\n width: px2rem(40px);\n height: px2rem(40px);\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(20px);\n transform-origin: center;\n transition:\n transform 300ms 100ms,\n opacity 200ms 200ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-44px);\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n opacity: 1;\n transition:\n transform 400ms,\n opacity 100ms;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n cursor: pointer;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n width: 100%;\n // Hack: when the header is translated upon scrolling, a new layer is\n // induced, which means that the height will now refer to the height of\n // the header, albeit positioning is fixed. This should be mitigated\n // in all cases when setting the height to 2x the viewport.\n height: 200vh;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [mobile portrait -]: Scale up 45 times\n @include break-to-device(mobile portrait) {\n transform: scale(45);\n }\n\n // [mobile landscape]: Scale up 60 times\n @include break-at-device(mobile landscape) {\n transform: scale(60);\n }\n\n // [tablet portrait]: Scale up 75 times\n @include break-at-device(tablet portrait) {\n transform: scale(75);\n }\n }\n }\n\n // Search wrapper\n &__inner {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: fixed;\n top: 0;\n left: 100%;\n z-index: 2;\n width: 100%;\n height: 100%;\n transform: translateX(5%);\n opacity: 0;\n transition:\n right 0ms 300ms,\n left 0ms 300ms,\n transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 150ms 150ms;\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n left: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n right 0ms 0ms,\n left 0ms 0ms,\n transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms 150ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n }\n\n // Adjust for right-to-left languages\n html [dir=\"rtl\"] & {\n right: 100%;\n left: initial;\n transform: translateX(-5%);\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: relative;\n float: right;\n width: px2rem(234px);\n padding: px2rem(2px) 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [tablet landscape]: Omit overlaying header title\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Match width of content area\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n }\n }\n\n // Search form\n &__form {\n position: relative;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n border-radius: px2rem(2px);\n }\n }\n\n // Search input\n &__input {\n position: relative;\n z-index: 2;\n padding: 0 px2rem(44px) 0 px2rem(72px);\n text-overflow: ellipsis;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 px2rem(12px) transparent;\n transition:\n color 250ms,\n background-color 250ms,\n box-shadow 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0 px2rem(72px) 0 px2rem(44px);\n }\n\n // Search placeholder\n &::placeholder {\n transition: color 250ms;\n }\n\n // Search icon and placeholder\n ~ .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n\n // Remove the \"x\" rendered by Internet Explorer\n &::-ms-clear {\n display: none;\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n box-shadow: 0 0 px2rem(12px) hsla(0, 0%, 0%, 0.07);\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n width: 100%;\n height: px2rem(48px);\n font-size: px2rem(18px);\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n width: 100%;\n height: px2rem(36px);\n padding-left: px2rem(44px);\n color: inherit;\n font-size: px2rem(16px);\n background-color: hsla(0, 0%, 0%, 0.26);\n border-radius: px2rem(2px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n }\n\n // Search icon\n + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n\n // Search placeholder\n &::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n\n // Search input on hover\n &:hover {\n background-color: hsla(0, 0%, 100%, 0.12);\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n color: var(--md-default-fg-color);\n text-overflow: clip;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px) px2rem(2px) 0 0;\n\n // Search icon and placeholder\n + .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n }\n }\n }\n\n // Search icon\n &__icon {\n position: absolute;\n z-index: 2;\n width: px2rem(24px);\n height: px2rem(24px);\n cursor: pointer;\n transition:\n color 250ms,\n opacity 250ms;\n\n // Search icon on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Search focus button\n &[for=\"__search\"] {\n top: px2rem(6px);\n left: px2rem(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(10px);\n left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(16px);\n left: initial;\n }\n\n // Hide the magnifying glass\n svg:first-child {\n display: none;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n pointer-events: none;\n\n // Hide the back arrow\n svg:last-child {\n display: none;\n }\n }\n }\n\n // Search reset button\n &[type=\"reset\"] {\n top: px2rem(6px);\n right: px2rem(10px);\n transform: scale(0.75);\n opacity: 0;\n transition:\n transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(10px);\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n right: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n }\n\n // Show reset button when search is active and input non-empty\n [data-md-toggle=\"search\"]:checked ~ .md-header\n .md-search__input:valid ~ & {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n\n // Search focus icon\n &:hover {\n opacity: 0.7;\n }\n }\n }\n }\n\n // Search output\n &__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 px2rem(2px) px2rem(2px);\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(48px);\n bottom: 0;\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n top: px2rem(38px);\n opacity: 0;\n transition: opacity 400ms;\n\n // Show output when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n @include z-depth(6);\n\n opacity: 1;\n }\n }\n }\n\n // Search scroll wrapper\n &__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 88+ has weird overscroll behavior. Overall, scroll snapping\n // seems to be something that is not ready for prime time on some browsers.\n // scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Mitigiate excessive repaints on non-retina devices\n @media (max-resolution: 1dppx) {\n transform: translateZ(0);\n }\n\n // [tablet landscape]: Set fixed width to omit unnecessary reflow\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Set fixed width to omit unnecessary reflow\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n\n // [tablet landscape +]: Limit height to viewport\n @include break-from-device(tablet landscape) {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Show scroll wrapper when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-height: 75vh;\n }\n\n // Search scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n}\n\n// Search result\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n\n // Search result metadata\n &__meta {\n padding: 0 px2rem(16px);\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: px2rem(36px);\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: initial;\n }\n }\n }\n\n // Search result list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Search result item\n &__item {\n box-shadow: 0 px2rem(-1px) 0 var(--md-default-fg-color--lightest);\n\n // Omit border on first child\n &:first-child {\n box-shadow: none;\n }\n }\n\n // Search result link\n &__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n\n // Search result link on focus/hover\n &:focus,\n &:hover {\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Adjust spacing on last child of last link\n &:last-child p:last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Search result more link\n &__more summary {\n display: block;\n padding: px2em(12px) px2rem(16px);\n color: var(--md-typeset-a-color);\n font-size: px2rem(12.8px);\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result more link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n\n // Adjust transparency of less relevant results\n ~ * > * {\n opacity: 0.65;\n }\n }\n\n // Search result article\n &__article {\n position: relative;\n padding: 0 px2rem(16px);\n overflow: hidden;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result article document\n &--document {\n\n // Search result title\n .md-search-result__title {\n margin: px2rem(11px) 0;\n font-weight: 400;\n font-size: px2rem(16px);\n line-height: 1.4;\n }\n }\n }\n\n // Search result icon\n &__icon {\n position: absolute;\n left: 0;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(10px);\n color: var(--md-default-fg-color--light);\n\n // [tablet portrait -]: Hide icon\n @include break-to-device(tablet portrait) {\n display: none;\n }\n\n // Search result icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n\n // Flip icon vertically\n &::after {\n transform: scaleX(-1);\n }\n }\n }\n\n // Search result title\n &__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: px2rem(12.8px);\n line-height: 1.6;\n }\n\n // Search result teaser\n &__teaser {\n display: -webkit-box;\n max-height: px2rem(40px);\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n\n // [mobile -]: Adjust number of lines\n @include break-to-device(mobile) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // [tablet landscape]: Adjust number of lines\n @include break-at-device(tablet landscape) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // Search term highlighting\n mark {\n text-decoration: underline;\n background-color: transparent;\n }\n }\n\n // Search result terms\n &__terms {\n margin: 0.5em 0;\n font-size: px2rem(12.8px);\n font-style: italic;\n }\n\n // Search term highlighting\n mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Selection\n.md-select {\n position: relative;\n z-index: 1;\n\n // Selection bubble\n &__inner {\n position: absolute;\n top: calc(100% - #{px2rem(4px)});\n left: 50%;\n max-height: 0;\n margin-top: px2rem(4px);\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n transform: translate3d(-50%, px2rem(6px), 0);\n opacity: 0;\n transition:\n transform 250ms 375ms,\n opacity 250ms 250ms,\n max-height 0ms 500ms;\n\n // Selection bubble on parent focus/hover\n .md-select:focus-within &,\n .md-select:hover & {\n max-height: px2rem(200px);\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms,\n max-height 0ms;\n }\n\n // Selection bubble handle\n &::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: px2rem(-4px);\n margin-left: px2rem(-4px);\n border: px2rem(4px) solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n }\n }\n\n // Selection list\n &__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: px2rem(16px);\n list-style-type: none;\n border-radius: px2rem(2px);\n }\n\n // Selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n outline: none;\n cursor: pointer;\n transition:\n background-color 250ms,\n color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Sidebar\n.md-sidebar {\n position: sticky;\n top: px2rem(48px);\n flex-shrink: 0;\n align-self: flex-start;\n width: px2rem(242px);\n padding: px2rem(24px) 0;\n\n // [print]: Hide sidebar\n @media print {\n display: none;\n }\n\n // [tablet -]: Show navigation as drawer\n @include break-to-device(tablet) {\n\n // Primary sidebar with navigation\n &--primary {\n position: fixed;\n top: 0;\n left: px2rem(-242px);\n z-index: 3;\n display: block;\n width: px2rem(242px);\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-242px);\n left: initial;\n }\n\n // Show sidebar when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ .md-container & {\n @include z-depth(8);\n\n transform: translateX(px2rem(242px));\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-242px));\n }\n }\n\n // Stretch scroll wrapper for primary sidebar\n .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n }\n }\n\n // [screen +]: Show navigation as sidebar\n @include break-from-device(screen) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n }\n\n // Secondary sidebar with table of contents\n &--secondary {\n display: none;\n order: 2;\n\n // [tablet landscape +]: Show table of contents as sidebar\n @include break-from-device(tablet landscape) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n\n // Sidebar is visible\n &:not([hidden]) {\n display: block;\n }\n\n // Ensure smooth scrolling on iOS\n .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n }\n }\n\n // Sidebar scroll wrapper\n &__scrollwrap {\n margin: 0 px2rem(4px);\n overflow-y: auto;\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 81+ exhibits a strange bug, where it scrolls the container\n // to the bottom if `scroll-snap-type` is set on the initial render. For\n // this reason, we disable scroll snapping until this is resolved (#1667).\n // scroll-snap-type: y mandatory;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Sidebar scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n}\n\n// [tablet -]: Show overlay on active drawer\n@include break-to-device(tablet) {\n\n // Sidebar overlay\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 3;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n opacity: 0;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Show overlay when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ & {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// Show repository facts\n@keyframes md-source__facts--done {\n 0% {\n height: 0;\n }\n\n 100% {\n height: px2rem(13px);\n }\n}\n\n// Show repository fact\n@keyframes md-source__fact--done {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n\n 50% {\n opacity: 0;\n }\n\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Repository information\n.md-source {\n display: block;\n font-size: px2rem(13px);\n line-height: 1.2;\n white-space: nowrap;\n outline-color: var(--md-accent-fg-color);\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n transition: opacity 250ms;\n\n // Repository information on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Repository icon\n &__icon {\n display: inline-block;\n width: px2rem(40px);\n height: px2rem(48px);\n vertical-align: middle;\n\n // Align with margin only (as opposed to normal button alignment)\n svg {\n margin-top: px2rem(12px);\n margin-left: px2rem(12px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(12px);\n margin-left: initial;\n }\n }\n\n // Adjust spacing if icon is present\n + .md-source__repository {\n margin-left: px2rem(-40px);\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(-40px);\n margin-left: initial;\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n }\n\n // Repository name\n &__repository {\n display: inline-block;\n max-width: calc(100% - #{px2rem(24px)});\n margin-left: px2rem(12px);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n }\n\n // Repository facts\n &__facts {\n margin: px2rem(2px) 0 0;\n padding: 0;\n overflow: hidden;\n font-size: px2rem(11px);\n list-style-type: none;\n opacity: 0.75;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: md-source__facts--done 250ms ease-in;\n }\n }\n\n // Repository fact\n &__fact {\n display: inline-block;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: md-source__fact--done 400ms ease-out;\n }\n\n // Repository fact icon\n &::before {\n display: inline-block;\n width: px2rem(12px);\n height: px2rem(12px);\n margin-right: px2rem(2px);\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Adjust spacing for repository fact icon\n &:nth-child(1n+2)::before {\n margin-left: px2rem(8px);\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: initial;\n margin-left: px2rem(2px);\n\n // Adjust spacing for repository fact icon\n &:nth-child(1n+2)::before {\n margin-right: px2rem(8px);\n margin-left: initial;\n }\n }\n\n // Repository fact: version\n &--version::before {\n mask-image: var(--md-source-version-icon);\n }\n\n // Repository fact: stars\n &--stars::before {\n mask-image: var(--md-source-stars-icon);\n }\n\n // Repository fact: forks\n &--forks::before {\n mask-image: var(--md-source-forks-icon);\n }\n\n // Repository fact: repositories\n &--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Navigation tabs\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n\n // [print]: Hide tabs\n @media print {\n display: none;\n }\n\n // [tablet -]: Hide tabs\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Tabs in hidden state, i.e. when scrolling down\n &[data-md-state=\"hidden\"] {\n pointer-events: none;\n }\n\n // Navigation tabs list\n &__list {\n margin: 0;\n margin-left: px2rem(4px);\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(4px);\n margin-left: initial;\n }\n }\n\n // Navigation tabs item\n &__item {\n display: inline-block;\n height: px2rem(48px);\n padding-right: px2rem(12px);\n padding-left: px2rem(12px);\n }\n\n // Navigation tabs link - could be defined as block elements and aligned via\n // line height, but this would imply more repaints when scrolling\n &__link {\n display: block;\n margin-top: px2rem(16px);\n font-size: px2rem(14px);\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n // Hack: save a repaint when tabs are appearing on scrolling up\n backface-visibility: hidden;\n opacity: 0.7;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms;\n\n // Active link and link on focus/hover\n &--active,\n &:focus,\n &:hover {\n color: inherit;\n opacity: 1;\n }\n\n // Delay transitions by a small amount\n @for $i from 2 through 16 {\n .md-tabs__item:nth-child(#{$i}) & {\n transition-delay: 20ms * ($i - 1);\n }\n }\n\n // Hide tabs upon scrolling - disable transition to minimizes repaints\n // while scrolling down, while scrolling up seems to be okay\n .md-tabs[data-md-state=\"hidden\"] & {\n transform: translateY(50%);\n opacity: 0;\n transition:\n transform 0ms 100ms,\n opacity 100ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Back-to-top button\n.md-top {\n position: sticky;\n bottom: px2rem(8px);\n z-index: 1;\n float: right;\n margin: px2rem(-56px) px2rem(8px) px2rem(8px);\n padding: px2rem(8px);\n color: var(--md-primary-bg-color);\n background: var(--md-primary-fg-color);\n border-radius: 100%;\n outline: none;\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);\n transform: translateY(0);\n transition:\n opacity 125ms,\n transform 125ms cubic-bezier(0.4, 0, 0.2, 1),\n background-color 125ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n\n // Back-to-top button in hidden state\n &[data-md-state=\"hidden\"] {\n transform: translateY(px2rem(-4px));\n opacity: 0;\n pointer-events: none;\n }\n\n // Back-to-top button on focus/hover\n &:focus,\n &:hover {\n background: var(--md-accent-fg-color);\n transform: scale(1.1);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Version selection\n.md-version {\n flex-shrink: 0;\n height: px2rem(48px);\n font-size: px2rem(16px);\n\n // Current selection\n &__current {\n position: relative;\n // Hack: in general, we would use `vertical-align` to align the version at\n // the bottom with the title, but since the list uses absolute positioning,\n // this won't work consistently. Furthermore, we would need to use inline\n // positioning to align the links, which looks jagged.\n top: px2rem(1px);\n margin-right: px2rem(8px);\n margin-left: px2rem(28px);\n color: inherit;\n outline: none;\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n margin-left: px2rem(8px);\n }\n\n // Version selection icon\n &::after {\n display: inline-block;\n width: px2rem(8px);\n height: px2rem(12px);\n margin-left: px2rem(8px);\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(8px);\n margin-left: initial;\n }\n }\n }\n\n // Version selection list\n &__list {\n position: absolute;\n top: px2rem(3px);\n z-index: 1;\n max-height: 0;\n margin: px2rem(4px) px2rem(16px);\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n opacity: 0;\n transition:\n max-height 0ms 500ms,\n opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n\n // Version selection list on parent focus/hover\n .md-version:focus-within &,\n .md-version:hover & {\n max-height: px2rem(200px);\n opacity: 1;\n transition:\n max-height 0ms,\n opacity 250ms;\n }\n }\n\n // Version selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Version selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n/// Admonition flavours\n$admonitions: (\n note: pencil $clr-blue-a200,\n abstract summary tldr: text-subject $clr-light-blue-a400,\n info todo: information $clr-cyan-a700,\n tip hint important: fire $clr-teal-a700,\n success check done: check-circle $clr-green-a700,\n question help faq: help-circle $clr-light-green-a700,\n warning caution attention: alert $clr-orange-a400,\n failure fail missing: close-circle $clr-red-a200,\n danger error: flash-circle $clr-red-a400,\n bug: bug $clr-pink-a400,\n example: format-list-numbered $clr-deep-purple-a200,\n quote cite: format-quote-close $clr-grey\n) !default;\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n @each $names, $props in $admonitions {\n --md-admonition-icon--#{nth($names, 1)}:\n svg-load(\"material/#{nth($props, 1)}.svg\");\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Admonition\n .admonition {\n margin: px2em(20px, 12.8px) 0;\n padding: 0 px2rem(12px);\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: px2rem(12.8px);\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: px2rem(4px) solid $clr-blue-a200;\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.05);\n\n // [print]: Omit shadow as it may lead to rendering errors\n @media print {\n box-shadow: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust vertical spacing for nested admonitions\n .admonition {\n margin-top: 1em;\n margin-bottom: 1em;\n }\n\n // Adjust spacing for contained table wrappers\n .md-typeset__scrollwrap {\n margin: 1em px2rem(-12px);\n }\n\n // Adjust spacing for contained tables\n .md-typeset__table {\n padding: 0 px2rem(12px);\n }\n\n // Adjust spacing for single-child tabbed block container\n > .tabbed-set:only-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n html & > :last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Admonition title\n .admonition-title {\n position: relative;\n margin: 0 px2rem(-12px) 0 px2rem(-16px);\n padding: px2rem(8px) px2rem(12px) px2rem(8px) px2rem(40px);\n font-weight: 700;\n background-color: transparentize($clr-blue-a200, 0.9);\n border-left: px2rem(4px) solid $clr-blue-a200;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin: 0 px2rem(-16px) 0 px2rem(-12px);\n padding: px2rem(8px) px2rem(40px) px2rem(8px) px2rem(12px);\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust spacing for title-only admonitions\n html &:last-child {\n margin-bottom: 0;\n }\n\n // Admonition icon\n &::before {\n position: absolute;\n left: px2rem(12px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: $clr-blue-a200;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(12px);\n left: initial;\n }\n }\n\n // Adjust spacing on last tabbed block container child - if the tabbed\n // block container is the sole child, it looks better to omit the margin\n + .tabbed-set:last-child {\n margin-top: 0;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: flavours\n// ----------------------------------------------------------------------------\n\n@each $names, $props in $admonitions {\n $name: nth($names, 1);\n $tint: nth($props, 2);\n\n // Admonition flavour\n .md-typeset .admonition.#{$name} {\n border-color: $tint;\n }\n\n // Admonition flavour title\n .md-typeset .#{$name} > .admonition-title {\n background-color: transparentize($tint, 0.9);\n border-color: $tint;\n\n // Admonition icon\n &::before {\n background-color: $tint;\n mask-image: var(--md-admonition-icon--#{$name});\n mask-repeat: no-repeat;\n mask-size: contain;\n }\n }\n\n // Define synonyms for flavours\n @if length($names) > 1 {\n @for $n from 2 through length($names) {\n .#{nth($names, $n)} {\n @extend .#{$name};\n }\n }\n }\n}\n","// ==========================================================================\n//\n// Name: UI Color Palette\n// Description: The color palette of material design.\n// Version: 2.3.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-color\n//\n// twitter: @mrmlnc\n//\n// ==========================================================================\n\n\n//\n// List of base colors\n//\n\n// $clr-red\n// $clr-pink\n// $clr-purple\n// $clr-deep-purple\n// $clr-indigo\n// $clr-blue\n// $clr-light-blue\n// $clr-cyan\n// $clr-teal\n// $clr-green\n// $clr-light-green\n// $clr-lime\n// $clr-yellow\n// $clr-amber\n// $clr-orange\n// $clr-deep-orange\n// $clr-brown\n// $clr-grey\n// $clr-blue-grey\n// $clr-black\n// $clr-white\n\n\n//\n// Red\n//\n\n$clr-red-list: (\n \"base\": #f44336,\n \"50\": #ffebee,\n \"100\": #ffcdd2,\n \"200\": #ef9a9a,\n \"300\": #e57373,\n \"400\": #ef5350,\n \"500\": #f44336,\n \"600\": #e53935,\n \"700\": #d32f2f,\n \"800\": #c62828,\n \"900\": #b71c1c,\n \"a100\": #ff8a80,\n \"a200\": #ff5252,\n \"a400\": #ff1744,\n \"a700\": #d50000\n);\n\n$clr-red: map-get($clr-red-list, \"base\");\n\n$clr-red-50: map-get($clr-red-list, \"50\");\n$clr-red-100: map-get($clr-red-list, \"100\");\n$clr-red-200: map-get($clr-red-list, \"200\");\n$clr-red-300: map-get($clr-red-list, \"300\");\n$clr-red-400: map-get($clr-red-list, \"400\");\n$clr-red-500: map-get($clr-red-list, \"500\");\n$clr-red-600: map-get($clr-red-list, \"600\");\n$clr-red-700: map-get($clr-red-list, \"700\");\n$clr-red-800: map-get($clr-red-list, \"800\");\n$clr-red-900: map-get($clr-red-list, \"900\");\n$clr-red-a100: map-get($clr-red-list, \"a100\");\n$clr-red-a200: map-get($clr-red-list, \"a200\");\n$clr-red-a400: map-get($clr-red-list, \"a400\");\n$clr-red-a700: map-get($clr-red-list, \"a700\");\n\n\n//\n// Pink\n//\n\n$clr-pink-list: (\n \"base\": #e91e63,\n \"50\": #fce4ec,\n \"100\": #f8bbd0,\n \"200\": #f48fb1,\n \"300\": #f06292,\n \"400\": #ec407a,\n \"500\": #e91e63,\n \"600\": #d81b60,\n \"700\": #c2185b,\n \"800\": #ad1457,\n \"900\": #880e4f,\n \"a100\": #ff80ab,\n \"a200\": #ff4081,\n \"a400\": #f50057,\n \"a700\": #c51162\n);\n\n$clr-pink: map-get($clr-pink-list, \"base\");\n\n$clr-pink-50: map-get($clr-pink-list, \"50\");\n$clr-pink-100: map-get($clr-pink-list, \"100\");\n$clr-pink-200: map-get($clr-pink-list, \"200\");\n$clr-pink-300: map-get($clr-pink-list, \"300\");\n$clr-pink-400: map-get($clr-pink-list, \"400\");\n$clr-pink-500: map-get($clr-pink-list, \"500\");\n$clr-pink-600: map-get($clr-pink-list, \"600\");\n$clr-pink-700: map-get($clr-pink-list, \"700\");\n$clr-pink-800: map-get($clr-pink-list, \"800\");\n$clr-pink-900: map-get($clr-pink-list, \"900\");\n$clr-pink-a100: map-get($clr-pink-list, \"a100\");\n$clr-pink-a200: map-get($clr-pink-list, \"a200\");\n$clr-pink-a400: map-get($clr-pink-list, \"a400\");\n$clr-pink-a700: map-get($clr-pink-list, \"a700\");\n\n\n//\n// Purple\n//\n\n$clr-purple-list: (\n \"base\": #9c27b0,\n \"50\": #f3e5f5,\n \"100\": #e1bee7,\n \"200\": #ce93d8,\n \"300\": #ba68c8,\n \"400\": #ab47bc,\n \"500\": #9c27b0,\n \"600\": #8e24aa,\n \"700\": #7b1fa2,\n \"800\": #6a1b9a,\n \"900\": #4a148c,\n \"a100\": #ea80fc,\n \"a200\": #e040fb,\n \"a400\": #d500f9,\n \"a700\": #aa00ff\n);\n\n$clr-purple: map-get($clr-purple-list, \"base\");\n\n$clr-purple-50: map-get($clr-purple-list, \"50\");\n$clr-purple-100: map-get($clr-purple-list, \"100\");\n$clr-purple-200: map-get($clr-purple-list, \"200\");\n$clr-purple-300: map-get($clr-purple-list, \"300\");\n$clr-purple-400: map-get($clr-purple-list, \"400\");\n$clr-purple-500: map-get($clr-purple-list, \"500\");\n$clr-purple-600: map-get($clr-purple-list, \"600\");\n$clr-purple-700: map-get($clr-purple-list, \"700\");\n$clr-purple-800: map-get($clr-purple-list, \"800\");\n$clr-purple-900: map-get($clr-purple-list, \"900\");\n$clr-purple-a100: map-get($clr-purple-list, \"a100\");\n$clr-purple-a200: map-get($clr-purple-list, \"a200\");\n$clr-purple-a400: map-get($clr-purple-list, \"a400\");\n$clr-purple-a700: map-get($clr-purple-list, \"a700\");\n\n\n//\n// Deep purple\n//\n\n$clr-deep-purple-list: (\n \"base\": #673ab7,\n \"50\": #ede7f6,\n \"100\": #d1c4e9,\n \"200\": #b39ddb,\n \"300\": #9575cd,\n \"400\": #7e57c2,\n \"500\": #673ab7,\n \"600\": #5e35b1,\n \"700\": #512da8,\n \"800\": #4527a0,\n \"900\": #311b92,\n \"a100\": #b388ff,\n \"a200\": #7c4dff,\n \"a400\": #651fff,\n \"a700\": #6200ea\n);\n\n$clr-deep-purple: map-get($clr-deep-purple-list, \"base\");\n\n$clr-deep-purple-50: map-get($clr-deep-purple-list, \"50\");\n$clr-deep-purple-100: map-get($clr-deep-purple-list, \"100\");\n$clr-deep-purple-200: map-get($clr-deep-purple-list, \"200\");\n$clr-deep-purple-300: map-get($clr-deep-purple-list, \"300\");\n$clr-deep-purple-400: map-get($clr-deep-purple-list, \"400\");\n$clr-deep-purple-500: map-get($clr-deep-purple-list, \"500\");\n$clr-deep-purple-600: map-get($clr-deep-purple-list, \"600\");\n$clr-deep-purple-700: map-get($clr-deep-purple-list, \"700\");\n$clr-deep-purple-800: map-get($clr-deep-purple-list, \"800\");\n$clr-deep-purple-900: map-get($clr-deep-purple-list, \"900\");\n$clr-deep-purple-a100: map-get($clr-deep-purple-list, \"a100\");\n$clr-deep-purple-a200: map-get($clr-deep-purple-list, \"a200\");\n$clr-deep-purple-a400: map-get($clr-deep-purple-list, \"a400\");\n$clr-deep-purple-a700: map-get($clr-deep-purple-list, \"a700\");\n\n\n//\n// Indigo\n//\n\n$clr-indigo-list: (\n \"base\": #3f51b5,\n \"50\": #e8eaf6,\n \"100\": #c5cae9,\n \"200\": #9fa8da,\n \"300\": #7986cb,\n \"400\": #5c6bc0,\n \"500\": #3f51b5,\n \"600\": #3949ab,\n \"700\": #303f9f,\n \"800\": #283593,\n \"900\": #1a237e,\n \"a100\": #8c9eff,\n \"a200\": #536dfe,\n \"a400\": #3d5afe,\n \"a700\": #304ffe\n);\n\n$clr-indigo: map-get($clr-indigo-list, \"base\");\n\n$clr-indigo-50: map-get($clr-indigo-list, \"50\");\n$clr-indigo-100: map-get($clr-indigo-list, \"100\");\n$clr-indigo-200: map-get($clr-indigo-list, \"200\");\n$clr-indigo-300: map-get($clr-indigo-list, \"300\");\n$clr-indigo-400: map-get($clr-indigo-list, \"400\");\n$clr-indigo-500: map-get($clr-indigo-list, \"500\");\n$clr-indigo-600: map-get($clr-indigo-list, \"600\");\n$clr-indigo-700: map-get($clr-indigo-list, \"700\");\n$clr-indigo-800: map-get($clr-indigo-list, \"800\");\n$clr-indigo-900: map-get($clr-indigo-list, \"900\");\n$clr-indigo-a100: map-get($clr-indigo-list, \"a100\");\n$clr-indigo-a200: map-get($clr-indigo-list, \"a200\");\n$clr-indigo-a400: map-get($clr-indigo-list, \"a400\");\n$clr-indigo-a700: map-get($clr-indigo-list, \"a700\");\n\n\n//\n// Blue\n//\n\n$clr-blue-list: (\n \"base\": #2196f3,\n \"50\": #e3f2fd,\n \"100\": #bbdefb,\n \"200\": #90caf9,\n \"300\": #64b5f6,\n \"400\": #42a5f5,\n \"500\": #2196f3,\n \"600\": #1e88e5,\n \"700\": #1976d2,\n \"800\": #1565c0,\n \"900\": #0d47a1,\n \"a100\": #82b1ff,\n \"a200\": #448aff,\n \"a400\": #2979ff,\n \"a700\": #2962ff\n);\n\n$clr-blue: map-get($clr-blue-list, \"base\");\n\n$clr-blue-50: map-get($clr-blue-list, \"50\");\n$clr-blue-100: map-get($clr-blue-list, \"100\");\n$clr-blue-200: map-get($clr-blue-list, \"200\");\n$clr-blue-300: map-get($clr-blue-list, \"300\");\n$clr-blue-400: map-get($clr-blue-list, \"400\");\n$clr-blue-500: map-get($clr-blue-list, \"500\");\n$clr-blue-600: map-get($clr-blue-list, \"600\");\n$clr-blue-700: map-get($clr-blue-list, \"700\");\n$clr-blue-800: map-get($clr-blue-list, \"800\");\n$clr-blue-900: map-get($clr-blue-list, \"900\");\n$clr-blue-a100: map-get($clr-blue-list, \"a100\");\n$clr-blue-a200: map-get($clr-blue-list, \"a200\");\n$clr-blue-a400: map-get($clr-blue-list, \"a400\");\n$clr-blue-a700: map-get($clr-blue-list, \"a700\");\n\n\n//\n// Light Blue\n//\n\n$clr-light-blue-list: (\n \"base\": #03a9f4,\n \"50\": #e1f5fe,\n \"100\": #b3e5fc,\n \"200\": #81d4fa,\n \"300\": #4fc3f7,\n \"400\": #29b6f6,\n \"500\": #03a9f4,\n \"600\": #039be5,\n \"700\": #0288d1,\n \"800\": #0277bd,\n \"900\": #01579b,\n \"a100\": #80d8ff,\n \"a200\": #40c4ff,\n \"a400\": #00b0ff,\n \"a700\": #0091ea\n);\n\n$clr-light-blue: map-get($clr-light-blue-list, \"base\");\n\n$clr-light-blue-50: map-get($clr-light-blue-list, \"50\");\n$clr-light-blue-100: map-get($clr-light-blue-list, \"100\");\n$clr-light-blue-200: map-get($clr-light-blue-list, \"200\");\n$clr-light-blue-300: map-get($clr-light-blue-list, \"300\");\n$clr-light-blue-400: map-get($clr-light-blue-list, \"400\");\n$clr-light-blue-500: map-get($clr-light-blue-list, \"500\");\n$clr-light-blue-600: map-get($clr-light-blue-list, \"600\");\n$clr-light-blue-700: map-get($clr-light-blue-list, \"700\");\n$clr-light-blue-800: map-get($clr-light-blue-list, \"800\");\n$clr-light-blue-900: map-get($clr-light-blue-list, \"900\");\n$clr-light-blue-a100: map-get($clr-light-blue-list, \"a100\");\n$clr-light-blue-a200: map-get($clr-light-blue-list, \"a200\");\n$clr-light-blue-a400: map-get($clr-light-blue-list, \"a400\");\n$clr-light-blue-a700: map-get($clr-light-blue-list, \"a700\");\n\n\n//\n// Cyan\n//\n\n$clr-cyan-list: (\n \"base\": #00bcd4,\n \"50\": #e0f7fa,\n \"100\": #b2ebf2,\n \"200\": #80deea,\n \"300\": #4dd0e1,\n \"400\": #26c6da,\n \"500\": #00bcd4,\n \"600\": #00acc1,\n \"700\": #0097a7,\n \"800\": #00838f,\n \"900\": #006064,\n \"a100\": #84ffff,\n \"a200\": #18ffff,\n \"a400\": #00e5ff,\n \"a700\": #00b8d4\n);\n\n$clr-cyan: map-get($clr-cyan-list, \"base\");\n\n$clr-cyan-50: map-get($clr-cyan-list, \"50\");\n$clr-cyan-100: map-get($clr-cyan-list, \"100\");\n$clr-cyan-200: map-get($clr-cyan-list, \"200\");\n$clr-cyan-300: map-get($clr-cyan-list, \"300\");\n$clr-cyan-400: map-get($clr-cyan-list, \"400\");\n$clr-cyan-500: map-get($clr-cyan-list, \"500\");\n$clr-cyan-600: map-get($clr-cyan-list, \"600\");\n$clr-cyan-700: map-get($clr-cyan-list, \"700\");\n$clr-cyan-800: map-get($clr-cyan-list, \"800\");\n$clr-cyan-900: map-get($clr-cyan-list, \"900\");\n$clr-cyan-a100: map-get($clr-cyan-list, \"a100\");\n$clr-cyan-a200: map-get($clr-cyan-list, \"a200\");\n$clr-cyan-a400: map-get($clr-cyan-list, \"a400\");\n$clr-cyan-a700: map-get($clr-cyan-list, \"a700\");\n\n\n//\n// Teal\n//\n\n$clr-teal-list: (\n \"base\": #009688,\n \"50\": #e0f2f1,\n \"100\": #b2dfdb,\n \"200\": #80cbc4,\n \"300\": #4db6ac,\n \"400\": #26a69a,\n \"500\": #009688,\n \"600\": #00897b,\n \"700\": #00796b,\n \"800\": #00695c,\n \"900\": #004d40,\n \"a100\": #a7ffeb,\n \"a200\": #64ffda,\n \"a400\": #1de9b6,\n \"a700\": #00bfa5\n);\n\n$clr-teal: map-get($clr-teal-list, \"base\");\n\n$clr-teal-50: map-get($clr-teal-list, \"50\");\n$clr-teal-100: map-get($clr-teal-list, \"100\");\n$clr-teal-200: map-get($clr-teal-list, \"200\");\n$clr-teal-300: map-get($clr-teal-list, \"300\");\n$clr-teal-400: map-get($clr-teal-list, \"400\");\n$clr-teal-500: map-get($clr-teal-list, \"500\");\n$clr-teal-600: map-get($clr-teal-list, \"600\");\n$clr-teal-700: map-get($clr-teal-list, \"700\");\n$clr-teal-800: map-get($clr-teal-list, \"800\");\n$clr-teal-900: map-get($clr-teal-list, \"900\");\n$clr-teal-a100: map-get($clr-teal-list, \"a100\");\n$clr-teal-a200: map-get($clr-teal-list, \"a200\");\n$clr-teal-a400: map-get($clr-teal-list, \"a400\");\n$clr-teal-a700: map-get($clr-teal-list, \"a700\");\n\n\n//\n// Green\n//\n\n$clr-green-list: (\n \"base\": #4caf50,\n \"50\": #e8f5e9,\n \"100\": #c8e6c9,\n \"200\": #a5d6a7,\n \"300\": #81c784,\n \"400\": #66bb6a,\n \"500\": #4caf50,\n \"600\": #43a047,\n \"700\": #388e3c,\n \"800\": #2e7d32,\n \"900\": #1b5e20,\n \"a100\": #b9f6ca,\n \"a200\": #69f0ae,\n \"a400\": #00e676,\n \"a700\": #00c853\n);\n\n$clr-green: map-get($clr-green-list, \"base\");\n\n$clr-green-50: map-get($clr-green-list, \"50\");\n$clr-green-100: map-get($clr-green-list, \"100\");\n$clr-green-200: map-get($clr-green-list, \"200\");\n$clr-green-300: map-get($clr-green-list, \"300\");\n$clr-green-400: map-get($clr-green-list, \"400\");\n$clr-green-500: map-get($clr-green-list, \"500\");\n$clr-green-600: map-get($clr-green-list, \"600\");\n$clr-green-700: map-get($clr-green-list, \"700\");\n$clr-green-800: map-get($clr-green-list, \"800\");\n$clr-green-900: map-get($clr-green-list, \"900\");\n$clr-green-a100: map-get($clr-green-list, \"a100\");\n$clr-green-a200: map-get($clr-green-list, \"a200\");\n$clr-green-a400: map-get($clr-green-list, \"a400\");\n$clr-green-a700: map-get($clr-green-list, \"a700\");\n\n\n//\n// Light green\n//\n\n$clr-light-green-list: (\n \"base\": #8bc34a,\n \"50\": #f1f8e9,\n \"100\": #dcedc8,\n \"200\": #c5e1a5,\n \"300\": #aed581,\n \"400\": #9ccc65,\n \"500\": #8bc34a,\n \"600\": #7cb342,\n \"700\": #689f38,\n \"800\": #558b2f,\n \"900\": #33691e,\n \"a100\": #ccff90,\n \"a200\": #b2ff59,\n \"a400\": #76ff03,\n \"a700\": #64dd17\n);\n\n$clr-light-green: map-get($clr-light-green-list, \"base\");\n\n$clr-light-green-50: map-get($clr-light-green-list, \"50\");\n$clr-light-green-100: map-get($clr-light-green-list, \"100\");\n$clr-light-green-200: map-get($clr-light-green-list, \"200\");\n$clr-light-green-300: map-get($clr-light-green-list, \"300\");\n$clr-light-green-400: map-get($clr-light-green-list, \"400\");\n$clr-light-green-500: map-get($clr-light-green-list, \"500\");\n$clr-light-green-600: map-get($clr-light-green-list, \"600\");\n$clr-light-green-700: map-get($clr-light-green-list, \"700\");\n$clr-light-green-800: map-get($clr-light-green-list, \"800\");\n$clr-light-green-900: map-get($clr-light-green-list, \"900\");\n$clr-light-green-a100: map-get($clr-light-green-list, \"a100\");\n$clr-light-green-a200: map-get($clr-light-green-list, \"a200\");\n$clr-light-green-a400: map-get($clr-light-green-list, \"a400\");\n$clr-light-green-a700: map-get($clr-light-green-list, \"a700\");\n\n\n//\n// Lime\n//\n\n$clr-lime-list: (\n \"base\": #cddc39,\n \"50\": #f9fbe7,\n \"100\": #f0f4c3,\n \"200\": #e6ee9c,\n \"300\": #dce775,\n \"400\": #d4e157,\n \"500\": #cddc39,\n \"600\": #c0ca33,\n \"700\": #afb42b,\n \"800\": #9e9d24,\n \"900\": #827717,\n \"a100\": #f4ff81,\n \"a200\": #eeff41,\n \"a400\": #c6ff00,\n \"a700\": #aeea00\n);\n\n$clr-lime: map-get($clr-lime-list, \"base\");\n\n$clr-lime-50: map-get($clr-lime-list, \"50\");\n$clr-lime-100: map-get($clr-lime-list, \"100\");\n$clr-lime-200: map-get($clr-lime-list, \"200\");\n$clr-lime-300: map-get($clr-lime-list, \"300\");\n$clr-lime-400: map-get($clr-lime-list, \"400\");\n$clr-lime-500: map-get($clr-lime-list, \"500\");\n$clr-lime-600: map-get($clr-lime-list, \"600\");\n$clr-lime-700: map-get($clr-lime-list, \"700\");\n$clr-lime-800: map-get($clr-lime-list, \"800\");\n$clr-lime-900: map-get($clr-lime-list, \"900\");\n$clr-lime-a100: map-get($clr-lime-list, \"a100\");\n$clr-lime-a200: map-get($clr-lime-list, \"a200\");\n$clr-lime-a400: map-get($clr-lime-list, \"a400\");\n$clr-lime-a700: map-get($clr-lime-list, \"a700\");\n\n\n//\n// Yellow\n//\n\n$clr-yellow-list: (\n \"base\": #ffeb3b,\n \"50\": #fffde7,\n \"100\": #fff9c4,\n \"200\": #fff59d,\n \"300\": #fff176,\n \"400\": #ffee58,\n \"500\": #ffeb3b,\n \"600\": #fdd835,\n \"700\": #fbc02d,\n \"800\": #f9a825,\n \"900\": #f57f17,\n \"a100\": #ffff8d,\n \"a200\": #ffff00,\n \"a400\": #ffea00,\n \"a700\": #ffd600\n);\n\n$clr-yellow: map-get($clr-yellow-list, \"base\");\n\n$clr-yellow-50: map-get($clr-yellow-list, \"50\");\n$clr-yellow-100: map-get($clr-yellow-list, \"100\");\n$clr-yellow-200: map-get($clr-yellow-list, \"200\");\n$clr-yellow-300: map-get($clr-yellow-list, \"300\");\n$clr-yellow-400: map-get($clr-yellow-list, \"400\");\n$clr-yellow-500: map-get($clr-yellow-list, \"500\");\n$clr-yellow-600: map-get($clr-yellow-list, \"600\");\n$clr-yellow-700: map-get($clr-yellow-list, \"700\");\n$clr-yellow-800: map-get($clr-yellow-list, \"800\");\n$clr-yellow-900: map-get($clr-yellow-list, \"900\");\n$clr-yellow-a100: map-get($clr-yellow-list, \"a100\");\n$clr-yellow-a200: map-get($clr-yellow-list, \"a200\");\n$clr-yellow-a400: map-get($clr-yellow-list, \"a400\");\n$clr-yellow-a700: map-get($clr-yellow-list, \"a700\");\n\n\n//\n// amber\n//\n\n$clr-amber-list: (\n \"base\": #ffc107,\n \"50\": #fff8e1,\n \"100\": #ffecb3,\n \"200\": #ffe082,\n \"300\": #ffd54f,\n \"400\": #ffca28,\n \"500\": #ffc107,\n \"600\": #ffb300,\n \"700\": #ffa000,\n \"800\": #ff8f00,\n \"900\": #ff6f00,\n \"a100\": #ffe57f,\n \"a200\": #ffd740,\n \"a400\": #ffc400,\n \"a700\": #ffab00\n);\n\n$clr-amber: map-get($clr-amber-list, \"base\");\n\n$clr-amber-50: map-get($clr-amber-list, \"50\");\n$clr-amber-100: map-get($clr-amber-list, \"100\");\n$clr-amber-200: map-get($clr-amber-list, \"200\");\n$clr-amber-300: map-get($clr-amber-list, \"300\");\n$clr-amber-400: map-get($clr-amber-list, \"400\");\n$clr-amber-500: map-get($clr-amber-list, \"500\");\n$clr-amber-600: map-get($clr-amber-list, \"600\");\n$clr-amber-700: map-get($clr-amber-list, \"700\");\n$clr-amber-800: map-get($clr-amber-list, \"800\");\n$clr-amber-900: map-get($clr-amber-list, \"900\");\n$clr-amber-a100: map-get($clr-amber-list, \"a100\");\n$clr-amber-a200: map-get($clr-amber-list, \"a200\");\n$clr-amber-a400: map-get($clr-amber-list, \"a400\");\n$clr-amber-a700: map-get($clr-amber-list, \"a700\");\n\n\n//\n// Orange\n//\n\n$clr-orange-list: (\n \"base\": #ff9800,\n \"50\": #fff3e0,\n \"100\": #ffe0b2,\n \"200\": #ffcc80,\n \"300\": #ffb74d,\n \"400\": #ffa726,\n \"500\": #ff9800,\n \"600\": #fb8c00,\n \"700\": #f57c00,\n \"800\": #ef6c00,\n \"900\": #e65100,\n \"a100\": #ffd180,\n \"a200\": #ffab40,\n \"a400\": #ff9100,\n \"a700\": #ff6d00\n);\n\n$clr-orange: map-get($clr-orange-list, \"base\");\n\n$clr-orange-50: map-get($clr-orange-list, \"50\");\n$clr-orange-100: map-get($clr-orange-list, \"100\");\n$clr-orange-200: map-get($clr-orange-list, \"200\");\n$clr-orange-300: map-get($clr-orange-list, \"300\");\n$clr-orange-400: map-get($clr-orange-list, \"400\");\n$clr-orange-500: map-get($clr-orange-list, \"500\");\n$clr-orange-600: map-get($clr-orange-list, \"600\");\n$clr-orange-700: map-get($clr-orange-list, \"700\");\n$clr-orange-800: map-get($clr-orange-list, \"800\");\n$clr-orange-900: map-get($clr-orange-list, \"900\");\n$clr-orange-a100: map-get($clr-orange-list, \"a100\");\n$clr-orange-a200: map-get($clr-orange-list, \"a200\");\n$clr-orange-a400: map-get($clr-orange-list, \"a400\");\n$clr-orange-a700: map-get($clr-orange-list, \"a700\");\n\n\n//\n// Deep orange\n//\n\n$clr-deep-orange-list: (\n \"base\": #ff5722,\n \"50\": #fbe9e7,\n \"100\": #ffccbc,\n \"200\": #ffab91,\n \"300\": #ff8a65,\n \"400\": #ff7043,\n \"500\": #ff5722,\n \"600\": #f4511e,\n \"700\": #e64a19,\n \"800\": #d84315,\n \"900\": #bf360c,\n \"a100\": #ff9e80,\n \"a200\": #ff6e40,\n \"a400\": #ff3d00,\n \"a700\": #dd2c00\n);\n\n$clr-deep-orange: map-get($clr-deep-orange-list, \"base\");\n\n$clr-deep-orange-50: map-get($clr-deep-orange-list, \"50\");\n$clr-deep-orange-100: map-get($clr-deep-orange-list, \"100\");\n$clr-deep-orange-200: map-get($clr-deep-orange-list, \"200\");\n$clr-deep-orange-300: map-get($clr-deep-orange-list, \"300\");\n$clr-deep-orange-400: map-get($clr-deep-orange-list, \"400\");\n$clr-deep-orange-500: map-get($clr-deep-orange-list, \"500\");\n$clr-deep-orange-600: map-get($clr-deep-orange-list, \"600\");\n$clr-deep-orange-700: map-get($clr-deep-orange-list, \"700\");\n$clr-deep-orange-800: map-get($clr-deep-orange-list, \"800\");\n$clr-deep-orange-900: map-get($clr-deep-orange-list, \"900\");\n$clr-deep-orange-a100: map-get($clr-deep-orange-list, \"a100\");\n$clr-deep-orange-a200: map-get($clr-deep-orange-list, \"a200\");\n$clr-deep-orange-a400: map-get($clr-deep-orange-list, \"a400\");\n$clr-deep-orange-a700: map-get($clr-deep-orange-list, \"a700\");\n\n\n//\n// Brown\n//\n\n$clr-brown-list: (\n \"base\": #795548,\n \"50\": #efebe9,\n \"100\": #d7ccc8,\n \"200\": #bcaaa4,\n \"300\": #a1887f,\n \"400\": #8d6e63,\n \"500\": #795548,\n \"600\": #6d4c41,\n \"700\": #5d4037,\n \"800\": #4e342e,\n \"900\": #3e2723,\n);\n\n$clr-brown: map-get($clr-brown-list, \"base\");\n\n$clr-brown-50: map-get($clr-brown-list, \"50\");\n$clr-brown-100: map-get($clr-brown-list, \"100\");\n$clr-brown-200: map-get($clr-brown-list, \"200\");\n$clr-brown-300: map-get($clr-brown-list, \"300\");\n$clr-brown-400: map-get($clr-brown-list, \"400\");\n$clr-brown-500: map-get($clr-brown-list, \"500\");\n$clr-brown-600: map-get($clr-brown-list, \"600\");\n$clr-brown-700: map-get($clr-brown-list, \"700\");\n$clr-brown-800: map-get($clr-brown-list, \"800\");\n$clr-brown-900: map-get($clr-brown-list, \"900\");\n\n\n//\n// Grey\n//\n\n$clr-grey-list: (\n \"base\": #9e9e9e,\n \"50\": #fafafa,\n \"100\": #f5f5f5,\n \"200\": #eeeeee,\n \"300\": #e0e0e0,\n \"400\": #bdbdbd,\n \"500\": #9e9e9e,\n \"600\": #757575,\n \"700\": #616161,\n \"800\": #424242,\n \"900\": #212121,\n);\n\n$clr-grey: map-get($clr-grey-list, \"base\");\n\n$clr-grey-50: map-get($clr-grey-list, \"50\");\n$clr-grey-100: map-get($clr-grey-list, \"100\");\n$clr-grey-200: map-get($clr-grey-list, \"200\");\n$clr-grey-300: map-get($clr-grey-list, \"300\");\n$clr-grey-400: map-get($clr-grey-list, \"400\");\n$clr-grey-500: map-get($clr-grey-list, \"500\");\n$clr-grey-600: map-get($clr-grey-list, \"600\");\n$clr-grey-700: map-get($clr-grey-list, \"700\");\n$clr-grey-800: map-get($clr-grey-list, \"800\");\n$clr-grey-900: map-get($clr-grey-list, \"900\");\n\n\n//\n// Blue grey\n//\n\n$clr-blue-grey-list: (\n \"base\": #607d8b,\n \"50\": #eceff1,\n \"100\": #cfd8dc,\n \"200\": #b0bec5,\n \"300\": #90a4ae,\n \"400\": #78909c,\n \"500\": #607d8b,\n \"600\": #546e7a,\n \"700\": #455a64,\n \"800\": #37474f,\n \"900\": #263238,\n);\n\n$clr-blue-grey: map-get($clr-blue-grey-list, \"base\");\n\n$clr-blue-grey-50: map-get($clr-blue-grey-list, \"50\");\n$clr-blue-grey-100: map-get($clr-blue-grey-list, \"100\");\n$clr-blue-grey-200: map-get($clr-blue-grey-list, \"200\");\n$clr-blue-grey-300: map-get($clr-blue-grey-list, \"300\");\n$clr-blue-grey-400: map-get($clr-blue-grey-list, \"400\");\n$clr-blue-grey-500: map-get($clr-blue-grey-list, \"500\");\n$clr-blue-grey-600: map-get($clr-blue-grey-list, \"600\");\n$clr-blue-grey-700: map-get($clr-blue-grey-list, \"700\");\n$clr-blue-grey-800: map-get($clr-blue-grey-list, \"800\");\n$clr-blue-grey-900: map-get($clr-blue-grey-list, \"900\");\n\n\n//\n// Black\n//\n\n$clr-black-list: (\n \"base\": #000\n);\n\n$clr-black: map-get($clr-black-list, \"base\");\n\n\n//\n// White\n//\n\n$clr-white-list: (\n \"base\": #fff\n);\n\n$clr-white: map-get($clr-white-list, \"base\");\n\n\n//\n// List for all Colors for looping\n//\n\n$clr-list-all: (\n \"red\": $clr-red-list,\n \"pink\": $clr-pink-list,\n \"purple\": $clr-purple-list,\n \"deep-purple\": $clr-deep-purple-list,\n \"indigo\": $clr-indigo-list,\n \"blue\": $clr-blue-list,\n \"light-blue\": $clr-light-blue-list,\n \"cyan\": $clr-cyan-list,\n \"teal\": $clr-teal-list,\n \"green\": $clr-green-list,\n \"light-green\": $clr-light-green-list,\n \"lime\": $clr-lime-list,\n \"yellow\": $clr-yellow-list,\n \"amber\": $clr-amber-list,\n \"orange\": $clr-orange-list,\n \"deep-orange\": $clr-deep-orange-list,\n \"brown\": $clr-brown-list,\n \"grey\": $clr-grey-list,\n \"blue-grey\": $clr-blue-grey-list,\n \"black\": $clr-black-list,\n \"white\": $clr-white-list\n);\n\n\n//\n// Typography\n//\n\n$clr-ui-display-4: $clr-grey-600;\n$clr-ui-display-3: $clr-grey-600;\n$clr-ui-display-2: $clr-grey-600;\n$clr-ui-display-1: $clr-grey-600;\n$clr-ui-headline: $clr-grey-900;\n$clr-ui-title: $clr-grey-900;\n$clr-ui-subhead-1: $clr-grey-900;\n$clr-ui-body-2: $clr-grey-900;\n$clr-ui-body-1: $clr-grey-900;\n$clr-ui-caption: $clr-grey-600;\n$clr-ui-menu: $clr-grey-900;\n$clr-ui-button: $clr-grey-900;\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Footnote reference\n [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n }\n\n // Footnote\n [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n }\n\n // Footnote container\n .footnote {\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n\n // Footnote list - omit left indentation\n > ol {\n margin-left: 0;\n\n // Footnote item - footnote items can contain lists, so we need to scope\n // the spacing adjustments to the top-level footnote item.\n > li {\n transition: color 125ms;\n\n // Darken color on target\n &:target {\n color: var(--md-default-fg-color);\n }\n\n // Show backreferences on footnote hover\n &:hover .footnote-backref,\n &:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0;\n }\n }\n }\n }\n\n // Footnote reference\n .footnote-ref {\n font-weight: 700;\n font-size: px2em(12px, 16px);\n }\n\n // Footnote backreference\n .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n // Hack: omit Unicode arrow for replacement with icon\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(px2rem(5px));\n opacity: 0;\n transition:\n color 250ms,\n transform 250ms 250ms,\n opacity 125ms 250ms;\n\n // [print]: Show footnote backreferences\n @media print {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-5px));\n }\n\n // Adjust color on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Footnote backreference icon\n &::before {\n display: inline-block;\n width: px2rem(16px);\n height: px2rem(16px);\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Headerlink\n .headerlink {\n display: inline-block;\n margin-left: px2rem(10px);\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition:\n color 250ms,\n opacity 125ms;\n\n // [print]: Hide headerlinks\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(10px);\n margin-left: initial;\n }\n }\n\n // Show headerlinks on parent hover\n :hover > .headerlink,\n :target > .headerlink,\n .headerlink:focus {\n opacity: 1;\n transition:\n color 250ms,\n opacity 125ms;\n }\n\n // Adjust color on parent target or focus/hover\n :target > .headerlink,\n .headerlink:focus,\n .headerlink:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Adjust scroll offset for all elements with `id` attributes - general scroll\n // margin offset for anything that can be targeted. Browser support is pretty\n // decent by now, but Edge <79 and Safari (iOS and macOS) still don't support\n // it properly, so we settle with a cross-browser anchor correction solution.\n :target {\n scroll-margin-top: px2rem(48px + 24px);\n }\n\n // Adjust scroll offset for headlines of level 1-3\n h1:target,\n h2:target,\n h3:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n content: \"\";\n }\n }\n\n // Adjust scroll offset for headlines of level 4\n h4:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n content: \"\";\n }\n }\n\n // Adjust scroll offset for headlines of level 5-6\n h5:target,\n h6:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px);\n padding-top: px2rem(48px + 24px);\n content: \"\";\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Arithmatex container\n div.arithmatex {\n overflow: auto;\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n margin: 0 px2rem(-16px);\n }\n\n // Arithmatex content\n > * {\n width: min-content;\n // stylelint-disable-next-line declaration-no-important\n margin: 1em auto !important;\n padding: 0 px2rem(16px);\n touch-action: auto;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Deletion, addition or comment\n del.critic,\n ins.critic,\n .critic.comment {\n box-decoration-break: clone;\n }\n\n // Deletion\n del.critic {\n background-color: var(--md-typeset-del-color);\n }\n\n // Addition\n ins.critic {\n background-color: var(--md-typeset-ins-color);\n }\n\n // Comment\n .critic.comment {\n color: var(--md-code-hl-comment-color);\n\n // Comment opening mark\n &::before {\n content: \"/* \";\n }\n\n // Comment closing mark\n &::after {\n content: \" */\";\n }\n }\n\n // Critic block\n .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: px2rem(16px);\n padding-left: px2rem(16px);\n overflow: auto;\n box-shadow: none;\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0.5em;\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0.5em;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Details\n details {\n @extend .admonition;\n\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n\n // Details title icon - rotate icon on transition to open state\n &[open] > summary::after {\n transform: rotate(90deg);\n }\n\n // Adjust spacing for details in closed state\n &:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n\n // Hack: we cannot set `overflow: hidden` on the `details` element (which\n // is why we set it to `overflow: visible`, as the outline would not be\n // visible when focusing. Therefore, we must set the border radius on the\n // summary explicitly.\n > summary {\n border-radius: px2rem(2px);\n }\n }\n\n // Hack: omit margin collapse\n &::after {\n display: table;\n content: \"\";\n }\n }\n\n // Details title\n summary {\n @extend .admonition-title;\n\n display: block;\n min-height: px2rem(20px);\n padding: px2rem(8px) px2rem(36px) px2rem(8px) px2rem(40px);\n border-top-left-radius: px2rem(2px);\n border-top-right-radius: px2rem(2px);\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: px2rem(8px) px2rem(44px) px2rem(8px) px2rem(36px);\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Details marker\n &::after {\n position: absolute;\n top: px2rem(8px);\n right: px2rem(8px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(8px);\n transform: rotate(180deg);\n }\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Emoji and icon container\n .emojione,\n .twemoji,\n .gemoji {\n display: inline-flex;\n height: px2em(18px);\n vertical-align: text-top;\n\n // Icon - inlined via mkdocs-material-extensions\n svg {\n width: px2em(18px);\n max-height: 100%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: syntax highlighting\n// ----------------------------------------------------------------------------\n\n// Code block\n.highlight {\n .o, // Operator\n .ow { // Operator, word\n color: var(--md-code-hl-operator-color);\n }\n\n .p { // Punctuation\n color: var(--md-code-hl-punctuation-color);\n }\n\n .cpf, // Comment, preprocessor file\n .l, // Literal\n .s, // Literal, string\n .sb, // Literal, string backticks\n .sc, // Literal, string char\n .s2, // Literal, string double\n .si, // Literal, string interpol\n .s1, // Literal, string single\n .ss { // Literal, string symbol\n color: var(--md-code-hl-string-color);\n }\n\n .cp, // Comment, pre-processor\n .se, // Literal, string escape\n .sh, // Literal, string heredoc\n .sr, // Literal, string regex\n .sx { // Literal, string other\n color: var(--md-code-hl-special-color);\n }\n\n .m, // Number\n .mb, // Number, binary\n .mf, // Number, float\n .mh, // Number, hex\n .mi, // Number, integer\n .il, // Number, integer long\n .mo { // Number, octal\n color: var(--md-code-hl-number-color);\n }\n\n .k, // Keyword,\n .kd, // Keyword, declaration\n .kn, // Keyword, namespace\n .kp, // Keyword, pseudo\n .kr, // Keyword, reserved\n .kt { // Keyword, type\n color: var(--md-code-hl-keyword-color);\n }\n\n .kc, // Keyword, constant\n .n { // Name\n color: var(--md-code-hl-name-color);\n }\n\n .no, // Name, constant\n .nb, // Name, builtin\n .bp { // Name, builtin pseudo\n color: var(--md-code-hl-constant-color);\n }\n\n .nc, // Name, class\n .ne, // Name, exception\n .nf, // Name, function\n .nn { // Name, namespace\n color: var(--md-code-hl-function-color);\n }\n\n .nd, // Name, decorator\n .ni, // Name, entity\n .nl, // Name, label\n .nt { // Name, tag\n color: var(--md-code-hl-keyword-color);\n }\n\n .c, // Comment\n .cm, // Comment, multiline\n .c1, // Comment, single\n .ch, // Comment, shebang\n .cs, // Comment, special\n .sd { // Literal, string doc\n color: var(--md-code-hl-comment-color);\n }\n\n .na, // Name, attribute\n .nv, // Variable,\n .vc, // Variable, class\n .vg, // Variable, global\n .vi { // Variable, instance\n color: var(--md-code-hl-variable-color);\n }\n\n .ge, // Generic, emph\n .gr, // Generic, error\n .gh, // Generic, heading\n .go, // Generic, output\n .gp, // Generic, prompt\n .gs, // Generic, strong\n .gu, // Generic, subheading\n .gt { // Generic, traceback\n color: var(--md-code-hl-generic-color);\n }\n\n .gd, // Diff, delete\n .gi { // Diff, insert\n margin: 0 px2em(-2px);\n padding: 0 px2em(2px);\n border-radius: px2rem(2px);\n }\n\n .gd { // Diff, delete\n background-color: var(--md-typeset-del-color);\n }\n\n .gi { // Diff, insert\n background-color: var(--md-typeset-ins-color);\n }\n\n // Highlighted line\n .hll {\n display: block;\n margin: 0 px2em(-16px, 13.6px);\n padding: 0 px2em(16px, 13.6px);\n background-color: var(--md-code-hl-color);\n }\n\n // Code block line numbers (inline)\n [data-linenos]::before {\n position: sticky;\n left: px2em(-16px, 13.6px);\n float: left;\n margin-right: px2em(16px, 13.6px);\n margin-left: px2em(-16px, 13.6px);\n padding-left: px2em(16px, 13.6px);\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Code block with line numbers\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n\n // Set table elements to block layout, because otherwise the whole flexbox\n // hacking won't work correctly\n tbody,\n td {\n display: block;\n padding: 0;\n }\n\n // We need to use flexbox layout, because otherwise it's not possible to\n // make the code container scroll while keeping the line numbers static\n tr {\n display: flex;\n }\n\n // The pre tags are nested inside a table, so we need to omit the margin\n // because it collapses below all the overflows\n pre {\n margin: 0;\n }\n\n // Code block line numbers - disable user selection, so code can be easily\n // copied without accidentally also copying the line numbers\n .linenos {\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n padding-right: 0;\n font-size: px2em(13.6px);\n background-color: var(--md-code-bg-color);\n user-select: none;\n }\n\n // Code block line numbers container\n .linenodiv {\n padding-right: px2em(8px, 13.6px);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n\n // Adjust colors and alignment\n pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n }\n }\n\n // Code block container - stretch to remaining space\n .code {\n flex: 1;\n overflow: hidden;\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Code block with line numbers\n .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: px2rem(2px);\n\n // Omit rounded borders on contained code block\n code {\n border-radius: 0;\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Top-level code block\n > .highlight {\n margin: 1em px2rem(-16px);\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n\n // Omit rounded borders\n code {\n border-radius: 0;\n }\n }\n\n // Top-level code block with line numbers\n > .highlighttable {\n margin: 1em px2rem(-16px);\n border-radius: 0;\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tabbed block content\n .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 px2rem(-1px) var(--md-default-fg-color--lightest);\n\n // [print]: Show all tabs (even hidden ones) when printing\n @media print {\n display: block;\n order: initial;\n }\n\n // Code block is the only child of a tab - remove margin and mirror\n // previous (now deprecated) SuperFences code block grouping behavior\n > pre:only-child,\n > .highlight:only-child pre,\n > .highlighttable:only-child {\n margin: 0;\n\n // Omit rounded borders\n > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n\n // Adjust spacing for nested tab\n > .tabbed-set {\n margin: 0;\n }\n }\n\n // Tabbed block container\n .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: px2rem(2px);\n\n // Tab radio button - the Tabbed extension will generate radio buttons with\n // labels, so tabs can be triggered without the necessity for JavaScript.\n // This is pretty cool, as it has great accessibility out-of-the box, so\n // we just hide the radio button and toggle the label color for indication.\n > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Tab label for checked radio button\n &:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n\n // Show tabbed block content\n + .tabbed-content {\n display: block;\n }\n }\n\n // Tab label on focus\n &:focus + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Tab label\n > label {\n z-index: 1;\n width: auto;\n padding: px2em(12px, 12.8px) 1.25em px2em(10px, 12.8px);\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2rem(12.8px);\n border-bottom: px2rem(2px) solid transparent;\n cursor: pointer;\n transition: color 250ms;\n\n // Tab label on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tasklist item\n .task-list-item {\n position: relative;\n list-style-type: none;\n\n // Make checkbox items align with normal list items, but position\n // everything in ems for correct layout at smaller font sizes\n [type=\"checkbox\"] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: -2em;\n left: initial;\n }\n }\n }\n\n // Hide native checkbox, when custom classes are enabled\n .task-list-control [type=\"checkbox\"] {\n z-index: -1;\n opacity: 0;\n }\n\n // Tasklist indicator in unchecked state\n .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: px2em(-24px);\n width: px2em(20px);\n height: px2em(20px);\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2em(-24px);\n left: initial;\n }\n }\n\n // Tasklist indicator in checked state\n [type=\"checkbox\"]:checked + .task-list-indicator::before {\n background-color: $clr-green-a400;\n mask-image: var(--md-tasklist-icon--checked);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // [tablet +]: Allow for rendering content as sidebars\n @include break-from-device(tablet) {\n\n // Modifier to float block elements\n .inline {\n float: left;\n width: px2rem(234px);\n margin-top: 0;\n margin-right: px2rem(16px);\n margin-bottom: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n }\n\n // Modifier to move to end (ltr: right, rtl: left)\n &.end {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(16px);\n margin-left: 0;\n }\n }\n }\n }\n}\n"]} \ No newline at end of file diff --git a/configuration/authentication/cookie/index.html b/configuration/authentication/cookie/index.html index d4584ded..1f83eaf9 100644 --- a/configuration/authentication/cookie/index.html +++ b/configuration/authentication/cookie/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/authentication/index.html b/configuration/authentication/index.html index cb233d22..68645dc4 100644 --- a/configuration/authentication/index.html +++ b/configuration/authentication/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/authentication/jwt/index.html b/configuration/authentication/jwt/index.html index 14dcd498..f5345436 100644 --- a/configuration/authentication/jwt/index.html +++ b/configuration/authentication/jwt/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/databases/mongodb/index.html b/configuration/databases/mongodb/index.html index a168d323..b570828d 100644 --- a/configuration/databases/mongodb/index.html +++ b/configuration/databases/mongodb/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/databases/ormar/index.html b/configuration/databases/ormar/index.html index 0946a533..5eb89ccf 100644 --- a/configuration/databases/ormar/index.html +++ b/configuration/databases/ormar/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/databases/sqlalchemy/index.html b/configuration/databases/sqlalchemy/index.html index c439829c..39b7da92 100644 --- a/configuration/databases/sqlalchemy/index.html +++ b/configuration/databases/sqlalchemy/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -857,11 +857,11 @@

FastAPI Users provides the necessary tools to work with SQL databases thanks to SQLAlchemy Core and encode/databases package for full async support.

Installation

Install the database driver that corresponds to your DBMS:

-
pip install databases[postgresql]
+
pip install 'databases[postgresql]'
 
-
pip install databases[mysql]
+
pip install 'databases[mysql]'
 
-
pip install databases[sqlite]
+
pip install 'databases[sqlite]'
 

For the sake of this tutorial from now on, we'll use a simple SQLite databse.

Setup User table

diff --git a/configuration/databases/tortoise/index.html b/configuration/databases/tortoise/index.html index 63ffd8a6..87930577 100644 --- a/configuration/databases/tortoise/index.html +++ b/configuration/databases/tortoise/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/full_example/index.html b/configuration/full_example/index.html index 9c0f450a..0b45058c 100644 --- a/configuration/full_example/index.html +++ b/configuration/full_example/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/model/index.html b/configuration/model/index.html index 7fdf9eed..8e6cbaf5 100644 --- a/configuration/model/index.html +++ b/configuration/model/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/oauth/index.html b/configuration/oauth/index.html index 820ee065..377c5c73 100644 --- a/configuration/oauth/index.html +++ b/configuration/oauth/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -1003,11 +1003,11 @@

FastAPI Users provides an optional OAuth2 authentication support. It relies on HTTPX OAuth library, which is a pure-async implementation of OAuth2.

Installation

You should install the library with the optional dependencies for OAuth:

-
pip install fastapi-users[sqlalchemy,oauth]
+
pip install 'fastapi-users[sqlalchemy,oauth]'
 
-
pip install fastapi-users[mongodb,oauth]
+
pip install 'fastapi-users[mongodb,oauth]'
 
-
pip install fastapi-users[tortoise-orm,oauth]
+
pip install 'fastapi-users[tortoise-orm,oauth]'
 

Configuration

Instantiate an OAuth2 client

diff --git a/configuration/routers/auth/index.html b/configuration/routers/auth/index.html index a25728e2..248e59a3 100644 --- a/configuration/routers/auth/index.html +++ b/configuration/routers/auth/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/routers/index.html b/configuration/routers/index.html index 8723d087..acd02214 100644 --- a/configuration/routers/index.html +++ b/configuration/routers/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/routers/register/index.html b/configuration/routers/register/index.html index fd4a3479..a54b37bc 100644 --- a/configuration/routers/register/index.html +++ b/configuration/routers/register/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/routers/reset/index.html b/configuration/routers/reset/index.html index 4e915e19..6a120867 100644 --- a/configuration/routers/reset/index.html +++ b/configuration/routers/reset/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/routers/users/index.html b/configuration/routers/users/index.html index 195db56e..a404ca41 100644 --- a/configuration/routers/users/index.html +++ b/configuration/routers/users/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/configuration/routers/verify/index.html b/configuration/routers/verify/index.html index f7804476..55df77a0 100644 --- a/configuration/routers/verify/index.html +++ b/configuration/routers/verify/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/index.html b/index.html index 05bc2dbc..306ab6c5 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -961,6 +961,9 @@
alexferrari88

💵
sandalwoodbox

🐛 + +
Vlad Hoi

📖 + diff --git a/installation/index.html b/installation/index.html index c126878c..6556e87b 100644 --- a/installation/index.html +++ b/installation/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -824,16 +824,16 @@

Installation

You can add FastAPI Users to your FastAPI project in a few easy steps. First of all, install the dependency:

With SQLAlchemy support

-
pip install fastapi-users[sqlalchemy]
+
pip install 'fastapi-users[sqlalchemy]'
 

With MongoDB support

-
pip install fastapi-users[mongodb]
+
pip install 'fastapi-users[mongodb]'
 

With Tortoise ORM support

-
pip install fastapi-users[tortoise-orm]
+
pip install 'fastapi-users[tortoise-orm]'
 

With ormar support

-
pip install fastapi-users[ormar]
+
pip install 'fastapi-users[ormar]'
 

That's it! Now, let's have a look at our User model.

diff --git a/migration/08_to_1x/index.html b/migration/08_to_1x/index.html index 78748211..1b9419f1 100644 --- a/migration/08_to_1x/index.html +++ b/migration/08_to_1x/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/migration/1x_to_2x/index.html b/migration/1x_to_2x/index.html index 95e1ea17..7e1be8a4 100644 --- a/migration/1x_to_2x/index.html +++ b/migration/1x_to_2x/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/migration/2x_to_3x/index.html b/migration/2x_to_3x/index.html index 72248fd6..c3b96c6d 100644 --- a/migration/2x_to_3x/index.html +++ b/migration/2x_to_3x/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/migration/3x_to_4x/index.html b/migration/3x_to_4x/index.html index dc394f49..c0f9e00f 100644 --- a/migration/3x_to_4x/index.html +++ b/migration/3x_to_4x/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/migration/4x_to_5x/index.html b/migration/4x_to_5x/index.html index 2651c950..6fe8a889 100644 --- a/migration/4x_to_5x/index.html +++ b/migration/4x_to_5x/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/search/search_index.json b/search/search_index.json index d07ee76a..02d123fb 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"FastAPI Users \u00b6 Ready-to-use and customizable users management for FastAPI Documentation : https://frankie567.github.io/fastapi-users/ Source Code : https://github.com/frankie567/fastapi-users Add quickly a registration and authentication system to your FastAPI project. FastAPI Users is designed to be as customizable and adaptable as possible. Features \u00b6 Extensible base user model Ready-to-use register, login, reset password and verify e-mail routes Ready-to-use OAuth2 flow Dependency callables to inject current user in route Customizable database backend SQLAlchemy async backend included thanks to encode/databases MongoDB async backend included thanks to mongodb/motor Tortoise ORM backend included ormar backend included Multiple customizable authentication backends JWT authentication backend included Cookie authentication backend included Full OpenAPI schema support, even with several authentication backends Contributors and sponsors \u2728\u2615\ufe0f \u00b6 Thanks goes to these wonderful people ( emoji key ): Fran\u00e7ois Voron \ud83d\udea7 Paolo Dina \ud83d\udcb5 \ud83d\udcbb Dmytro Ohorodnik \ud83d\udc1b Matthew D. Scholefield \ud83d\udc1b roywes \ud83d\udc1b \ud83d\udcbb Satwik Kansal \ud83d\udcd6 Edd Salkield \ud83d\udcbb \ud83d\udcd6 mark-todd \ud83d\udcbb \ud83d\udcd6 lill74 \ud83d\udc1b \ud83d\udcbb SelfhostedPro \ud83d\udee1\ufe0f \ud83d\udcbb Oskar Gmerek \ud83d\udcd6 Martin Collado \ud83d\udc1b \ud83d\udcbb Eric Lopes \ud83d\udcd6 \ud83d\udee1\ufe0f Beau Breon \ud83d\udcbb Niyas Mohammed \ud83d\udcd6 prostomarkeloff \ud83d\udcd6 \ud83d\udcbb Marius M\u00e9zerette \ud83d\udc1b \ud83e\udd14 Nickolas Grigoriadis \ud83d\udc1b Open Data Coder \ud83e\udd14 Mohammed Alshehri \ud83e\udd14 Tyler Renelle \ud83e\udd14 collerek \ud83d\udcbb Robert Bracco \ud83d\udcb5 Augusto Herrmann \ud83d\udcd6 Smithybrewer \ud83d\udc1b silllli \ud83d\udcd6 alexferrari88 \ud83d\udcb5 sandalwoodbox \ud83d\udc1b This project follows the all-contributors specification. Contributions of any kind welcome! Development \u00b6 Setup environment \u00b6 You should create a virtual environment and activate it: python -m venv venv/ source venv/bin/activate And then install the development dependencies: pip install -r requirements.dev.txt Run unit tests \u00b6 You can run all the tests with: make test The command will start a MongoDB container for the related unit tests. So you should have Docker installed. Alternatively, you can run pytest yourself. The MongoDB unit tests will be skipped if no server is available on your local machine: pytest There are quite a few unit tests, so you might run into ulimit issues where there are too many open file descriptors. You may be able to set a new, higher limit temporarily with: ulimit -n 2048 Format the code \u00b6 Execute the following command to apply isort and black formatting: make format License \u00b6 This project is licensed under the terms of the MIT license.","title":"About"},{"location":"#fastapi-users","text":"Ready-to-use and customizable users management for FastAPI Documentation : https://frankie567.github.io/fastapi-users/ Source Code : https://github.com/frankie567/fastapi-users Add quickly a registration and authentication system to your FastAPI project. FastAPI Users is designed to be as customizable and adaptable as possible.","title":"FastAPI Users"},{"location":"#features","text":"Extensible base user model Ready-to-use register, login, reset password and verify e-mail routes Ready-to-use OAuth2 flow Dependency callables to inject current user in route Customizable database backend SQLAlchemy async backend included thanks to encode/databases MongoDB async backend included thanks to mongodb/motor Tortoise ORM backend included ormar backend included Multiple customizable authentication backends JWT authentication backend included Cookie authentication backend included Full OpenAPI schema support, even with several authentication backends","title":"Features"},{"location":"#contributors-and-sponsors","text":"Thanks goes to these wonderful people ( emoji key ): Fran\u00e7ois Voron \ud83d\udea7 Paolo Dina \ud83d\udcb5 \ud83d\udcbb Dmytro Ohorodnik \ud83d\udc1b Matthew D. Scholefield \ud83d\udc1b roywes \ud83d\udc1b \ud83d\udcbb Satwik Kansal \ud83d\udcd6 Edd Salkield \ud83d\udcbb \ud83d\udcd6 mark-todd \ud83d\udcbb \ud83d\udcd6 lill74 \ud83d\udc1b \ud83d\udcbb SelfhostedPro \ud83d\udee1\ufe0f \ud83d\udcbb Oskar Gmerek \ud83d\udcd6 Martin Collado \ud83d\udc1b \ud83d\udcbb Eric Lopes \ud83d\udcd6 \ud83d\udee1\ufe0f Beau Breon \ud83d\udcbb Niyas Mohammed \ud83d\udcd6 prostomarkeloff \ud83d\udcd6 \ud83d\udcbb Marius M\u00e9zerette \ud83d\udc1b \ud83e\udd14 Nickolas Grigoriadis \ud83d\udc1b Open Data Coder \ud83e\udd14 Mohammed Alshehri \ud83e\udd14 Tyler Renelle \ud83e\udd14 collerek \ud83d\udcbb Robert Bracco \ud83d\udcb5 Augusto Herrmann \ud83d\udcd6 Smithybrewer \ud83d\udc1b silllli \ud83d\udcd6 alexferrari88 \ud83d\udcb5 sandalwoodbox \ud83d\udc1b This project follows the all-contributors specification. Contributions of any kind welcome!","title":"Contributors and sponsors \u2728\u2615\ufe0f"},{"location":"#development","text":"","title":"Development"},{"location":"#setup-environment","text":"You should create a virtual environment and activate it: python -m venv venv/ source venv/bin/activate And then install the development dependencies: pip install -r requirements.dev.txt","title":"Setup environment"},{"location":"#run-unit-tests","text":"You can run all the tests with: make test The command will start a MongoDB container for the related unit tests. So you should have Docker installed. Alternatively, you can run pytest yourself. The MongoDB unit tests will be skipped if no server is available on your local machine: pytest There are quite a few unit tests, so you might run into ulimit issues where there are too many open file descriptors. You may be able to set a new, higher limit temporarily with: ulimit -n 2048","title":"Run unit tests"},{"location":"#format-the-code","text":"Execute the following command to apply isort and black formatting: make format","title":"Format the code"},{"location":"#license","text":"This project is licensed under the terms of the MIT license.","title":"License"},{"location":"installation/","text":"Installation \u00b6 You can add FastAPI Users to your FastAPI project in a few easy steps. First of all, install the dependency: With SQLAlchemy support \u00b6 pip install fastapi-users [ sqlalchemy ] With MongoDB support \u00b6 pip install fastapi-users [ mongodb ] With Tortoise ORM support \u00b6 pip install fastapi-users [ tortoise-orm ] With ormar support \u00b6 pip install fastapi-users [ ormar ] That's it! Now, let's have a look at our User model .","title":"Installation"},{"location":"installation/#installation","text":"You can add FastAPI Users to your FastAPI project in a few easy steps. First of all, install the dependency:","title":"Installation"},{"location":"installation/#with-sqlalchemy-support","text":"pip install fastapi-users [ sqlalchemy ]","title":"With SQLAlchemy support"},{"location":"installation/#with-mongodb-support","text":"pip install fastapi-users [ mongodb ]","title":"With MongoDB support"},{"location":"installation/#with-tortoise-orm-support","text":"pip install fastapi-users [ tortoise-orm ]","title":"With Tortoise ORM support"},{"location":"installation/#with-ormar-support","text":"pip install fastapi-users [ ormar ] That's it! Now, let's have a look at our User model .","title":"With ormar support"},{"location":"configuration/full_example/","text":"Full example \u00b6 Here is a full working example with JWT authentication to help get you started. Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database. SQLAlchemy \u00b6 import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () MongoDB \u00b6 import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Tortoise ORM \u00b6 from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Ormar \u00b6 import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase DATABASE_URL = \"sqlite:///test.db\" SECRET = \"SECRET\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () What now? \u00b6 You're ready to go! Be sure to check the Usage section to understand how yo work with FastAPI Users .","title":"Full example"},{"location":"configuration/full_example/#full-example","text":"Here is a full working example with JWT authentication to help get you started. Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database.","title":"Full example"},{"location":"configuration/full_example/#sqlalchemy","text":"import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect ()","title":"SQLAlchemy"},{"location":"configuration/full_example/#mongodb","text":"import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ])","title":"MongoDB"},{"location":"configuration/full_example/#tortoise-orm","text":"from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ])","title":"Tortoise ORM"},{"location":"configuration/full_example/#ormar","text":"import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase DATABASE_URL = \"sqlite:///test.db\" SECRET = \"SECRET\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect ()","title":"Ormar"},{"location":"configuration/full_example/#what-now","text":"You're ready to go! Be sure to check the Usage section to understand how yo work with FastAPI Users .","title":"What now?"},{"location":"configuration/model/","text":"User model \u00b6 FastAPI Users defines a minimal User model for authentication purposes. It is structured like this: id ( UUID4 ) \u2013 Unique identifier of the user. Defaults to a UUID4 . email ( str ) \u2013 Email of the user. Validated by email-validator . is_active ( bool ) \u2013 Whether or not the user is active. If not, login and forgot password requests will be denied. Defaults to True . is_verified ( bool ) \u2013 Whether or not the user is verified. Optional but helpful with the verify router logic. Defaults to False . is_superuser ( bool ) \u2013 Whether or not the user is a superuser. Useful to implement administration logic. Defaults to False . Define your models \u00b6 There are four Pydantic models variations provided as mixins: BaseUser , which provides the basic fields and validation ; BaseCreateUser , dedicated to user registration, which consists of compulsory email and password fields ; BaseUpdateUser , dedicated to user profile update, which adds an optional password field ; BaseUserDB , which is a representation of the user in database, adding a hashed_password field. You should define each of those variations, inheriting from each mixin: from fastapi_users import models class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass You can of course add your own properties there to fit to your needs! Password validation \u00b6 FastAPI Users doesn't provide a default password validation, but you can implement it easily with a Pydantic validator on the UserCreate class. Here is a simple example to check if the password is at least six characters long: from fastapi_users import models from pydantic import validator class UserCreate ( models . BaseUserCreate ): @validator ( 'password' ) def valid_password ( cls , v : str ): if len ( v ) < 6 : raise ValueError ( 'Password should be at least 6 characters' ) return v Next steps \u00b6 Depending on your database backend, the database configuration will differ a bit. I'm using SQLAlchemy I'm using MongoDB I'm using Tortoise ORM I'm using ormar","title":"User model"},{"location":"configuration/model/#user-model","text":"FastAPI Users defines a minimal User model for authentication purposes. It is structured like this: id ( UUID4 ) \u2013 Unique identifier of the user. Defaults to a UUID4 . email ( str ) \u2013 Email of the user. Validated by email-validator . is_active ( bool ) \u2013 Whether or not the user is active. If not, login and forgot password requests will be denied. Defaults to True . is_verified ( bool ) \u2013 Whether or not the user is verified. Optional but helpful with the verify router logic. Defaults to False . is_superuser ( bool ) \u2013 Whether or not the user is a superuser. Useful to implement administration logic. Defaults to False .","title":"User model"},{"location":"configuration/model/#define-your-models","text":"There are four Pydantic models variations provided as mixins: BaseUser , which provides the basic fields and validation ; BaseCreateUser , dedicated to user registration, which consists of compulsory email and password fields ; BaseUpdateUser , dedicated to user profile update, which adds an optional password field ; BaseUserDB , which is a representation of the user in database, adding a hashed_password field. You should define each of those variations, inheriting from each mixin: from fastapi_users import models class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass You can of course add your own properties there to fit to your needs!","title":"Define your models"},{"location":"configuration/model/#password-validation","text":"FastAPI Users doesn't provide a default password validation, but you can implement it easily with a Pydantic validator on the UserCreate class. Here is a simple example to check if the password is at least six characters long: from fastapi_users import models from pydantic import validator class UserCreate ( models . BaseUserCreate ): @validator ( 'password' ) def valid_password ( cls , v : str ): if len ( v ) < 6 : raise ValueError ( 'Password should be at least 6 characters' ) return v","title":"Password validation"},{"location":"configuration/model/#next-steps","text":"Depending on your database backend, the database configuration will differ a bit. I'm using SQLAlchemy I'm using MongoDB I'm using Tortoise ORM I'm using ormar","title":"Next steps"},{"location":"configuration/oauth/","text":"OAuth2 \u00b6 FastAPI Users provides an optional OAuth2 authentication support. It relies on HTTPX OAuth library , which is a pure-async implementation of OAuth2. Installation \u00b6 You should install the library with the optional dependencies for OAuth: pip install fastapi-users [ sqlalchemy,oauth ] pip install fastapi-users [ mongodb,oauth ] pip install fastapi-users [ tortoise-orm,oauth ] Configuration \u00b6 Instantiate an OAuth2 client \u00b6 You first need to get an HTTPX OAuth client instance. Read the documentation for more information. from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) Setup the models \u00b6 The user models differ a bit from the standard one as we have to have a way to store the OAuth information (access tokens, account ids...). from fastapi_users import models class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass Notice that we inherit from the BaseOAuthAccountMixin , which adds a List of BaseOAuthAccount objects. This object is structured like this: id ( UUID4 ) \u2013 Unique identifier of the OAuth account information. Defaults to a UUID4 . oauth_name ( str ) \u2013 Name of the OAuth service. It corresponds to the name property of the OAuth client. access_token ( str ) \u2013 Access token. expires_at ( Optional[int] ) - Timestamp at which the access token is expired. refresh_token ( Optional[str] ) \u2013 On services that support it, a token to get a fresh access token. account_id ( str ) - Identifier of the OAuth account on the corresponding service. account_email ( str ) - Email address of the OAuth account on the corresponding service. Setup the database adapter \u00b6 SQLAlchemy \u00b6 You'll need to define the table for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.sqlalchemy import SQLAlchemyBaseOAuthAccountTable class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass Then, you should declare it on the database adapter: user_db = SQLAlchemyUserDatabase ( UserDB , database , User . __table__ , OAuthAccount . __table__ ) MongoDB \u00b6 Nothing to do, the basic configuration is enough. Tortoise ORM \u00b6 You'll need to define the Tortoise model for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.tortoise import TortoiseBaseOAuthAccountModel class OAuthAccount ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.User\" , related_name = \"oauth_accounts\" ) Warning Note that you should define the foreign key yourself, so that you can point it the user model in your namespace. Then, you should declare it on the database adapter: user_db = TortoiseUserDatabase ( UserDB , User , OAuthAccount ) Generate a router \u00b6 Once you have a FastAPIUsers instance, you can make it generate a single OAuth router for the given client. from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) After register \u00b6 You can provide a custom function to be called after a successful registration. It is called with two argument : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) Full example \u00b6 Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database. SQLAlchemy \u00b6 import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( SQLAlchemyBaseOAuthAccountTable , SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ oauth_accounts = OAuthAccount . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users , oauth_accounts ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () MongoDB \u00b6 import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase from httpx_oauth.clients.google import GoogleOAuth2 DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) Tortoise ORM \u00b6 from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( TortoiseBaseOAuthAccountModel , TortoiseBaseUserModel , TortoiseUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from tortoise import fields from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class UserModel ( TortoiseBaseUserModel ): pass class OAuthAccountModel ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.UserModel\" , related_name = \"oauth_accounts\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel , OAuthAccountModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"oauth_full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"OAuth2"},{"location":"configuration/oauth/#oauth2","text":"FastAPI Users provides an optional OAuth2 authentication support. It relies on HTTPX OAuth library , which is a pure-async implementation of OAuth2.","title":"OAuth2"},{"location":"configuration/oauth/#installation","text":"You should install the library with the optional dependencies for OAuth: pip install fastapi-users [ sqlalchemy,oauth ] pip install fastapi-users [ mongodb,oauth ] pip install fastapi-users [ tortoise-orm,oauth ]","title":"Installation"},{"location":"configuration/oauth/#configuration","text":"","title":"Configuration"},{"location":"configuration/oauth/#instantiate-an-oauth2-client","text":"You first need to get an HTTPX OAuth client instance. Read the documentation for more information. from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" )","title":"Instantiate an OAuth2 client"},{"location":"configuration/oauth/#setup-the-models","text":"The user models differ a bit from the standard one as we have to have a way to store the OAuth information (access tokens, account ids...). from fastapi_users import models class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass Notice that we inherit from the BaseOAuthAccountMixin , which adds a List of BaseOAuthAccount objects. This object is structured like this: id ( UUID4 ) \u2013 Unique identifier of the OAuth account information. Defaults to a UUID4 . oauth_name ( str ) \u2013 Name of the OAuth service. It corresponds to the name property of the OAuth client. access_token ( str ) \u2013 Access token. expires_at ( Optional[int] ) - Timestamp at which the access token is expired. refresh_token ( Optional[str] ) \u2013 On services that support it, a token to get a fresh access token. account_id ( str ) - Identifier of the OAuth account on the corresponding service. account_email ( str ) - Email address of the OAuth account on the corresponding service.","title":"Setup the models"},{"location":"configuration/oauth/#setup-the-database-adapter","text":"","title":"Setup the database adapter"},{"location":"configuration/oauth/#sqlalchemy","text":"You'll need to define the table for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.sqlalchemy import SQLAlchemyBaseOAuthAccountTable class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass Then, you should declare it on the database adapter: user_db = SQLAlchemyUserDatabase ( UserDB , database , User . __table__ , OAuthAccount . __table__ )","title":"SQLAlchemy"},{"location":"configuration/oauth/#mongodb","text":"Nothing to do, the basic configuration is enough.","title":"MongoDB"},{"location":"configuration/oauth/#tortoise-orm","text":"You'll need to define the Tortoise model for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.tortoise import TortoiseBaseOAuthAccountModel class OAuthAccount ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.User\" , related_name = \"oauth_accounts\" ) Warning Note that you should define the foreign key yourself, so that you can point it the user model in your namespace. Then, you should declare it on the database adapter: user_db = TortoiseUserDatabase ( UserDB , User , OAuthAccount )","title":"Tortoise ORM"},{"location":"configuration/oauth/#generate-a-router","text":"Once you have a FastAPIUsers instance, you can make it generate a single OAuth router for the given client. from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"Generate a router"},{"location":"configuration/oauth/#after-register","text":"You can provide a custom function to be called after a successful registration. It is called with two argument : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"After register"},{"location":"configuration/oauth/#full-example","text":"Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database.","title":"Full example"},{"location":"configuration/oauth/#sqlalchemy_1","text":"import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( SQLAlchemyBaseOAuthAccountTable , SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ oauth_accounts = OAuthAccount . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users , oauth_accounts ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect ()","title":"SQLAlchemy"},{"location":"configuration/oauth/#mongodb_1","text":"import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase from httpx_oauth.clients.google import GoogleOAuth2 DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"MongoDB"},{"location":"configuration/oauth/#tortoise-orm_1","text":"from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( TortoiseBaseOAuthAccountModel , TortoiseBaseUserModel , TortoiseUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from tortoise import fields from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class UserModel ( TortoiseBaseUserModel ): pass class OAuthAccountModel ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.UserModel\" , related_name = \"oauth_accounts\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel , OAuthAccountModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"oauth_full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"Tortoise ORM"},{"location":"configuration/authentication/","text":"Authentication \u00b6 FastAPI Users allows you to plug in several authentication methods. How it works? \u00b6 You can have several authentication methods, e.g. a cookie authentication for browser-based queries and a JWT token authentication for pure API queries. When checking authentication, each method is run one after the other. The first method yielding a user wins. If no method yields a user, an HTTPException is raised. For each backend, you'll be able to add a router with the corresponding /login and /logout (if applicable routes). More on this in the routers documentation . Provided methods \u00b6 JWT authentication Cookie authentication","title":"Introduction"},{"location":"configuration/authentication/#authentication","text":"FastAPI Users allows you to plug in several authentication methods.","title":"Authentication"},{"location":"configuration/authentication/#how-it-works","text":"You can have several authentication methods, e.g. a cookie authentication for browser-based queries and a JWT token authentication for pure API queries. When checking authentication, each method is run one after the other. The first method yielding a user wins. If no method yields a user, an HTTPException is raised. For each backend, you'll be able to add a router with the corresponding /login and /logout (if applicable routes). More on this in the routers documentation .","title":"How it works?"},{"location":"configuration/authentication/#provided-methods","text":"JWT authentication Cookie authentication","title":"Provided methods"},{"location":"configuration/authentication/cookie/","text":"Cookie \u00b6 Cookies are an easy way to store stateful information into the user browser. Thus, it is more useful for browser-based navigation (e.g. a front-end app making API requests) rather than pure API interaction. Configuration \u00b6 from fastapi_users.authentication import CookieAuthentication SECRET = \"SECRET\" auth_backends = [] cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( cookie_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of the cookie (in seconds). You can also define the parameters for the generated cookie: cookie_name ( fastapiusersauth ): Name of the cookie. cookie_path ( / ): Cookie path. cookie_domain ( None ): Cookie domain. cookie_secure ( True ): Whether to only send the cookie to the server via SSL request. cookie_httponly ( True ): Whether to prevent access to the cookie via JavaScript. cookie_samesite ( lax ): A string that specifies the samesite strategy for the cookie. Valid values are 'lax', 'strict' and 'none'. Defaults to 'lax'. Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to cookie . cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-cookie\" , ) Tip The value of the cookie is actually a JWT. This authentication backend shares most of its logic with the JWT one. Login \u00b6 This method will return a response with a valid set-cookie header upon successful login: 200 OK Check documentation about login route . Logout \u00b6 This method will remove the authentication cookie: 200 OK Check documentation about logout route . Authentication \u00b6 This method expects that you provide a valid cookie in the headers. Next steps \u00b6 We will now configure the main FastAPI Users object that will expose the routers .","title":"Cookie"},{"location":"configuration/authentication/cookie/#cookie","text":"Cookies are an easy way to store stateful information into the user browser. Thus, it is more useful for browser-based navigation (e.g. a front-end app making API requests) rather than pure API interaction.","title":"Cookie"},{"location":"configuration/authentication/cookie/#configuration","text":"from fastapi_users.authentication import CookieAuthentication SECRET = \"SECRET\" auth_backends = [] cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( cookie_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of the cookie (in seconds). You can also define the parameters for the generated cookie: cookie_name ( fastapiusersauth ): Name of the cookie. cookie_path ( / ): Cookie path. cookie_domain ( None ): Cookie domain. cookie_secure ( True ): Whether to only send the cookie to the server via SSL request. cookie_httponly ( True ): Whether to prevent access to the cookie via JavaScript. cookie_samesite ( lax ): A string that specifies the samesite strategy for the cookie. Valid values are 'lax', 'strict' and 'none'. Defaults to 'lax'. Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to cookie . cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-cookie\" , ) Tip The value of the cookie is actually a JWT. This authentication backend shares most of its logic with the JWT one.","title":"Configuration"},{"location":"configuration/authentication/cookie/#login","text":"This method will return a response with a valid set-cookie header upon successful login: 200 OK Check documentation about login route .","title":"Login"},{"location":"configuration/authentication/cookie/#logout","text":"This method will remove the authentication cookie: 200 OK Check documentation about logout route .","title":"Logout"},{"location":"configuration/authentication/cookie/#authentication","text":"This method expects that you provide a valid cookie in the headers.","title":"Authentication"},{"location":"configuration/authentication/cookie/#next-steps","text":"We will now configure the main FastAPI Users object that will expose the routers .","title":"Next steps"},{"location":"configuration/authentication/jwt/","text":"JWT \u00b6 JSON Web Token (JWT) is an internet standard for creating access tokens based on JSON. Configuration \u00b6 from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" auth_backends = [] jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( jwt_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of token (in seconds). Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to jwt . jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-jwt\" , ) Login \u00b6 This method will return a JWT token upon successful login: 200 OK { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Check documentation about login route . Logout \u00b6 This backend does not provide a logout method (a JWT is valid until it expires). Authentication \u00b6 This method expects that you provide a Bearer authentication with a valid JWT. curl http://localhost:9000/protected-route -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI' Tip: Refresh \u00b6 The default implementation does not provide a mechanism to refresh the JWT. However, you can implement it quite easily like this: from fastapi import Depends , Response @router . post ( \"/auth/jwt/refresh\" ) async def refresh_jwt ( response : Response , user = Depends ( fastapi_users . get_current_active_user )): return await jwt_authentication . get_login_response ( user , response ) Next steps \u00b6 We will now configure the main FastAPI Users object that will expose the routers .","title":"JWT"},{"location":"configuration/authentication/jwt/#jwt","text":"JSON Web Token (JWT) is an internet standard for creating access tokens based on JSON.","title":"JWT"},{"location":"configuration/authentication/jwt/#configuration","text":"from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" auth_backends = [] jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( jwt_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of token (in seconds). Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to jwt . jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-jwt\" , )","title":"Configuration"},{"location":"configuration/authentication/jwt/#login","text":"This method will return a JWT token upon successful login: 200 OK { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Check documentation about login route .","title":"Login"},{"location":"configuration/authentication/jwt/#logout","text":"This backend does not provide a logout method (a JWT is valid until it expires).","title":"Logout"},{"location":"configuration/authentication/jwt/#authentication","text":"This method expects that you provide a Bearer authentication with a valid JWT. curl http://localhost:9000/protected-route -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI'","title":"Authentication"},{"location":"configuration/authentication/jwt/#tip-refresh","text":"The default implementation does not provide a mechanism to refresh the JWT. However, you can implement it quite easily like this: from fastapi import Depends , Response @router . post ( \"/auth/jwt/refresh\" ) async def refresh_jwt ( response : Response , user = Depends ( fastapi_users . get_current_active_user )): return await jwt_authentication . get_login_response ( user , response )","title":"Tip: Refresh"},{"location":"configuration/authentication/jwt/#next-steps","text":"We will now configure the main FastAPI Users object that will expose the routers .","title":"Next steps"},{"location":"configuration/databases/mongodb/","text":"MongoDB \u00b6 FastAPI Users provides the necessary tools to work with MongoDB databases thanks to mongodb/motor package for full async support. Setup database connection and collection \u00b6 Let's create a MongoDB connection and instantiate a collection. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) You can choose any name for the database and the collection. Warning You may have noticed the uuidRepresentation parameter. It controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this . Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) Notice that we pass a reference to your UserDB model . Info The database adapter will automatically create a unique index on id and email . Warning FastAPI Users will use its defined id UUID as unique identifier for the user, rather than the builtin MongoDB _id . Next steps \u00b6 We will now configure an authentication method .","title":"MongoDB"},{"location":"configuration/databases/mongodb/#mongodb","text":"FastAPI Users provides the necessary tools to work with MongoDB databases thanks to mongodb/motor package for full async support.","title":"MongoDB"},{"location":"configuration/databases/mongodb/#setup-database-connection-and-collection","text":"Let's create a MongoDB connection and instantiate a collection. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) You can choose any name for the database and the collection. Warning You may have noticed the uuidRepresentation parameter. It controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this .","title":"Setup database connection and collection"},{"location":"configuration/databases/mongodb/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) Notice that we pass a reference to your UserDB model . Info The database adapter will automatically create a unique index on id and email . Warning FastAPI Users will use its defined id UUID as unique identifier for the user, rather than the builtin MongoDB _id .","title":"Create the database adapter"},{"location":"configuration/databases/mongodb/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/databases/ormar/","text":"Ormar \u00b6 FastAPI Users provides the necessary tools to work with ormar. Installation \u00b6 Install the database driver that corresponds to your DBMS: pip install asyncpg psycopg2 pip install aiomysql pymysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse. Setup User table \u00b6 Let's declare our User ORM model. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs! Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () Notice that we pass a reference to your UserDB model . Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic . Next steps \u00b6 We will now configure an authentication method .","title":"Ormar"},{"location":"configuration/databases/ormar/#ormar","text":"FastAPI Users provides the necessary tools to work with ormar.","title":"Ormar"},{"location":"configuration/databases/ormar/#installation","text":"Install the database driver that corresponds to your DBMS: pip install asyncpg psycopg2 pip install aiomysql pymysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse.","title":"Installation"},{"location":"configuration/databases/ormar/#setup-user-table","text":"Let's declare our User ORM model. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs!","title":"Setup User table"},{"location":"configuration/databases/ormar/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () Notice that we pass a reference to your UserDB model . Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic .","title":"Create the database adapter"},{"location":"configuration/databases/ormar/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/databases/sqlalchemy/","text":"SQLAlchemy \u00b6 FastAPI Users provides the necessary tools to work with SQL databases thanks to SQLAlchemy Core and encode/databases package for full async support. Installation \u00b6 Install the database driver that corresponds to your DBMS: pip install databases [ postgresql ] pip install databases [ mysql ] pip install databases [ sqlite ] For the sake of this tutorial from now on, we'll use a simple SQLite databse. Setup User table \u00b6 Let's create a metadata object and declare our User table. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () As you can see, FastAPI Users provides a mixin that will include base fields for our User table. You can of course add you own fields there to fit to your needs! Create the tables \u00b6 We'll now create an SQLAlchemy engine and ask it to create all the defined tables. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic . Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Notice that we pass it three things: A reference to your UserDB model . The users variable, which is the actual SQLAlchemy table behind the table class. A database instance, which allows us to do asynchronous request to the database. Next steps \u00b6 We will now configure an authentication method . What about SQLAlchemy ORM? \u00b6 The primary objective was to use pure async approach as much as possible. However, we understand that ORM is convenient and useful for many developers. If this feature becomes very demanded, we will add a database adapter for SQLAlchemy ORM.","title":"SQLAlchemy"},{"location":"configuration/databases/sqlalchemy/#sqlalchemy","text":"FastAPI Users provides the necessary tools to work with SQL databases thanks to SQLAlchemy Core and encode/databases package for full async support.","title":"SQLAlchemy"},{"location":"configuration/databases/sqlalchemy/#installation","text":"Install the database driver that corresponds to your DBMS: pip install databases [ postgresql ] pip install databases [ mysql ] pip install databases [ sqlite ] For the sake of this tutorial from now on, we'll use a simple SQLite databse.","title":"Installation"},{"location":"configuration/databases/sqlalchemy/#setup-user-table","text":"Let's create a metadata object and declare our User table. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () As you can see, FastAPI Users provides a mixin that will include base fields for our User table. You can of course add you own fields there to fit to your needs!","title":"Setup User table"},{"location":"configuration/databases/sqlalchemy/#create-the-tables","text":"We'll now create an SQLAlchemy engine and ask it to create all the defined tables. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic .","title":"Create the tables"},{"location":"configuration/databases/sqlalchemy/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Notice that we pass it three things: A reference to your UserDB model . The users variable, which is the actual SQLAlchemy table behind the table class. A database instance, which allows us to do asynchronous request to the database.","title":"Create the database adapter"},{"location":"configuration/databases/sqlalchemy/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/databases/sqlalchemy/#what-about-sqlalchemy-orm","text":"The primary objective was to use pure async approach as much as possible. However, we understand that ORM is convenient and useful for many developers. If this feature becomes very demanded, we will add a database adapter for SQLAlchemy ORM.","title":"What about SQLAlchemy ORM?"},{"location":"configuration/databases/tortoise/","text":"Tortoise ORM \u00b6 FastAPI Users provides the necessary tools to work with Tortoise ORM. Installation \u00b6 Install the database driver that corresponds to your DBMS: pip install asyncpg pip install aiomysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse. Setup User table \u00b6 Let's declare our User ORM model. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs! Tweak UserDB model \u00b6 In order to make the Pydantic model and the Tortoise ORM model working well together, you'll have to add a mixin and some configuration options to your UserDB model. Tortoise ORM provides utilities to ease the integration with Pydantic and we'll use them here. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) The PydanticModel mixin adds methods used internally by Tortoise ORM to the Pydantic model so that it can easily transform it back to an ORM model. It expects then that you provide the property orig_model which should point to the User ORM model we defined just above . Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Notice that we pass a reference to your UserDB model . Register Tortoise \u00b6 For using Tortoise ORM we must register our models and database. Tortoise ORM supports integration with FastAPI out-of-the-box. It will automatically bind startup and shutdown events. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See https://tortoise-orm.readthedocs.io/en/latest/migration.html . Next steps \u00b6 We will now configure an authentication method .","title":"Tortoise ORM"},{"location":"configuration/databases/tortoise/#tortoise-orm","text":"FastAPI Users provides the necessary tools to work with Tortoise ORM.","title":"Tortoise ORM"},{"location":"configuration/databases/tortoise/#installation","text":"Install the database driver that corresponds to your DBMS: pip install asyncpg pip install aiomysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse.","title":"Installation"},{"location":"configuration/databases/tortoise/#setup-user-table","text":"Let's declare our User ORM model. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs!","title":"Setup User table"},{"location":"configuration/databases/tortoise/#tweak-userdb-model","text":"In order to make the Pydantic model and the Tortoise ORM model working well together, you'll have to add a mixin and some configuration options to your UserDB model. Tortoise ORM provides utilities to ease the integration with Pydantic and we'll use them here. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) The PydanticModel mixin adds methods used internally by Tortoise ORM to the Pydantic model so that it can easily transform it back to an ORM model. It expects then that you provide the property orig_model which should point to the User ORM model we defined just above .","title":"Tweak UserDB model"},{"location":"configuration/databases/tortoise/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Notice that we pass a reference to your UserDB model .","title":"Create the database adapter"},{"location":"configuration/databases/tortoise/#register-tortoise","text":"For using Tortoise ORM we must register our models and database. Tortoise ORM supports integration with FastAPI out-of-the-box. It will automatically bind startup and shutdown events. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See https://tortoise-orm.readthedocs.io/en/latest/migration.html .","title":"Register Tortoise"},{"location":"configuration/databases/tortoise/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/routers/","text":"Routers \u00b6 We're almost there! The last step is to configure the FastAPIUsers object that will wire the database adapter, the authentication classes and let us generate the actual API routes . Configure FastAPIUsers \u00b6 Configure FastAPIUsers object with all the elements we defined before. More precisely: db : Database adapter instance. auth_backends : List of authentication backends. See Authentication . user_model : Pydantic model of a user. user_create_model : Pydantic model for creating a user. user_update_model : Pydantic model for updating a user. user_db_model : Pydantic model of a DB representation of a user. from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) Available routers \u00b6 This helper class will let you generate useful routers to setup the authentication system. Each of them is optional , so you can pick only the one that you are interested in! Here are the routers provided: Auth router : Provides /login and /logout routes for a given authentication backend . Register router : Provides /register routes to allow a user to create a new account. Reset password router : Provides /forgot-password and /reset-password routes to allow a user to reset its password. Verify router : Provides /request-verify-token and /verify routes to manage user e-mail verification. Users router : Provides routes to manage users. OAuth router : Provides routes to perform an OAuth authentication against a service provider (like Google or Facebook). You should check out each of them to understand how to use them.","title":"Introduction"},{"location":"configuration/routers/#routers","text":"We're almost there! The last step is to configure the FastAPIUsers object that will wire the database adapter, the authentication classes and let us generate the actual API routes .","title":"Routers"},{"location":"configuration/routers/#configure-fastapiusers","text":"Configure FastAPIUsers object with all the elements we defined before. More precisely: db : Database adapter instance. auth_backends : List of authentication backends. See Authentication . user_model : Pydantic model of a user. user_create_model : Pydantic model for creating a user. user_update_model : Pydantic model for updating a user. user_db_model : Pydantic model of a DB representation of a user. from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , )","title":"Configure FastAPIUsers"},{"location":"configuration/routers/#available-routers","text":"This helper class will let you generate useful routers to setup the authentication system. Each of them is optional , so you can pick only the one that you are interested in! Here are the routers provided: Auth router : Provides /login and /logout routes for a given authentication backend . Register router : Provides /register routes to allow a user to create a new account. Reset password router : Provides /forgot-password and /reset-password routes to allow a user to reset its password. Verify router : Provides /request-verify-token and /verify routes to manage user e-mail verification. Users router : Provides routes to manage users. OAuth router : Provides routes to perform an OAuth authentication against a service provider (like Google or Facebook). You should check out each of them to understand how to use them.","title":"Available routers"},{"location":"configuration/routers/auth/","text":"Auth router \u00b6 The auth router will generate /login and /logout (if applicable) routes for a given authentication backend . Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], ) Optional: user verification \u00b6 You can require the user to be verified (i.e. is_verified property set to True ) to allow login. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_auth_router ( jwt_authentication , requires_verification = True ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], )","title":"Auth router"},{"location":"configuration/routers/auth/#auth-router","text":"The auth router will generate /login and /logout (if applicable) routes for a given authentication backend . Check the routes usage to learn how to use them.","title":"Auth router"},{"location":"configuration/routers/auth/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], )","title":"Setup"},{"location":"configuration/routers/auth/#optional-user-verification","text":"You can require the user to be verified (i.e. is_verified property set to True ) to allow login. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_auth_router ( jwt_authentication , requires_verification = True ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], )","title":"Optional: user verification"},{"location":"configuration/routers/register/","text":"Register routes \u00b6 The register router will generate a /register route to allow a user to create a new account. Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_register_router (), prefix = \"/auth\" , tags = [ \"auth\" ], ) After register \u00b6 You can provide a custom function to be called after a successful registration. It is called with two arguments : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Register routes"},{"location":"configuration/routers/register/#register-routes","text":"The register router will generate a /register route to allow a user to create a new account. Check the routes usage to learn how to use them.","title":"Register routes"},{"location":"configuration/routers/register/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_register_router (), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Setup"},{"location":"configuration/routers/register/#after-register","text":"You can provide a custom function to be called after a successful registration. It is called with two arguments : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After register"},{"location":"configuration/routers/reset/","text":"Reset password router \u00b6 The reset password router will generate /forgot-password (the user asks for a token to reset its password) and /reset-password (the user changes its password given the token) routes. Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: reset_password_token_secret : Secret to encode reset password token. reset_password_token_lifetime_seconds : Lifetime of reset password token. Defaults to 3600 . after_forgot_password : Optional function called after a successful forgot password request. See below. After forgot password \u00b6 You can provide a custom function to be called after a successful forgot password request. It is called with three arguments : The user which has requested to reset their password. A ready-to-use JWT token that will be accepted by the reset password route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to reset their password. You can define it as an async or standard method. Example: def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) After reset password \u00b6 You can provide a custom function to be called after a successful password reset. It is called with two arguments : The user which has reset their password. The original Request object . For example, you may want to send an e-mail to the concerned user to warn him that their password has been changed and that they should take action if they think they have been hacked. You can define it as an async or standard method. Example: def on_after_reset_password ( user : UserDB , request : Request ): print ( f \"User { user . id } has reset their password.\" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_reset_password = on_after_reset_password ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Reset password router"},{"location":"configuration/routers/reset/#reset-password-router","text":"The reset password router will generate /forgot-password (the user asks for a token to reset its password) and /reset-password (the user changes its password given the token) routes. Check the routes usage to learn how to use them.","title":"Reset password router"},{"location":"configuration/routers/reset/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: reset_password_token_secret : Secret to encode reset password token. reset_password_token_lifetime_seconds : Lifetime of reset password token. Defaults to 3600 . after_forgot_password : Optional function called after a successful forgot password request. See below.","title":"Setup"},{"location":"configuration/routers/reset/#after-forgot-password","text":"You can provide a custom function to be called after a successful forgot password request. It is called with three arguments : The user which has requested to reset their password. A ready-to-use JWT token that will be accepted by the reset password route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to reset their password. You can define it as an async or standard method. Example: def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After forgot password"},{"location":"configuration/routers/reset/#after-reset-password","text":"You can provide a custom function to be called after a successful password reset. It is called with two arguments : The user which has reset their password. The original Request object . For example, you may want to send an e-mail to the concerned user to warn him that their password has been changed and that they should take action if they think they have been hacked. You can define it as an async or standard method. Example: def on_after_reset_password ( user : UserDB , request : Request ): print ( f \"User { user . id } has reset their password.\" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_reset_password = on_after_reset_password ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After reset password"},{"location":"configuration/routers/users/","text":"Users router \u00b6 This router provides routes to manage users. Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ], ) Optional: user verification \u00b6 You can require the user to be verified (i.e. is_verified property set to True ) to access those routes. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_users_router ( requires_verification = True ), prefix = \"/users\" , tags = [ \"users\" ], ) After update \u00b6 You can provide a custom function to be called after a successful update user request. It is called with three arguments : The user which was updated. The dictionary containing the updated fields. The original Request object . It may be useful, for example, if you wish to update your user in a data analytics or customer success platform. You can define it as an async or standard method. Example: def on_after_update ( user : UserDB , updated_user_data : Dict [ str , Any ], request : Request ): print ( f \"User { user . id } has been updated with the following data: { updated_user_data } \" ) app . include_router ( fastapi_users . get_users_router ( on_after_update ), prefix = \"/users\" , tags = [ \"users\" ], )","title":"Users router"},{"location":"configuration/routers/users/#users-router","text":"This router provides routes to manage users. Check the routes usage to learn how to use them.","title":"Users router"},{"location":"configuration/routers/users/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ], )","title":"Setup"},{"location":"configuration/routers/users/#optional-user-verification","text":"You can require the user to be verified (i.e. is_verified property set to True ) to access those routes. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_users_router ( requires_verification = True ), prefix = \"/users\" , tags = [ \"users\" ], )","title":"Optional: user verification"},{"location":"configuration/routers/users/#after-update","text":"You can provide a custom function to be called after a successful update user request. It is called with three arguments : The user which was updated. The dictionary containing the updated fields. The original Request object . It may be useful, for example, if you wish to update your user in a data analytics or customer success platform. You can define it as an async or standard method. Example: def on_after_update ( user : UserDB , updated_user_data : Dict [ str , Any ], request : Request ): print ( f \"User { user . id } has been updated with the following data: { updated_user_data } \" ) app . include_router ( fastapi_users . get_users_router ( on_after_update ), prefix = \"/users\" , tags = [ \"users\" ], )","title":"After update"},{"location":"configuration/routers/verify/","text":"Verify router \u00b6 This router provides routes to manage user email verification. Check the routes usage to learn how to use them. \ud83d\udc4f\ud83d\udc4f\ud83d\udc4f A big thank you to Edd Salkield and Mark Todd who worked hard on this feature! Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: verification_token_secret : Secret to encode verify token. verification_token_lifetime_seconds : Lifetime of verify token. Defaults to 3600 . after_verification_request : Optional function called after a successful verify request. See below. after_verification : Optional function called after a successful verification. See below. After verification request \u00b6 You can provide a custom function to be called after a successful verification request. It is called with three arguments : The user for which the verification has been requested. A ready-to-use JWT token that will be accepted by the verify route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to verify their e-mail. You can define it as an async or standard method. Example: def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) After verification \u00b6 You can provide a custom function to be called after a successful user verification. It is called with two arguments : The user that has been verified. The original Request object . This may be useful if you wish to send another e-mail or store this information in a data analytics or customer success platform. You can define it as an async or standard method. Example: def after_verification ( user : UserDB , request : Request ): print ( f \" { user . id } is now verified.\" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification = after_verification ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Verify router"},{"location":"configuration/routers/verify/#verify-router","text":"This router provides routes to manage user email verification. Check the routes usage to learn how to use them. \ud83d\udc4f\ud83d\udc4f\ud83d\udc4f A big thank you to Edd Salkield and Mark Todd who worked hard on this feature!","title":"Verify router"},{"location":"configuration/routers/verify/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: verification_token_secret : Secret to encode verify token. verification_token_lifetime_seconds : Lifetime of verify token. Defaults to 3600 . after_verification_request : Optional function called after a successful verify request. See below. after_verification : Optional function called after a successful verification. See below.","title":"Setup"},{"location":"configuration/routers/verify/#after-verification-request","text":"You can provide a custom function to be called after a successful verification request. It is called with three arguments : The user for which the verification has been requested. A ready-to-use JWT token that will be accepted by the verify route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to verify their e-mail. You can define it as an async or standard method. Example: def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After verification request"},{"location":"configuration/routers/verify/#after-verification","text":"You can provide a custom function to be called after a successful user verification. It is called with two arguments : The user that has been verified. The original Request object . This may be useful if you wish to send another e-mail or store this information in a data analytics or customer success platform. You can define it as an async or standard method. Example: def after_verification ( user : UserDB , request : Request ): print ( f \" { user . id } is now verified.\" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification = after_verification ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After verification"},{"location":"migration/08_to_1x/","text":"0.8.x \u27a1\ufe0f 1.x.x \u00b6 1.0 version introduces major breaking changes that need you to update some of your code and migrate your data. Id. are UUID \u00b6 Users and OAuth accounts id. are now represented as real UUID objects instead of plain strings. This change was introduced to leverage efficient storage and indexing for DBMS that supports UUID (especially PostgreSQL and Mongo). In Python code \u00b6 If you were doing comparison betwen a user id. and a string (in unit tests for example), you should now cast the id. to string: # Before assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == user . id # Now assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == str ( user . id ) If you were refering to user id. in your Pydantic models, the field should now be of UUID4 type instead of str : from pydantic import BaseModel , UUID4 # Before class Model ( BaseModel ): user_id : str # After class Model ( BaseModel ): user_id : UUID4 MongoDB \u00b6 To avoid any issues, it's recommended to use the standard UUID representation when instantiating the MongoDB client: DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) This parameter controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this . In database \u00b6 Id. were before stored as strings in the database. You should make a migration to convert string data to UUID data. Danger Scripts below are provided as guidelines. Please review them carefully , adapt them and check that they are working on a test database before applying them to production. BE CAREFUL. THEY CAN DESTROY YOUR DATA. . PostgreSQL \u00b6 PostgreSQL supports UUID type. If not already, you should enable the uuid-ossp extension: CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\" ; To convert the existing id. string column, we can: Create a new column with UUID type. Fill it with the id. converted to UUID. Drop the original id. column. Make the new column a primary key and rename it. ALTER TABLE \"user\" ADD uuid_id UUID ; UPDATE \"user\" SET uuid_id = uuid ( id ); ALTER TABLE \"user\" DROP id ; ALTER TABLE \"user\" ADD PRIMARY KEY ( uuid_id ); ALTER TABLE \"user\" RENAME COLUMN uuid_id TO id ; MySQL \u00b6 MySQL doesn't support UUID type. We'll just convert the column to CHAR(36) type: ALTER TABLE \"user\" MODIFY id CHAR ( 36 ); MongoDB \u00b6 Mongo shell \u00b6 For MongoDB, we can use a forEach iterator to convert the id. for each document: db . getCollection ( 'users' ). find (). forEach ( function ( user ) { var uuid = UUID ( user . id ); db . getCollection ( 'users' ). update ({ _id : user . _id }, [{ $set : { id : uuid }}]); }); Python \u00b6 import uuid import motor.motor_asyncio async def migrate_uuid (): client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] users = db [ \"users\" ] async for user in users . find ({}): await users . update_one ( { \"_id\" : user [ \"_id\" ]}, { \"$set\" : { \"id\" : uuid . UUID ( user [ \"id\" ])}}, ) Splitted routers \u00b6 You now have the responsibility to wire the routers . FastAPI Users doesn't give a bloated users router anymore. Event handlers are also removed. You have to provide your \"after-\" logic as a parameter of the router generator. Before \u00b6 jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . router , prefix = \"/users\" , tags = [ \"users\" ]) @fastapi_users . on_after_register () def on_after_register ( user : User , request : Request ): print ( f \"User { user . id } has registered.\" ) @fastapi_users . on_after_forgot_password () def on_after_forgot_password ( user : User , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) After \u00b6 def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Important things to notice: FastAPIUsers takes two arguments less ( reset_password_token_secret and reset_password_token_lifetime_seconds ). You have more flexibility to choose the prefix and tags of the routers. The /login / /logout are now your responsibility to include for each backend. The path will change (before /login/jwt , after /jwt/login ). If you don't care about some of those routers, you can discard them.","title":"0.8.x \u27a1\ufe0f 1.x.x"},{"location":"migration/08_to_1x/#08x-1xx","text":"1.0 version introduces major breaking changes that need you to update some of your code and migrate your data.","title":"0.8.x \u27a1\ufe0f 1.x.x"},{"location":"migration/08_to_1x/#id-are-uuid","text":"Users and OAuth accounts id. are now represented as real UUID objects instead of plain strings. This change was introduced to leverage efficient storage and indexing for DBMS that supports UUID (especially PostgreSQL and Mongo).","title":"Id. are UUID"},{"location":"migration/08_to_1x/#in-python-code","text":"If you were doing comparison betwen a user id. and a string (in unit tests for example), you should now cast the id. to string: # Before assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == user . id # Now assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == str ( user . id ) If you were refering to user id. in your Pydantic models, the field should now be of UUID4 type instead of str : from pydantic import BaseModel , UUID4 # Before class Model ( BaseModel ): user_id : str # After class Model ( BaseModel ): user_id : UUID4","title":"In Python code"},{"location":"migration/08_to_1x/#mongodb","text":"To avoid any issues, it's recommended to use the standard UUID representation when instantiating the MongoDB client: DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) This parameter controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this .","title":"MongoDB"},{"location":"migration/08_to_1x/#in-database","text":"Id. were before stored as strings in the database. You should make a migration to convert string data to UUID data. Danger Scripts below are provided as guidelines. Please review them carefully , adapt them and check that they are working on a test database before applying them to production. BE CAREFUL. THEY CAN DESTROY YOUR DATA. .","title":"In database"},{"location":"migration/08_to_1x/#postgresql","text":"PostgreSQL supports UUID type. If not already, you should enable the uuid-ossp extension: CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\" ; To convert the existing id. string column, we can: Create a new column with UUID type. Fill it with the id. converted to UUID. Drop the original id. column. Make the new column a primary key and rename it. ALTER TABLE \"user\" ADD uuid_id UUID ; UPDATE \"user\" SET uuid_id = uuid ( id ); ALTER TABLE \"user\" DROP id ; ALTER TABLE \"user\" ADD PRIMARY KEY ( uuid_id ); ALTER TABLE \"user\" RENAME COLUMN uuid_id TO id ;","title":"PostgreSQL"},{"location":"migration/08_to_1x/#mysql","text":"MySQL doesn't support UUID type. We'll just convert the column to CHAR(36) type: ALTER TABLE \"user\" MODIFY id CHAR ( 36 );","title":"MySQL"},{"location":"migration/08_to_1x/#mongodb_1","text":"","title":"MongoDB"},{"location":"migration/08_to_1x/#mongo-shell","text":"For MongoDB, we can use a forEach iterator to convert the id. for each document: db . getCollection ( 'users' ). find (). forEach ( function ( user ) { var uuid = UUID ( user . id ); db . getCollection ( 'users' ). update ({ _id : user . _id }, [{ $set : { id : uuid }}]); });","title":"Mongo shell"},{"location":"migration/08_to_1x/#python","text":"import uuid import motor.motor_asyncio async def migrate_uuid (): client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] users = db [ \"users\" ] async for user in users . find ({}): await users . update_one ( { \"_id\" : user [ \"_id\" ]}, { \"$set\" : { \"id\" : uuid . UUID ( user [ \"id\" ])}}, )","title":"Python"},{"location":"migration/08_to_1x/#splitted-routers","text":"You now have the responsibility to wire the routers . FastAPI Users doesn't give a bloated users router anymore. Event handlers are also removed. You have to provide your \"after-\" logic as a parameter of the router generator.","title":"Splitted routers"},{"location":"migration/08_to_1x/#before","text":"jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . router , prefix = \"/users\" , tags = [ \"users\" ]) @fastapi_users . on_after_register () def on_after_register ( user : User , request : Request ): print ( f \"User { user . id } has registered.\" ) @fastapi_users . on_after_forgot_password () def on_after_forgot_password ( user : User , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" )","title":"Before"},{"location":"migration/08_to_1x/#after","text":"def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Important things to notice: FastAPIUsers takes two arguments less ( reset_password_token_secret and reset_password_token_lifetime_seconds ). You have more flexibility to choose the prefix and tags of the routers. The /login / /logout are now your responsibility to include for each backend. The path will change (before /login/jwt , after /jwt/login ). If you don't care about some of those routers, you can discard them.","title":"After"},{"location":"migration/1x_to_2x/","text":"1.x.x \u27a1\ufe0f 2.x.x \u00b6 JWT authentication backend \u00b6 To be fully compatible with Swagger authentication, the output of a successful login operation with the JWT authentication backend has changed: Before { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } After { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Make sure to update your clients to read the token in the right property.","title":"1.x.x \u27a1\ufe0f 2.x.x"},{"location":"migration/1x_to_2x/#1xx-2xx","text":"","title":"1.x.x \u27a1\ufe0f 2.x.x"},{"location":"migration/1x_to_2x/#jwt-authentication-backend","text":"To be fully compatible with Swagger authentication, the output of a successful login operation with the JWT authentication backend has changed: Before { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } After { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Make sure to update your clients to read the token in the right property.","title":"JWT authentication backend"},{"location":"migration/2x_to_3x/","text":"2.x.x \u27a1\ufe0f 3.x.x \u00b6 Emails are now case-insensitive \u00b6 Before 3.x.x, the local part (before the @) of the email address was case-sensitive. Therefore, king.arthur@camelot.bt and King.Arthur@camelot.bt were considered as two different users . This behaviour was a bit confusing and not consistent with 99% of web services out there. After 3.x.x, users are fetched from the database with a case-insensitive email search. Bear in mind though that if the user registers with the email King.Arthur@camelot.bt , it will be stored exactly like this in the database (with casing) ; but he will be able to login as king.arthur@camelot.bt . Danger It's super important then, before you upgrade to 3.x.x that you check if there are several users with the same email with different cases ; and that you merge or delete those accounts .","title":"2.x.x \u27a1\ufe0f 3.x.x"},{"location":"migration/2x_to_3x/#2xx-3xx","text":"","title":"2.x.x \u27a1\ufe0f 3.x.x"},{"location":"migration/2x_to_3x/#emails-are-now-case-insensitive","text":"Before 3.x.x, the local part (before the @) of the email address was case-sensitive. Therefore, king.arthur@camelot.bt and King.Arthur@camelot.bt were considered as two different users . This behaviour was a bit confusing and not consistent with 99% of web services out there. After 3.x.x, users are fetched from the database with a case-insensitive email search. Bear in mind though that if the user registers with the email King.Arthur@camelot.bt , it will be stored exactly like this in the database (with casing) ; but he will be able to login as king.arthur@camelot.bt . Danger It's super important then, before you upgrade to 3.x.x that you check if there are several users with the same email with different cases ; and that you merge or delete those accounts .","title":"Emails are now case-insensitive"},{"location":"migration/3x_to_4x/","text":"3.x.x \u27a1\ufe0f 4.x.x \u00b6 expires_at property in OAuthAccount is now optional \u00b6 Before 4.x.x, the expires_at property in OAuthAccount model was mandatory. It was causing issues with some services that don't have such expiration property. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"3.x.x \u27a1\ufe0f 4.x.x"},{"location":"migration/3x_to_4x/#3xx-4xx","text":"","title":"3.x.x \u27a1\ufe0f 4.x.x"},{"location":"migration/3x_to_4x/#expires_at-property-in-oauthaccount-is-now-optional","text":"Before 4.x.x, the expires_at property in OAuthAccount model was mandatory. It was causing issues with some services that don't have such expiration property. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"expires_at property in OAuthAccount is now optional"},{"location":"migration/4x_to_5x/","text":"4.x.x \u27a1\ufe0f 5.x.x \u00b6 New property is_verified in User model. \u00b6 Starting 5.x.x., there is a new e-mail verification feature . Even if optional, the is_verified property has been added to the User model. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"4.x.x \u27a1\ufe0f 5.x.x"},{"location":"migration/4x_to_5x/#4xx-5xx","text":"","title":"4.x.x \u27a1\ufe0f 5.x.x"},{"location":"migration/4x_to_5x/#new-property-is_verified-in-user-model","text":"Starting 5.x.x., there is a new e-mail verification feature . Even if optional, the is_verified property has been added to the User model. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"New property is_verified in User model."},{"location":"usage/dependency-callables/","text":"Dependency callables \u00b6 FastAPI Users provides a dependency callable to easily inject authenticated user in your routes. They are available from your FastAPIUsers instance. Tip For more information about how to make an authenticated request to your API, check the documentation of your Authentication method . current_user \u00b6 Return a dependency callable to retrieve currently authenticated user, passing the following parameters: optional : If True , None is returned if there is no authenticated user or if it doesn't pass the other requirements. Otherwise, throw 401 Unauthorized . Defaults to False . active : If True , throw 401 Unauthorized if the authenticated user is inactive. Defaults to False . verified : If True , throw 401 Unauthorized if the authenticated user is not verified. Defaults to False . superuser : If True , throw 403 Forbidden if the authenticated user is not a superuser. Defaults to False . Examples \u00b6 Get the current user ( active or not ) \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ())): return f \"Hello, { user . email } \" Get the current active user \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True ))): return f \"Hello, { user . email } \" Get the current active and verified user \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , verified = True ))): return f \"Hello, { user . email } \" Get the current active superuser \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , superuser = True ))): return f \"Hello, { user . email } \" Reuse it \u00b6 If you use it often, you can of course set it in a variable and reuse it at will: current_active_user = fastapi_users . current_user ( active = True ) @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( current_active_user )): return f \"Hello, { user . email } \" Deprecated \u00b6 Warning Those ones are still provided for backward compatibility but are deprecated and will be removed in a future release. get_current_user \u00b6 Get the current user ( active or not ). Will throw a 401 Unauthorized if missing or wrong credentials. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_user )): return f \"Hello, { user . email } \" get_current_active_user \u00b6 Get the current active user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_active_user )): return f \"Hello, { user . email } \" get_current_verified_user \u00b6 Get the current active and verified user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_user )): return f \"Hello, { user . email } \" get_current_superuser \u00b6 Get the current superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_superuser )): return f \"Hello, { user . email } \" get_current_verified_superuser \u00b6 Get the current verified superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_superuser )): return f \"Hello, { user . email } \" get_optional_current_user \u00b6 Get the current user ( active or not ). Will return None if missing or wrong credentials. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : Optional [ User ] = Depends ( fastapi_users . get_optional_current_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_active_user \u00b6 Get the current active user. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_active_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_verified_user \u00b6 Get the current active and verified user. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_superuser \u00b6 Get the current superuser. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_verified_superuser \u00b6 Get the current active and verified superuser. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" In path operation \u00b6 If you don't need the user in the route logic, you can use this syntax: @app . get ( \"/protected-route\" , dependencies = [ Depends ( fastapi_users . get_current_superuser )]) def protected_route (): return \"Hello, some user.\" You can read more about this in FastAPI docs .","title":"Dependency callables"},{"location":"usage/dependency-callables/#dependency-callables","text":"FastAPI Users provides a dependency callable to easily inject authenticated user in your routes. They are available from your FastAPIUsers instance. Tip For more information about how to make an authenticated request to your API, check the documentation of your Authentication method .","title":"Dependency callables"},{"location":"usage/dependency-callables/#current_user","text":"Return a dependency callable to retrieve currently authenticated user, passing the following parameters: optional : If True , None is returned if there is no authenticated user or if it doesn't pass the other requirements. Otherwise, throw 401 Unauthorized . Defaults to False . active : If True , throw 401 Unauthorized if the authenticated user is inactive. Defaults to False . verified : If True , throw 401 Unauthorized if the authenticated user is not verified. Defaults to False . superuser : If True , throw 403 Forbidden if the authenticated user is not a superuser. Defaults to False .","title":"current_user"},{"location":"usage/dependency-callables/#examples","text":"","title":"Examples"},{"location":"usage/dependency-callables/#get-the-current-user-active-or-not","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ())): return f \"Hello, { user . email } \"","title":"Get the current user (active or not )"},{"location":"usage/dependency-callables/#get-the-current-active-user","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True ))): return f \"Hello, { user . email } \"","title":"Get the current active user"},{"location":"usage/dependency-callables/#get-the-current-active-and-verified-user","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , verified = True ))): return f \"Hello, { user . email } \"","title":"Get the current active and verified user"},{"location":"usage/dependency-callables/#get-the-current-active-superuser","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , superuser = True ))): return f \"Hello, { user . email } \"","title":"Get the current active superuser"},{"location":"usage/dependency-callables/#reuse-it","text":"If you use it often, you can of course set it in a variable and reuse it at will: current_active_user = fastapi_users . current_user ( active = True ) @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( current_active_user )): return f \"Hello, { user . email } \"","title":"Reuse it"},{"location":"usage/dependency-callables/#deprecated","text":"Warning Those ones are still provided for backward compatibility but are deprecated and will be removed in a future release.","title":"Deprecated"},{"location":"usage/dependency-callables/#get_current_user","text":"Get the current user ( active or not ). Will throw a 401 Unauthorized if missing or wrong credentials. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_user )): return f \"Hello, { user . email } \"","title":"get_current_user"},{"location":"usage/dependency-callables/#get_current_active_user","text":"Get the current active user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_active_user )): return f \"Hello, { user . email } \"","title":"get_current_active_user"},{"location":"usage/dependency-callables/#get_current_verified_user","text":"Get the current active and verified user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_user )): return f \"Hello, { user . email } \"","title":"get_current_verified_user"},{"location":"usage/dependency-callables/#get_current_superuser","text":"Get the current superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_superuser )): return f \"Hello, { user . email } \"","title":"get_current_superuser"},{"location":"usage/dependency-callables/#get_current_verified_superuser","text":"Get the current verified superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_superuser )): return f \"Hello, { user . email } \"","title":"get_current_verified_superuser"},{"location":"usage/dependency-callables/#get_optional_current_user","text":"Get the current user ( active or not ). Will return None if missing or wrong credentials. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : Optional [ User ] = Depends ( fastapi_users . get_optional_current_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_user"},{"location":"usage/dependency-callables/#get_optional_current_active_user","text":"Get the current active user. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_active_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_active_user"},{"location":"usage/dependency-callables/#get_optional_current_verified_user","text":"Get the current active and verified user. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_verified_user"},{"location":"usage/dependency-callables/#get_optional_current_superuser","text":"Get the current superuser. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_superuser"},{"location":"usage/dependency-callables/#get_optional_current_verified_superuser","text":"Get the current active and verified superuser. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_verified_superuser"},{"location":"usage/dependency-callables/#in-path-operation","text":"If you don't need the user in the route logic, you can use this syntax: @app . get ( \"/protected-route\" , dependencies = [ Depends ( fastapi_users . get_current_superuser )]) def protected_route (): return \"Hello, some user.\" You can read more about this in FastAPI docs .","title":"In path operation"},{"location":"usage/flow/","text":"Flow \u00b6 This page will present you a complete registration and authentication flow once you've setup FastAPI Users . Each example will be presented with a cURL and an axios example. 1. Registration \u00b6 First step, of course, is to register as a user. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -X POST \\ -d \"{\\\"email\\\": \\\"king.arthur@camelot.bt\\\",\\\"password\\\": \\\"guinevere\\\"}\" \\ http://localhost:8000/auth/register axios axios . post ( 'http://localhost:8000/auth/register' , { email : 'king.arthur@camelot.bt' , password : 'guinevere' , }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Several things to bear in mind: If you have defined other required fields in your User model (like a first name or a birthdate), you'll have to provide them in the payload. The user is active by default. The user cannot set is_active or is_superuser itself at registration. Only a superuser can do it by PATCHing the user. 2. Login \u00b6 Now, you can login as this new user. You can generate a login route for each authentication backend . Each backend will have a different response. JWT backend \u00b6 Request \u00b6 cURL curl \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/jwt/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/jwt/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username . Response \u00b6 You'll get a JSON response looking like this: { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" , \"token_type\" : \"bearer\" } You can use this token to make authenticated requests as the user king.arthur@camelot.bt . We'll see how in the next section. Cookie backend \u00b6 Request \u00b6 cURL curl \\ -v \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/cookie/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/cookie/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username . Response \u00b6 You'll get an empty response. However, the response will come with a Set-Cookie header (that's why we added the -v option in cURL to see them). set-cookie: fastapiusersauth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYzYwNjBmMTEtNTM0OS00YTI0LThiNGEtYTJhODc1ZGM1Mzk1IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4OTQ3fQ.qNA4oPVYhoqrJIk-zvAyEfEVoEnP156G30H_SWEU0sU; HttpOnly; Max-Age=3600; Path=/; Secure You can make authenticated requests as the user king.arthur@camelot.bt by setting a Cookie header with this cookie. Tip The cookie backend is more suited for browsers, as they handle them automatically. This means that if you make a login request in the browser, it will automatically store the cookie and automatically send it in subsequent requests. 3. Get my profile \u00b6 Now that we can authenticate, we can get our own profile data. Depending on your authentication backend , the method to authenticate the request will vary. We'll stick with JWT from now on. Request \u00b6 cURL export TOKEN = \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" ; curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/me axios const TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM' ; axios . get ( 'http://localhost:8000/users/me' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Tip If you use one of the dependency callable to protect one of your own endpoint, you'll have to authenticate exactly in the same way. 4. Update my profile \u00b6 We can also update our own profile. For example, we can change our password like this. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"password\\\": \\\"lancelot\\\"}\" \\ http://localhost:8000/users/me axios axios . patch ( 'http://localhost:8000/users/me' , { password : 'lancelot' , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Once again, the user cannot set is_active or is_superuser itself. Only a superuser can do it by PATCHing the user. 5. Become a superuser \ud83e\uddb8\ud83c\udffb\u200d\u2642\ufe0f \u00b6 If you want to manage the users of your application, you'll have to become a superuser . The very first superuser can only be set at database level : open it through a CLI or a GUI, find your user and set the is_superuser column/property to true . 5.1. Get the profile of any user \u00b6 Now that you are a superuser, you can leverage the power of superuser routes . You can for example get the profile of any user in the database given its id. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . get ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 5.1. Update any user \u00b6 We can now update the profile of any user. For example, we can promote it as superuser. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"is_superuser\\\": true}\" \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . patch ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { is_superuser : true , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : true } 5.2. Delete any user \u00b6 Finally, we can delete a user. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X DELETE \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . delete ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get an empty response. 6. Logout \u00b6 We can also end the session. Note that it doesn't apply to every authentication backends . For JWT, it doesn't make sense to end the session, the token is valid until it expires. However, for Cookie backend, the server will clear the cookie. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Cookie: fastapiusersauth= $TOKEN \" \\ -X POST \\ http://localhost:8000/auth/cookie/logout axios axios . post ( 'http://localhost:8000/auth/cookie/logout' , null , { headers : { 'Cookie' : `fastapiusersauth= ${ TOKEN } ` , }, } ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get an empty response. Conclusion \u00b6 That's it! You now have a good overview of how you can manage the users through the API. Be sure to check the Routes page to have all the details about each endpoints.","title":"Flow"},{"location":"usage/flow/#flow","text":"This page will present you a complete registration and authentication flow once you've setup FastAPI Users . Each example will be presented with a cURL and an axios example.","title":"Flow"},{"location":"usage/flow/#1-registration","text":"First step, of course, is to register as a user.","title":"1. Registration"},{"location":"usage/flow/#request","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -X POST \\ -d \"{\\\"email\\\": \\\"king.arthur@camelot.bt\\\",\\\"password\\\": \\\"guinevere\\\"}\" \\ http://localhost:8000/auth/register axios axios . post ( 'http://localhost:8000/auth/register' , { email : 'king.arthur@camelot.bt' , password : 'guinevere' , }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Several things to bear in mind: If you have defined other required fields in your User model (like a first name or a birthdate), you'll have to provide them in the payload. The user is active by default. The user cannot set is_active or is_superuser itself at registration. Only a superuser can do it by PATCHing the user.","title":"Response"},{"location":"usage/flow/#2-login","text":"Now, you can login as this new user. You can generate a login route for each authentication backend . Each backend will have a different response.","title":"2. Login"},{"location":"usage/flow/#jwt-backend","text":"","title":"JWT backend"},{"location":"usage/flow/#request_1","text":"cURL curl \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/jwt/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/jwt/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username .","title":"Request"},{"location":"usage/flow/#response_1","text":"You'll get a JSON response looking like this: { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" , \"token_type\" : \"bearer\" } You can use this token to make authenticated requests as the user king.arthur@camelot.bt . We'll see how in the next section.","title":"Response"},{"location":"usage/flow/#cookie-backend","text":"","title":"Cookie backend"},{"location":"usage/flow/#request_2","text":"cURL curl \\ -v \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/cookie/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/cookie/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username .","title":"Request"},{"location":"usage/flow/#response_2","text":"You'll get an empty response. However, the response will come with a Set-Cookie header (that's why we added the -v option in cURL to see them). set-cookie: fastapiusersauth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYzYwNjBmMTEtNTM0OS00YTI0LThiNGEtYTJhODc1ZGM1Mzk1IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4OTQ3fQ.qNA4oPVYhoqrJIk-zvAyEfEVoEnP156G30H_SWEU0sU; HttpOnly; Max-Age=3600; Path=/; Secure You can make authenticated requests as the user king.arthur@camelot.bt by setting a Cookie header with this cookie. Tip The cookie backend is more suited for browsers, as they handle them automatically. This means that if you make a login request in the browser, it will automatically store the cookie and automatically send it in subsequent requests.","title":"Response"},{"location":"usage/flow/#3-get-my-profile","text":"Now that we can authenticate, we can get our own profile data. Depending on your authentication backend , the method to authenticate the request will vary. We'll stick with JWT from now on.","title":"3. Get my profile"},{"location":"usage/flow/#request_3","text":"cURL export TOKEN = \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" ; curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/me axios const TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM' ; axios . get ( 'http://localhost:8000/users/me' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_3","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Tip If you use one of the dependency callable to protect one of your own endpoint, you'll have to authenticate exactly in the same way.","title":"Response"},{"location":"usage/flow/#4-update-my-profile","text":"We can also update our own profile. For example, we can change our password like this.","title":"4. Update my profile"},{"location":"usage/flow/#request_4","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"password\\\": \\\"lancelot\\\"}\" \\ http://localhost:8000/users/me axios axios . patch ( 'http://localhost:8000/users/me' , { password : 'lancelot' , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_4","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Once again, the user cannot set is_active or is_superuser itself. Only a superuser can do it by PATCHing the user.","title":"Response"},{"location":"usage/flow/#5-become-a-superuser","text":"If you want to manage the users of your application, you'll have to become a superuser . The very first superuser can only be set at database level : open it through a CLI or a GUI, find your user and set the is_superuser column/property to true .","title":"5. Become a superuser \ud83e\uddb8\ud83c\udffb\u200d\u2642\ufe0f"},{"location":"usage/flow/#51-get-the-profile-of-any-user","text":"Now that you are a superuser, you can leverage the power of superuser routes . You can for example get the profile of any user in the database given its id.","title":"5.1. Get the profile of any user"},{"location":"usage/flow/#request_5","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . get ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_5","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false }","title":"Response"},{"location":"usage/flow/#51-update-any-user","text":"We can now update the profile of any user. For example, we can promote it as superuser.","title":"5.1. Update any user"},{"location":"usage/flow/#request_6","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"is_superuser\\\": true}\" \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . patch ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { is_superuser : true , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_6","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : true }","title":"Response"},{"location":"usage/flow/#52-delete-any-user","text":"Finally, we can delete a user.","title":"5.2. Delete any user"},{"location":"usage/flow/#request_7","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X DELETE \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . delete ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_7","text":"You'll get an empty response.","title":"Response"},{"location":"usage/flow/#6-logout","text":"We can also end the session. Note that it doesn't apply to every authentication backends . For JWT, it doesn't make sense to end the session, the token is valid until it expires. However, for Cookie backend, the server will clear the cookie.","title":"6. Logout"},{"location":"usage/flow/#request_8","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Cookie: fastapiusersauth= $TOKEN \" \\ -X POST \\ http://localhost:8000/auth/cookie/logout axios axios . post ( 'http://localhost:8000/auth/cookie/logout' , null , { headers : { 'Cookie' : `fastapiusersauth= ${ TOKEN } ` , }, } ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_8","text":"You'll get an empty response.","title":"Response"},{"location":"usage/flow/#conclusion","text":"That's it! You now have a good overview of how you can manage the users through the API. Be sure to check the Routes page to have all the details about each endpoints.","title":"Conclusion"},{"location":"usage/helpers/","text":"Helpers \u00b6 FastAPI Users provides some helper functions to perform some actions programmatically. They are available from your FastAPIUsers instance. Create user \u00b6 Create a user. regular_user = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , ) ) superuser = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , is_superuser = True , ) ) Verify user \u00b6 Verify a user. verified_user = await fastapi_users . verify_user ( non_verified_user ) assert verified_user . is_verified is True Get user \u00b6 Retrieve a user by e-mail. user = await fastapi_users . get_user ( \"king.arthur@camelot.bt\" )","title":"Helpers"},{"location":"usage/helpers/#helpers","text":"FastAPI Users provides some helper functions to perform some actions programmatically. They are available from your FastAPIUsers instance.","title":"Helpers"},{"location":"usage/helpers/#create-user","text":"Create a user. regular_user = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , ) ) superuser = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , is_superuser = True , ) )","title":"Create user"},{"location":"usage/helpers/#verify-user","text":"Verify a user. verified_user = await fastapi_users . verify_user ( non_verified_user ) assert verified_user . is_verified is True","title":"Verify user"},{"location":"usage/helpers/#get-user","text":"Retrieve a user by e-mail. user = await fastapi_users . get_user ( \"king.arthur@camelot.bt\" )","title":"Get user"},{"location":"usage/routes/","text":"Routes \u00b6 You'll find here the routes exposed by FastAPI Users . Note that you can also review them through the interactive API docs . Auth router \u00b6 Each authentication backend you generate a router for will produce the following routes. Take care about the prefix you gave it, especially if you have several backends. POST /login \u00b6 Login a user against the method named name . Check the corresponding authentication method to view the success response. Payload ( application/x-www-form-urlencoded ) username=king.arthur@camelot.bt&password=guinevere 422 Validation Error 400 Bad Request Bad credentials or the user is inactive. { \"detail\" : \"LOGIN_BAD_CREDENTIALS\" } 400 Bad Request The user is not verified. { \"detail\" : \"LOGIN_USER_NOT_VERIFIED\" } POST /logout \u00b6 Logout the authenticated user against the method named name . Check the corresponding authentication method to view the success response. 401 Unauthorized Missing token or inactive user. 200 OK The logout process was successful. Tip Some backend (like JWT) won't produce this route. Register router \u00b6 POST /register \u00b6 Register a new user. Will call the after_register handler on successful registration. Payload { \"email\" : \"king.arthur@camelot.bt\" , \"password\" : \"guinevere\" } 201 Created { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 422 Validation Error 400 Bad Request A user already exists with this email. { \"detail\" : \"REGISTER_USER_ALREADY_EXISTS\" } Reset password router \u00b6 POST /forgot-password \u00b6 Request a reset password procedure. Will generate a temporary token and call the after_forgot_password handler if the user exists. To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted POST /reset-password \u00b6 Reset a password. Requires the token generated by the /forgot-password route. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"password\" : \"merlin\" } 200 OK 422 Validation Error 400 Bad Request Bad or expired token. { \"detail\" : \"RESET_PASSWORD_BAD_TOKEN\" } Verify router \u00b6 POST /request-verify-token \u00b6 Request a user to verify their e-mail. Will generate a temporary token and call the after_verification_request handler if the user exists , active and not already verified . To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist, not active or already verified. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted POST /verify \u00b6 Verify a user. Requires the token generated by the /request-verify-token route. Will call the call the after_verification handler on success. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } 200 OK 422 Validation Error 400 Bad Request Expired token. { \"detail\" : \"VERIFY_USER_TOKEN_EXPIRED\" } 400 Bad Request Bad token, not existing user or not the e-mail currently set for the user. { \"detail\" : \"VERIFY_USER_BAD_TOKEN\" } 400 Bad Request The user is already verified. { \"detail\" : \"VERIFY_USER_ALREADY_VERIFIED\" } OAuth router \u00b6 Each OAuth router you define will expose the two following routes. GET /authorize \u00b6 Return the authorization URL for the OAuth service where you should redirect your user. Query parameters authentication_backend : name property of a defined authentication method to use to authenticate the user on successful callback. Usually jwt or cookie . scopes : Optional list of scopes to ask for. Expected format: scopes=a&scopes=b . 200 OK { \"authorization_url\" : \"https://www.tintagel.bt/oauth/authorize?client_id=CLIENT_ID&scopes=a+b&redirect_uri=https://www.camelot.bt/oauth/callback\" } 422 Validation Error 400 Bad Request Unknown authentication backend. GET /callback \u00b6 Handle the OAuth callback. Query parameters code : OAuth callback code. state : State token. error : OAuth error. Depending on the situation, several things can happen: The OAuth account exists in database and is linked to a user: OAuth account is updated in database with fresh access token. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database but a user with the same email address exists: OAuth account is linked to the user. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database and no user with the email address exists: A new user is created and linked to the OAuth account. The user is authenticated following the chosen authentication method . Users router \u00b6 GET /me \u00b6 Return the current authenticated active user. 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. PATCH /me \u00b6 Update the current authenticated active user. Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@tintagel.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. GET /{user_id} \u00b6 Return the user with id user_id . 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist. PATCH /{user_id} \u00b6 Update the user with id user_id . Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" , \"is_active\" : false , \"is_superuser\" : true } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : false , \"is_superuser\" : true } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist. DELETE /{user_id} \u00b6 Delete the user with id user_id . 204 No content 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"Routes"},{"location":"usage/routes/#routes","text":"You'll find here the routes exposed by FastAPI Users . Note that you can also review them through the interactive API docs .","title":"Routes"},{"location":"usage/routes/#auth-router","text":"Each authentication backend you generate a router for will produce the following routes. Take care about the prefix you gave it, especially if you have several backends.","title":"Auth router"},{"location":"usage/routes/#post-login","text":"Login a user against the method named name . Check the corresponding authentication method to view the success response. Payload ( application/x-www-form-urlencoded ) username=king.arthur@camelot.bt&password=guinevere 422 Validation Error 400 Bad Request Bad credentials or the user is inactive. { \"detail\" : \"LOGIN_BAD_CREDENTIALS\" } 400 Bad Request The user is not verified. { \"detail\" : \"LOGIN_USER_NOT_VERIFIED\" }","title":"POST /login"},{"location":"usage/routes/#post-logout","text":"Logout the authenticated user against the method named name . Check the corresponding authentication method to view the success response. 401 Unauthorized Missing token or inactive user. 200 OK The logout process was successful. Tip Some backend (like JWT) won't produce this route.","title":"POST /logout"},{"location":"usage/routes/#register-router","text":"","title":"Register router"},{"location":"usage/routes/#post-register","text":"Register a new user. Will call the after_register handler on successful registration. Payload { \"email\" : \"king.arthur@camelot.bt\" , \"password\" : \"guinevere\" } 201 Created { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 422 Validation Error 400 Bad Request A user already exists with this email. { \"detail\" : \"REGISTER_USER_ALREADY_EXISTS\" }","title":"POST /register"},{"location":"usage/routes/#reset-password-router","text":"","title":"Reset password router"},{"location":"usage/routes/#post-forgot-password","text":"Request a reset password procedure. Will generate a temporary token and call the after_forgot_password handler if the user exists. To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted","title":"POST /forgot-password"},{"location":"usage/routes/#post-reset-password","text":"Reset a password. Requires the token generated by the /forgot-password route. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"password\" : \"merlin\" } 200 OK 422 Validation Error 400 Bad Request Bad or expired token. { \"detail\" : \"RESET_PASSWORD_BAD_TOKEN\" }","title":"POST /reset-password"},{"location":"usage/routes/#verify-router","text":"","title":"Verify router"},{"location":"usage/routes/#post-request-verify-token","text":"Request a user to verify their e-mail. Will generate a temporary token and call the after_verification_request handler if the user exists , active and not already verified . To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist, not active or already verified. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted","title":"POST /request-verify-token"},{"location":"usage/routes/#post-verify","text":"Verify a user. Requires the token generated by the /request-verify-token route. Will call the call the after_verification handler on success. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } 200 OK 422 Validation Error 400 Bad Request Expired token. { \"detail\" : \"VERIFY_USER_TOKEN_EXPIRED\" } 400 Bad Request Bad token, not existing user or not the e-mail currently set for the user. { \"detail\" : \"VERIFY_USER_BAD_TOKEN\" } 400 Bad Request The user is already verified. { \"detail\" : \"VERIFY_USER_ALREADY_VERIFIED\" }","title":"POST /verify"},{"location":"usage/routes/#oauth-router","text":"Each OAuth router you define will expose the two following routes.","title":"OAuth router"},{"location":"usage/routes/#get-authorize","text":"Return the authorization URL for the OAuth service where you should redirect your user. Query parameters authentication_backend : name property of a defined authentication method to use to authenticate the user on successful callback. Usually jwt or cookie . scopes : Optional list of scopes to ask for. Expected format: scopes=a&scopes=b . 200 OK { \"authorization_url\" : \"https://www.tintagel.bt/oauth/authorize?client_id=CLIENT_ID&scopes=a+b&redirect_uri=https://www.camelot.bt/oauth/callback\" } 422 Validation Error 400 Bad Request Unknown authentication backend.","title":"GET /authorize"},{"location":"usage/routes/#get-callback","text":"Handle the OAuth callback. Query parameters code : OAuth callback code. state : State token. error : OAuth error. Depending on the situation, several things can happen: The OAuth account exists in database and is linked to a user: OAuth account is updated in database with fresh access token. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database but a user with the same email address exists: OAuth account is linked to the user. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database and no user with the email address exists: A new user is created and linked to the OAuth account. The user is authenticated following the chosen authentication method .","title":"GET /callback"},{"location":"usage/routes/#users-router","text":"","title":"Users router"},{"location":"usage/routes/#get-me","text":"Return the current authenticated active user. 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user.","title":"GET /me"},{"location":"usage/routes/#patch-me","text":"Update the current authenticated active user. Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@tintagel.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user.","title":"PATCH /me"},{"location":"usage/routes/#get-user_id","text":"Return the user with id user_id . 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"GET /{user_id}"},{"location":"usage/routes/#patch-user_id","text":"Update the user with id user_id . Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" , \"is_active\" : false , \"is_superuser\" : true } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : false , \"is_superuser\" : true } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"PATCH /{user_id}"},{"location":"usage/routes/#delete-user_id","text":"Delete the user with id user_id . 204 No content 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"DELETE /{user_id}"}]} \ No newline at end of file +{"config":{"lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"FastAPI Users \u00b6 Ready-to-use and customizable users management for FastAPI Documentation : https://frankie567.github.io/fastapi-users/ Source Code : https://github.com/frankie567/fastapi-users Add quickly a registration and authentication system to your FastAPI project. FastAPI Users is designed to be as customizable and adaptable as possible. Features \u00b6 Extensible base user model Ready-to-use register, login, reset password and verify e-mail routes Ready-to-use OAuth2 flow Dependency callables to inject current user in route Customizable database backend SQLAlchemy async backend included thanks to encode/databases MongoDB async backend included thanks to mongodb/motor Tortoise ORM backend included ormar backend included Multiple customizable authentication backends JWT authentication backend included Cookie authentication backend included Full OpenAPI schema support, even with several authentication backends Contributors and sponsors \u2728\u2615\ufe0f \u00b6 Thanks goes to these wonderful people ( emoji key ): Fran\u00e7ois Voron \ud83d\udea7 Paolo Dina \ud83d\udcb5 \ud83d\udcbb Dmytro Ohorodnik \ud83d\udc1b Matthew D. Scholefield \ud83d\udc1b roywes \ud83d\udc1b \ud83d\udcbb Satwik Kansal \ud83d\udcd6 Edd Salkield \ud83d\udcbb \ud83d\udcd6 mark-todd \ud83d\udcbb \ud83d\udcd6 lill74 \ud83d\udc1b \ud83d\udcbb SelfhostedPro \ud83d\udee1\ufe0f \ud83d\udcbb Oskar Gmerek \ud83d\udcd6 Martin Collado \ud83d\udc1b \ud83d\udcbb Eric Lopes \ud83d\udcd6 \ud83d\udee1\ufe0f Beau Breon \ud83d\udcbb Niyas Mohammed \ud83d\udcd6 prostomarkeloff \ud83d\udcd6 \ud83d\udcbb Marius M\u00e9zerette \ud83d\udc1b \ud83e\udd14 Nickolas Grigoriadis \ud83d\udc1b Open Data Coder \ud83e\udd14 Mohammed Alshehri \ud83e\udd14 Tyler Renelle \ud83e\udd14 collerek \ud83d\udcbb Robert Bracco \ud83d\udcb5 Augusto Herrmann \ud83d\udcd6 Smithybrewer \ud83d\udc1b silllli \ud83d\udcd6 alexferrari88 \ud83d\udcb5 sandalwoodbox \ud83d\udc1b Vlad Hoi \ud83d\udcd6 This project follows the all-contributors specification. Contributions of any kind welcome! Development \u00b6 Setup environment \u00b6 You should create a virtual environment and activate it: python -m venv venv/ source venv/bin/activate And then install the development dependencies: pip install -r requirements.dev.txt Run unit tests \u00b6 You can run all the tests with: make test The command will start a MongoDB container for the related unit tests. So you should have Docker installed. Alternatively, you can run pytest yourself. The MongoDB unit tests will be skipped if no server is available on your local machine: pytest There are quite a few unit tests, so you might run into ulimit issues where there are too many open file descriptors. You may be able to set a new, higher limit temporarily with: ulimit -n 2048 Format the code \u00b6 Execute the following command to apply isort and black formatting: make format License \u00b6 This project is licensed under the terms of the MIT license.","title":"About"},{"location":"#fastapi-users","text":"Ready-to-use and customizable users management for FastAPI Documentation : https://frankie567.github.io/fastapi-users/ Source Code : https://github.com/frankie567/fastapi-users Add quickly a registration and authentication system to your FastAPI project. FastAPI Users is designed to be as customizable and adaptable as possible.","title":"FastAPI Users"},{"location":"#features","text":"Extensible base user model Ready-to-use register, login, reset password and verify e-mail routes Ready-to-use OAuth2 flow Dependency callables to inject current user in route Customizable database backend SQLAlchemy async backend included thanks to encode/databases MongoDB async backend included thanks to mongodb/motor Tortoise ORM backend included ormar backend included Multiple customizable authentication backends JWT authentication backend included Cookie authentication backend included Full OpenAPI schema support, even with several authentication backends","title":"Features"},{"location":"#contributors-and-sponsors","text":"Thanks goes to these wonderful people ( emoji key ): Fran\u00e7ois Voron \ud83d\udea7 Paolo Dina \ud83d\udcb5 \ud83d\udcbb Dmytro Ohorodnik \ud83d\udc1b Matthew D. Scholefield \ud83d\udc1b roywes \ud83d\udc1b \ud83d\udcbb Satwik Kansal \ud83d\udcd6 Edd Salkield \ud83d\udcbb \ud83d\udcd6 mark-todd \ud83d\udcbb \ud83d\udcd6 lill74 \ud83d\udc1b \ud83d\udcbb SelfhostedPro \ud83d\udee1\ufe0f \ud83d\udcbb Oskar Gmerek \ud83d\udcd6 Martin Collado \ud83d\udc1b \ud83d\udcbb Eric Lopes \ud83d\udcd6 \ud83d\udee1\ufe0f Beau Breon \ud83d\udcbb Niyas Mohammed \ud83d\udcd6 prostomarkeloff \ud83d\udcd6 \ud83d\udcbb Marius M\u00e9zerette \ud83d\udc1b \ud83e\udd14 Nickolas Grigoriadis \ud83d\udc1b Open Data Coder \ud83e\udd14 Mohammed Alshehri \ud83e\udd14 Tyler Renelle \ud83e\udd14 collerek \ud83d\udcbb Robert Bracco \ud83d\udcb5 Augusto Herrmann \ud83d\udcd6 Smithybrewer \ud83d\udc1b silllli \ud83d\udcd6 alexferrari88 \ud83d\udcb5 sandalwoodbox \ud83d\udc1b Vlad Hoi \ud83d\udcd6 This project follows the all-contributors specification. Contributions of any kind welcome!","title":"Contributors and sponsors \u2728\u2615\ufe0f"},{"location":"#development","text":"","title":"Development"},{"location":"#setup-environment","text":"You should create a virtual environment and activate it: python -m venv venv/ source venv/bin/activate And then install the development dependencies: pip install -r requirements.dev.txt","title":"Setup environment"},{"location":"#run-unit-tests","text":"You can run all the tests with: make test The command will start a MongoDB container for the related unit tests. So you should have Docker installed. Alternatively, you can run pytest yourself. The MongoDB unit tests will be skipped if no server is available on your local machine: pytest There are quite a few unit tests, so you might run into ulimit issues where there are too many open file descriptors. You may be able to set a new, higher limit temporarily with: ulimit -n 2048","title":"Run unit tests"},{"location":"#format-the-code","text":"Execute the following command to apply isort and black formatting: make format","title":"Format the code"},{"location":"#license","text":"This project is licensed under the terms of the MIT license.","title":"License"},{"location":"installation/","text":"Installation \u00b6 You can add FastAPI Users to your FastAPI project in a few easy steps. First of all, install the dependency: With SQLAlchemy support \u00b6 pip install 'fastapi-users[sqlalchemy]' With MongoDB support \u00b6 pip install 'fastapi-users[mongodb]' With Tortoise ORM support \u00b6 pip install 'fastapi-users[tortoise-orm]' With ormar support \u00b6 pip install 'fastapi-users[ormar]' That's it! Now, let's have a look at our User model .","title":"Installation"},{"location":"installation/#installation","text":"You can add FastAPI Users to your FastAPI project in a few easy steps. First of all, install the dependency:","title":"Installation"},{"location":"installation/#with-sqlalchemy-support","text":"pip install 'fastapi-users[sqlalchemy]'","title":"With SQLAlchemy support"},{"location":"installation/#with-mongodb-support","text":"pip install 'fastapi-users[mongodb]'","title":"With MongoDB support"},{"location":"installation/#with-tortoise-orm-support","text":"pip install 'fastapi-users[tortoise-orm]'","title":"With Tortoise ORM support"},{"location":"installation/#with-ormar-support","text":"pip install 'fastapi-users[ormar]' That's it! Now, let's have a look at our User model .","title":"With ormar support"},{"location":"configuration/full_example/","text":"Full example \u00b6 Here is a full working example with JWT authentication to help get you started. Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database. SQLAlchemy \u00b6 import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () MongoDB \u00b6 import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Tortoise ORM \u00b6 from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Ormar \u00b6 import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase DATABASE_URL = \"sqlite:///test.db\" SECRET = \"SECRET\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () What now? \u00b6 You're ready to go! Be sure to check the Usage section to understand how yo work with FastAPI Users .","title":"Full example"},{"location":"configuration/full_example/#full-example","text":"Here is a full working example with JWT authentication to help get you started. Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database.","title":"Full example"},{"location":"configuration/full_example/#sqlalchemy","text":"import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect ()","title":"SQLAlchemy"},{"location":"configuration/full_example/#mongodb","text":"import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ])","title":"MongoDB"},{"location":"configuration/full_example/#tortoise-orm","text":"from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ])","title":"Tortoise ORM"},{"location":"configuration/full_example/#ormar","text":"import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase DATABASE_URL = \"sqlite:///test.db\" SECRET = \"SECRET\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect ()","title":"Ormar"},{"location":"configuration/full_example/#what-now","text":"You're ready to go! Be sure to check the Usage section to understand how yo work with FastAPI Users .","title":"What now?"},{"location":"configuration/model/","text":"User model \u00b6 FastAPI Users defines a minimal User model for authentication purposes. It is structured like this: id ( UUID4 ) \u2013 Unique identifier of the user. Defaults to a UUID4 . email ( str ) \u2013 Email of the user. Validated by email-validator . is_active ( bool ) \u2013 Whether or not the user is active. If not, login and forgot password requests will be denied. Defaults to True . is_verified ( bool ) \u2013 Whether or not the user is verified. Optional but helpful with the verify router logic. Defaults to False . is_superuser ( bool ) \u2013 Whether or not the user is a superuser. Useful to implement administration logic. Defaults to False . Define your models \u00b6 There are four Pydantic models variations provided as mixins: BaseUser , which provides the basic fields and validation ; BaseCreateUser , dedicated to user registration, which consists of compulsory email and password fields ; BaseUpdateUser , dedicated to user profile update, which adds an optional password field ; BaseUserDB , which is a representation of the user in database, adding a hashed_password field. You should define each of those variations, inheriting from each mixin: from fastapi_users import models class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass You can of course add your own properties there to fit to your needs! Password validation \u00b6 FastAPI Users doesn't provide a default password validation, but you can implement it easily with a Pydantic validator on the UserCreate class. Here is a simple example to check if the password is at least six characters long: from fastapi_users import models from pydantic import validator class UserCreate ( models . BaseUserCreate ): @validator ( 'password' ) def valid_password ( cls , v : str ): if len ( v ) < 6 : raise ValueError ( 'Password should be at least 6 characters' ) return v Next steps \u00b6 Depending on your database backend, the database configuration will differ a bit. I'm using SQLAlchemy I'm using MongoDB I'm using Tortoise ORM I'm using ormar","title":"User model"},{"location":"configuration/model/#user-model","text":"FastAPI Users defines a minimal User model for authentication purposes. It is structured like this: id ( UUID4 ) \u2013 Unique identifier of the user. Defaults to a UUID4 . email ( str ) \u2013 Email of the user. Validated by email-validator . is_active ( bool ) \u2013 Whether or not the user is active. If not, login and forgot password requests will be denied. Defaults to True . is_verified ( bool ) \u2013 Whether or not the user is verified. Optional but helpful with the verify router logic. Defaults to False . is_superuser ( bool ) \u2013 Whether or not the user is a superuser. Useful to implement administration logic. Defaults to False .","title":"User model"},{"location":"configuration/model/#define-your-models","text":"There are four Pydantic models variations provided as mixins: BaseUser , which provides the basic fields and validation ; BaseCreateUser , dedicated to user registration, which consists of compulsory email and password fields ; BaseUpdateUser , dedicated to user profile update, which adds an optional password field ; BaseUserDB , which is a representation of the user in database, adding a hashed_password field. You should define each of those variations, inheriting from each mixin: from fastapi_users import models class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass You can of course add your own properties there to fit to your needs!","title":"Define your models"},{"location":"configuration/model/#password-validation","text":"FastAPI Users doesn't provide a default password validation, but you can implement it easily with a Pydantic validator on the UserCreate class. Here is a simple example to check if the password is at least six characters long: from fastapi_users import models from pydantic import validator class UserCreate ( models . BaseUserCreate ): @validator ( 'password' ) def valid_password ( cls , v : str ): if len ( v ) < 6 : raise ValueError ( 'Password should be at least 6 characters' ) return v","title":"Password validation"},{"location":"configuration/model/#next-steps","text":"Depending on your database backend, the database configuration will differ a bit. I'm using SQLAlchemy I'm using MongoDB I'm using Tortoise ORM I'm using ormar","title":"Next steps"},{"location":"configuration/oauth/","text":"OAuth2 \u00b6 FastAPI Users provides an optional OAuth2 authentication support. It relies on HTTPX OAuth library , which is a pure-async implementation of OAuth2. Installation \u00b6 You should install the library with the optional dependencies for OAuth: pip install 'fastapi-users[sqlalchemy,oauth]' pip install 'fastapi-users[mongodb,oauth]' pip install 'fastapi-users[tortoise-orm,oauth]' Configuration \u00b6 Instantiate an OAuth2 client \u00b6 You first need to get an HTTPX OAuth client instance. Read the documentation for more information. from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) Setup the models \u00b6 The user models differ a bit from the standard one as we have to have a way to store the OAuth information (access tokens, account ids...). from fastapi_users import models class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass Notice that we inherit from the BaseOAuthAccountMixin , which adds a List of BaseOAuthAccount objects. This object is structured like this: id ( UUID4 ) \u2013 Unique identifier of the OAuth account information. Defaults to a UUID4 . oauth_name ( str ) \u2013 Name of the OAuth service. It corresponds to the name property of the OAuth client. access_token ( str ) \u2013 Access token. expires_at ( Optional[int] ) - Timestamp at which the access token is expired. refresh_token ( Optional[str] ) \u2013 On services that support it, a token to get a fresh access token. account_id ( str ) - Identifier of the OAuth account on the corresponding service. account_email ( str ) - Email address of the OAuth account on the corresponding service. Setup the database adapter \u00b6 SQLAlchemy \u00b6 You'll need to define the table for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.sqlalchemy import SQLAlchemyBaseOAuthAccountTable class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass Then, you should declare it on the database adapter: user_db = SQLAlchemyUserDatabase ( UserDB , database , User . __table__ , OAuthAccount . __table__ ) MongoDB \u00b6 Nothing to do, the basic configuration is enough. Tortoise ORM \u00b6 You'll need to define the Tortoise model for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.tortoise import TortoiseBaseOAuthAccountModel class OAuthAccount ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.User\" , related_name = \"oauth_accounts\" ) Warning Note that you should define the foreign key yourself, so that you can point it the user model in your namespace. Then, you should declare it on the database adapter: user_db = TortoiseUserDatabase ( UserDB , User , OAuthAccount ) Generate a router \u00b6 Once you have a FastAPIUsers instance, you can make it generate a single OAuth router for the given client. from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) After register \u00b6 You can provide a custom function to be called after a successful registration. It is called with two argument : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) Full example \u00b6 Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database. SQLAlchemy \u00b6 import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( SQLAlchemyBaseOAuthAccountTable , SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ oauth_accounts = OAuthAccount . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users , oauth_accounts ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () MongoDB \u00b6 import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase from httpx_oauth.clients.google import GoogleOAuth2 DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) Tortoise ORM \u00b6 from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( TortoiseBaseOAuthAccountModel , TortoiseBaseUserModel , TortoiseUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from tortoise import fields from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class UserModel ( TortoiseBaseUserModel ): pass class OAuthAccountModel ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.UserModel\" , related_name = \"oauth_accounts\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel , OAuthAccountModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"oauth_full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"OAuth2"},{"location":"configuration/oauth/#oauth2","text":"FastAPI Users provides an optional OAuth2 authentication support. It relies on HTTPX OAuth library , which is a pure-async implementation of OAuth2.","title":"OAuth2"},{"location":"configuration/oauth/#installation","text":"You should install the library with the optional dependencies for OAuth: pip install 'fastapi-users[sqlalchemy,oauth]' pip install 'fastapi-users[mongodb,oauth]' pip install 'fastapi-users[tortoise-orm,oauth]'","title":"Installation"},{"location":"configuration/oauth/#configuration","text":"","title":"Configuration"},{"location":"configuration/oauth/#instantiate-an-oauth2-client","text":"You first need to get an HTTPX OAuth client instance. Read the documentation for more information. from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" )","title":"Instantiate an OAuth2 client"},{"location":"configuration/oauth/#setup-the-models","text":"The user models differ a bit from the standard one as we have to have a way to store the OAuth information (access tokens, account ids...). from fastapi_users import models class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass Notice that we inherit from the BaseOAuthAccountMixin , which adds a List of BaseOAuthAccount objects. This object is structured like this: id ( UUID4 ) \u2013 Unique identifier of the OAuth account information. Defaults to a UUID4 . oauth_name ( str ) \u2013 Name of the OAuth service. It corresponds to the name property of the OAuth client. access_token ( str ) \u2013 Access token. expires_at ( Optional[int] ) - Timestamp at which the access token is expired. refresh_token ( Optional[str] ) \u2013 On services that support it, a token to get a fresh access token. account_id ( str ) - Identifier of the OAuth account on the corresponding service. account_email ( str ) - Email address of the OAuth account on the corresponding service.","title":"Setup the models"},{"location":"configuration/oauth/#setup-the-database-adapter","text":"","title":"Setup the database adapter"},{"location":"configuration/oauth/#sqlalchemy","text":"You'll need to define the table for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.sqlalchemy import SQLAlchemyBaseOAuthAccountTable class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass Then, you should declare it on the database adapter: user_db = SQLAlchemyUserDatabase ( UserDB , database , User . __table__ , OAuthAccount . __table__ )","title":"SQLAlchemy"},{"location":"configuration/oauth/#mongodb","text":"Nothing to do, the basic configuration is enough.","title":"MongoDB"},{"location":"configuration/oauth/#tortoise-orm","text":"You'll need to define the Tortoise model for storing the OAuth account model. We provide a base one for this: from fastapi_users.db.tortoise import TortoiseBaseOAuthAccountModel class OAuthAccount ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.User\" , related_name = \"oauth_accounts\" ) Warning Note that you should define the foreign key yourself, so that you can point it the user model in your namespace. Then, you should declare it on the database adapter: user_db = TortoiseUserDatabase ( UserDB , User , OAuthAccount )","title":"Tortoise ORM"},{"location":"configuration/oauth/#generate-a-router","text":"Once you have a FastAPIUsers instance, you can make it generate a single OAuth router for the given client. from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"Generate a router"},{"location":"configuration/oauth/#after-register","text":"You can provide a custom function to be called after a successful registration. It is called with two argument : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: from fastapi import FastAPI from fastapi_users import FastAPIUsers from httpx_oauth.clients.google import GoogleOAuth2 def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB ) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"After register"},{"location":"configuration/oauth/#full-example","text":"Warning Notice that SECRET should be changed to a strong passphrase. Insecure passwords may give attackers full access to your database.","title":"Full example"},{"location":"configuration/oauth/#sqlalchemy_1","text":"import databases import sqlalchemy from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( SQLAlchemyBaseOAuthAccountTable , SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base DATABASE_URL = \"sqlite:///./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass class OAuthAccount ( SQLAlchemyBaseOAuthAccountTable , Base ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ oauth_accounts = OAuthAccount . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users , oauth_accounts ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ]) @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect ()","title":"SQLAlchemy"},{"location":"configuration/oauth/#mongodb_1","text":"import motor.motor_asyncio from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import MongoDBUserDatabase from httpx_oauth.clients.google import GoogleOAuth2 DATABASE_URL = \"mongodb://localhost:27017\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] user_db = MongoDBUserDatabase ( UserDB , collection ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"MongoDB"},{"location":"configuration/oauth/#tortoise-orm_1","text":"from fastapi import FastAPI , Request from fastapi_users import FastAPIUsers , models from fastapi_users.authentication import JWTAuthentication from fastapi_users.db import ( TortoiseBaseOAuthAccountModel , TortoiseBaseUserModel , TortoiseUserDatabase , ) from httpx_oauth.clients.google import GoogleOAuth2 from tortoise import fields from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel DATABASE_URL = \"sqlite://./test.db\" SECRET = \"SECRET\" google_oauth_client = GoogleOAuth2 ( \"CLIENT_ID\" , \"CLIENT_SECRET\" ) class UserModel ( TortoiseBaseUserModel ): pass class OAuthAccountModel ( TortoiseBaseOAuthAccountModel ): user = fields . ForeignKeyField ( \"models.UserModel\" , related_name = \"oauth_accounts\" ) class User ( models . BaseUser , models . BaseOAuthAccountMixin ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel user_db = TortoiseUserDatabase ( UserDB , UserModel , OAuthAccountModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"oauth_full_tortoise\" ]}, generate_schemas = True , ) def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , tokenUrl = \"/auth/jwt/login\" ) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_verify_router ( SECRET , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) google_oauth_router = fastapi_users . get_oauth_router ( google_oauth_client , SECRET , after_register = on_after_register ) app . include_router ( google_oauth_router , prefix = \"/auth/google\" , tags = [ \"auth\" ])","title":"Tortoise ORM"},{"location":"configuration/authentication/","text":"Authentication \u00b6 FastAPI Users allows you to plug in several authentication methods. How it works? \u00b6 You can have several authentication methods, e.g. a cookie authentication for browser-based queries and a JWT token authentication for pure API queries. When checking authentication, each method is run one after the other. The first method yielding a user wins. If no method yields a user, an HTTPException is raised. For each backend, you'll be able to add a router with the corresponding /login and /logout (if applicable routes). More on this in the routers documentation . Provided methods \u00b6 JWT authentication Cookie authentication","title":"Introduction"},{"location":"configuration/authentication/#authentication","text":"FastAPI Users allows you to plug in several authentication methods.","title":"Authentication"},{"location":"configuration/authentication/#how-it-works","text":"You can have several authentication methods, e.g. a cookie authentication for browser-based queries and a JWT token authentication for pure API queries. When checking authentication, each method is run one after the other. The first method yielding a user wins. If no method yields a user, an HTTPException is raised. For each backend, you'll be able to add a router with the corresponding /login and /logout (if applicable routes). More on this in the routers documentation .","title":"How it works?"},{"location":"configuration/authentication/#provided-methods","text":"JWT authentication Cookie authentication","title":"Provided methods"},{"location":"configuration/authentication/cookie/","text":"Cookie \u00b6 Cookies are an easy way to store stateful information into the user browser. Thus, it is more useful for browser-based navigation (e.g. a front-end app making API requests) rather than pure API interaction. Configuration \u00b6 from fastapi_users.authentication import CookieAuthentication SECRET = \"SECRET\" auth_backends = [] cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( cookie_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of the cookie (in seconds). You can also define the parameters for the generated cookie: cookie_name ( fastapiusersauth ): Name of the cookie. cookie_path ( / ): Cookie path. cookie_domain ( None ): Cookie domain. cookie_secure ( True ): Whether to only send the cookie to the server via SSL request. cookie_httponly ( True ): Whether to prevent access to the cookie via JavaScript. cookie_samesite ( lax ): A string that specifies the samesite strategy for the cookie. Valid values are 'lax', 'strict' and 'none'. Defaults to 'lax'. Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to cookie . cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-cookie\" , ) Tip The value of the cookie is actually a JWT. This authentication backend shares most of its logic with the JWT one. Login \u00b6 This method will return a response with a valid set-cookie header upon successful login: 200 OK Check documentation about login route . Logout \u00b6 This method will remove the authentication cookie: 200 OK Check documentation about logout route . Authentication \u00b6 This method expects that you provide a valid cookie in the headers. Next steps \u00b6 We will now configure the main FastAPI Users object that will expose the routers .","title":"Cookie"},{"location":"configuration/authentication/cookie/#cookie","text":"Cookies are an easy way to store stateful information into the user browser. Thus, it is more useful for browser-based navigation (e.g. a front-end app making API requests) rather than pure API interaction.","title":"Cookie"},{"location":"configuration/authentication/cookie/#configuration","text":"from fastapi_users.authentication import CookieAuthentication SECRET = \"SECRET\" auth_backends = [] cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( cookie_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of the cookie (in seconds). You can also define the parameters for the generated cookie: cookie_name ( fastapiusersauth ): Name of the cookie. cookie_path ( / ): Cookie path. cookie_domain ( None ): Cookie domain. cookie_secure ( True ): Whether to only send the cookie to the server via SSL request. cookie_httponly ( True ): Whether to prevent access to the cookie via JavaScript. cookie_samesite ( lax ): A string that specifies the samesite strategy for the cookie. Valid values are 'lax', 'strict' and 'none'. Defaults to 'lax'. Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to cookie . cookie_authentication = CookieAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-cookie\" , ) Tip The value of the cookie is actually a JWT. This authentication backend shares most of its logic with the JWT one.","title":"Configuration"},{"location":"configuration/authentication/cookie/#login","text":"This method will return a response with a valid set-cookie header upon successful login: 200 OK Check documentation about login route .","title":"Login"},{"location":"configuration/authentication/cookie/#logout","text":"This method will remove the authentication cookie: 200 OK Check documentation about logout route .","title":"Logout"},{"location":"configuration/authentication/cookie/#authentication","text":"This method expects that you provide a valid cookie in the headers.","title":"Authentication"},{"location":"configuration/authentication/cookie/#next-steps","text":"We will now configure the main FastAPI Users object that will expose the routers .","title":"Next steps"},{"location":"configuration/authentication/jwt/","text":"JWT \u00b6 JSON Web Token (JWT) is an internet standard for creating access tokens based on JSON. Configuration \u00b6 from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" auth_backends = [] jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( jwt_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of token (in seconds). Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to jwt . jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-jwt\" , ) Login \u00b6 This method will return a JWT token upon successful login: 200 OK { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Check documentation about login route . Logout \u00b6 This backend does not provide a logout method (a JWT is valid until it expires). Authentication \u00b6 This method expects that you provide a Bearer authentication with a valid JWT. curl http://localhost:9000/protected-route -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI' Tip: Refresh \u00b6 The default implementation does not provide a mechanism to refresh the JWT. However, you can implement it quite easily like this: from fastapi import Depends , Response @router . post ( \"/auth/jwt/refresh\" ) async def refresh_jwt ( response : Response , user = Depends ( fastapi_users . get_current_active_user )): return await jwt_authentication . get_login_response ( user , response ) Next steps \u00b6 We will now configure the main FastAPI Users object that will expose the routers .","title":"JWT"},{"location":"configuration/authentication/jwt/#jwt","text":"JSON Web Token (JWT) is an internet standard for creating access tokens based on JSON.","title":"JWT"},{"location":"configuration/authentication/jwt/#configuration","text":"from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" auth_backends = [] jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) auth_backends . append ( jwt_authentication ) As you can see, instantiation is quite simple. You just have to define a constant SECRET which is used to encode the token and the lifetime of token (in seconds). Tip You can also optionally define the name . It's useful in the case you wish to have several backends of the same class. Each backend should have a unique name. Defaults to jwt . jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 , name = \"my-jwt\" , )","title":"Configuration"},{"location":"configuration/authentication/jwt/#login","text":"This method will return a JWT token upon successful login: 200 OK { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Check documentation about login route .","title":"Login"},{"location":"configuration/authentication/jwt/#logout","text":"This backend does not provide a logout method (a JWT is valid until it expires).","title":"Logout"},{"location":"configuration/authentication/jwt/#authentication","text":"This method expects that you provide a Bearer authentication with a valid JWT. curl http://localhost:9000/protected-route -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI'","title":"Authentication"},{"location":"configuration/authentication/jwt/#tip-refresh","text":"The default implementation does not provide a mechanism to refresh the JWT. However, you can implement it quite easily like this: from fastapi import Depends , Response @router . post ( \"/auth/jwt/refresh\" ) async def refresh_jwt ( response : Response , user = Depends ( fastapi_users . get_current_active_user )): return await jwt_authentication . get_login_response ( user , response )","title":"Tip: Refresh"},{"location":"configuration/authentication/jwt/#next-steps","text":"We will now configure the main FastAPI Users object that will expose the routers .","title":"Next steps"},{"location":"configuration/databases/mongodb/","text":"MongoDB \u00b6 FastAPI Users provides the necessary tools to work with MongoDB databases thanks to mongodb/motor package for full async support. Setup database connection and collection \u00b6 Let's create a MongoDB connection and instantiate a collection. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) You can choose any name for the database and the collection. Warning You may have noticed the uuidRepresentation parameter. It controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this . Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) Notice that we pass a reference to your UserDB model . Info The database adapter will automatically create a unique index on id and email . Warning FastAPI Users will use its defined id UUID as unique identifier for the user, rather than the builtin MongoDB _id . Next steps \u00b6 We will now configure an authentication method .","title":"MongoDB"},{"location":"configuration/databases/mongodb/#mongodb","text":"FastAPI Users provides the necessary tools to work with MongoDB databases thanks to mongodb/motor package for full async support.","title":"MongoDB"},{"location":"configuration/databases/mongodb/#setup-database-connection-and-collection","text":"Let's create a MongoDB connection and instantiate a collection. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) You can choose any name for the database and the collection. Warning You may have noticed the uuidRepresentation parameter. It controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this .","title":"Setup database connection and collection"},{"location":"configuration/databases/mongodb/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import motor.motor_asyncio from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import MongoDBUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] collection = db [ \"users\" ] app = FastAPI () user_db = MongoDBUserDatabase ( UserDB , collection ) Notice that we pass a reference to your UserDB model . Info The database adapter will automatically create a unique index on id and email . Warning FastAPI Users will use its defined id UUID as unique identifier for the user, rather than the builtin MongoDB _id .","title":"Create the database adapter"},{"location":"configuration/databases/mongodb/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/databases/ormar/","text":"Ormar \u00b6 FastAPI Users provides the necessary tools to work with ormar. Installation \u00b6 Install the database driver that corresponds to your DBMS: pip install asyncpg psycopg2 pip install aiomysql pymysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse. Setup User table \u00b6 Let's declare our User ORM model. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs! Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () Notice that we pass a reference to your UserDB model . Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic . Next steps \u00b6 We will now configure an authentication method .","title":"Ormar"},{"location":"configuration/databases/ormar/#ormar","text":"FastAPI Users provides the necessary tools to work with ormar.","title":"Ormar"},{"location":"configuration/databases/ormar/#installation","text":"Install the database driver that corresponds to your DBMS: pip install asyncpg psycopg2 pip install aiomysql pymysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse.","title":"Installation"},{"location":"configuration/databases/ormar/#setup-user-table","text":"Let's declare our User ORM model. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs!","title":"Setup User table"},{"location":"configuration/databases/ormar/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import OrmarBaseUserModel , OrmarUserDatabase class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///test.db\" metadata = sqlalchemy . MetaData () database = databases . Database ( DATABASE_URL ) class UserModel ( OrmarBaseUserModel ): class Meta : tablename = \"users\" metadata = metadata database = database engine = sqlalchemy . create_engine ( DATABASE_URL ) metadata . create_all ( engine ) user_db = OrmarUserDatabase ( UserDB , UserModel ) app = FastAPI () app . state . database = database @app . on_event ( \"startup\" ) async def startup () -> None : database_ = app . state . database if not database_ . is_connected : await database_ . connect () @app . on_event ( \"shutdown\" ) async def shutdown () -> None : database_ = app . state . database if database_ . is_connected : await database_ . disconnect () Notice that we pass a reference to your UserDB model . Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic .","title":"Create the database adapter"},{"location":"configuration/databases/ormar/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/databases/sqlalchemy/","text":"SQLAlchemy \u00b6 FastAPI Users provides the necessary tools to work with SQL databases thanks to SQLAlchemy Core and encode/databases package for full async support. Installation \u00b6 Install the database driver that corresponds to your DBMS: pip install 'databases[postgresql]' pip install 'databases[mysql]' pip install 'databases[sqlite]' For the sake of this tutorial from now on, we'll use a simple SQLite databse. Setup User table \u00b6 Let's create a metadata object and declare our User table. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () As you can see, FastAPI Users provides a mixin that will include base fields for our User table. You can of course add you own fields there to fit to your needs! Create the tables \u00b6 We'll now create an SQLAlchemy engine and ask it to create all the defined tables. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic . Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Notice that we pass it three things: A reference to your UserDB model . The users variable, which is the actual SQLAlchemy table behind the table class. A database instance, which allows us to do asynchronous request to the database. Next steps \u00b6 We will now configure an authentication method . What about SQLAlchemy ORM? \u00b6 The primary objective was to use pure async approach as much as possible. However, we understand that ORM is convenient and useful for many developers. If this feature becomes very demanded, we will add a database adapter for SQLAlchemy ORM.","title":"SQLAlchemy"},{"location":"configuration/databases/sqlalchemy/#sqlalchemy","text":"FastAPI Users provides the necessary tools to work with SQL databases thanks to SQLAlchemy Core and encode/databases package for full async support.","title":"SQLAlchemy"},{"location":"configuration/databases/sqlalchemy/#installation","text":"Install the database driver that corresponds to your DBMS: pip install 'databases[postgresql]' pip install 'databases[mysql]' pip install 'databases[sqlite]' For the sake of this tutorial from now on, we'll use a simple SQLite databse.","title":"Installation"},{"location":"configuration/databases/sqlalchemy/#setup-user-table","text":"Let's create a metadata object and declare our User table. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () As you can see, FastAPI Users provides a mixin that will include base fields for our User table. You can of course add you own fields there to fit to your needs!","title":"Setup User table"},{"location":"configuration/databases/sqlalchemy/#create-the-tables","text":"We'll now create an SQLAlchemy engine and ask it to create all the defined tables. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See Alembic .","title":"Create the tables"},{"location":"configuration/databases/sqlalchemy/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. import databases import sqlalchemy from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import SQLAlchemyBaseUserTable , SQLAlchemyUserDatabase from sqlalchemy.ext.declarative import DeclarativeMeta , declarative_base class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB ): pass DATABASE_URL = \"sqlite:///./test.db\" database = databases . Database ( DATABASE_URL ) Base : DeclarativeMeta = declarative_base () class UserTable ( Base , SQLAlchemyBaseUserTable ): pass engine = sqlalchemy . create_engine ( DATABASE_URL , connect_args = { \"check_same_thread\" : False } ) Base . metadata . create_all ( engine ) users = UserTable . __table__ user_db = SQLAlchemyUserDatabase ( UserDB , database , users ) app = FastAPI () @app . on_event ( \"startup\" ) async def startup (): await database . connect () @app . on_event ( \"shutdown\" ) async def shutdown (): await database . disconnect () Notice that we pass it three things: A reference to your UserDB model . The users variable, which is the actual SQLAlchemy table behind the table class. A database instance, which allows us to do asynchronous request to the database.","title":"Create the database adapter"},{"location":"configuration/databases/sqlalchemy/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/databases/sqlalchemy/#what-about-sqlalchemy-orm","text":"The primary objective was to use pure async approach as much as possible. However, we understand that ORM is convenient and useful for many developers. If this feature becomes very demanded, we will add a database adapter for SQLAlchemy ORM.","title":"What about SQLAlchemy ORM?"},{"location":"configuration/databases/tortoise/","text":"Tortoise ORM \u00b6 FastAPI Users provides the necessary tools to work with Tortoise ORM. Installation \u00b6 Install the database driver that corresponds to your DBMS: pip install asyncpg pip install aiomysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse. Setup User table \u00b6 Let's declare our User ORM model. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs! Tweak UserDB model \u00b6 In order to make the Pydantic model and the Tortoise ORM model working well together, you'll have to add a mixin and some configuration options to your UserDB model. Tortoise ORM provides utilities to ease the integration with Pydantic and we'll use them here. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) The PydanticModel mixin adds methods used internally by Tortoise ORM to the Pydantic model so that it can easily transform it back to an ORM model. It expects then that you provide the property orig_model which should point to the User ORM model we defined just above . Create the database adapter \u00b6 The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Notice that we pass a reference to your UserDB model . Register Tortoise \u00b6 For using Tortoise ORM we must register our models and database. Tortoise ORM supports integration with FastAPI out-of-the-box. It will automatically bind startup and shutdown events. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See https://tortoise-orm.readthedocs.io/en/latest/migration.html . Next steps \u00b6 We will now configure an authentication method .","title":"Tortoise ORM"},{"location":"configuration/databases/tortoise/#tortoise-orm","text":"FastAPI Users provides the necessary tools to work with Tortoise ORM.","title":"Tortoise ORM"},{"location":"configuration/databases/tortoise/#installation","text":"Install the database driver that corresponds to your DBMS: pip install asyncpg pip install aiomysql pip install aiosqlite For the sake of this tutorial from now on, we'll use a simple SQLite databse.","title":"Installation"},{"location":"configuration/databases/tortoise/#setup-user-table","text":"Let's declare our User ORM model. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) As you can see, FastAPI Users provides an abstract model that will include base fields for our User table. You can of course add you own fields there to fit to your needs!","title":"Setup User table"},{"location":"configuration/databases/tortoise/#tweak-userdb-model","text":"In order to make the Pydantic model and the Tortoise ORM model working well together, you'll have to add a mixin and some configuration options to your UserDB model. Tortoise ORM provides utilities to ease the integration with Pydantic and we'll use them here. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) The PydanticModel mixin adds methods used internally by Tortoise ORM to the Pydantic model so that it can easily transform it back to an ORM model. It expects then that you provide the property orig_model which should point to the User ORM model we defined just above .","title":"Tweak UserDB model"},{"location":"configuration/databases/tortoise/#create-the-database-adapter","text":"The database adapter of FastAPI Users makes the link between your database configuration and the users logic. Create it like this. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Notice that we pass a reference to your UserDB model .","title":"Create the database adapter"},{"location":"configuration/databases/tortoise/#register-tortoise","text":"For using Tortoise ORM we must register our models and database. Tortoise ORM supports integration with FastAPI out-of-the-box. It will automatically bind startup and shutdown events. from fastapi import FastAPI from fastapi_users import models from fastapi_users.db import TortoiseBaseUserModel , TortoiseUserDatabase from tortoise.contrib.fastapi import register_tortoise from tortoise.contrib.pydantic import PydanticModel class UserModel ( TortoiseBaseUserModel ): pass class User ( models . BaseUser ): pass class UserCreate ( models . BaseUserCreate ): pass class UserUpdate ( User , models . BaseUserUpdate ): pass class UserDB ( User , models . BaseUserDB , PydanticModel ): class Config : orm_mode = True orig_model = UserModel DATABASE_URL = \"sqlite://./test.db\" user_db = TortoiseUserDatabase ( UserDB , UserModel ) app = FastAPI () register_tortoise ( app , db_url = DATABASE_URL , modules = { \"models\" : [ \"path_to_your_package\" ]}, generate_schemas = True , ) Warning In production, it's strongly recommended to setup a migration system to update your SQL schemas. See https://tortoise-orm.readthedocs.io/en/latest/migration.html .","title":"Register Tortoise"},{"location":"configuration/databases/tortoise/#next-steps","text":"We will now configure an authentication method .","title":"Next steps"},{"location":"configuration/routers/","text":"Routers \u00b6 We're almost there! The last step is to configure the FastAPIUsers object that will wire the database adapter, the authentication classes and let us generate the actual API routes . Configure FastAPIUsers \u00b6 Configure FastAPIUsers object with all the elements we defined before. More precisely: db : Database adapter instance. auth_backends : List of authentication backends. See Authentication . user_model : Pydantic model of a user. user_create_model : Pydantic model for creating a user. user_update_model : Pydantic model for updating a user. user_db_model : Pydantic model of a DB representation of a user. from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) Available routers \u00b6 This helper class will let you generate useful routers to setup the authentication system. Each of them is optional , so you can pick only the one that you are interested in! Here are the routers provided: Auth router : Provides /login and /logout routes for a given authentication backend . Register router : Provides /register routes to allow a user to create a new account. Reset password router : Provides /forgot-password and /reset-password routes to allow a user to reset its password. Verify router : Provides /request-verify-token and /verify routes to manage user e-mail verification. Users router : Provides routes to manage users. OAuth router : Provides routes to perform an OAuth authentication against a service provider (like Google or Facebook). You should check out each of them to understand how to use them.","title":"Introduction"},{"location":"configuration/routers/#routers","text":"We're almost there! The last step is to configure the FastAPIUsers object that will wire the database adapter, the authentication classes and let us generate the actual API routes .","title":"Routers"},{"location":"configuration/routers/#configure-fastapiusers","text":"Configure FastAPIUsers object with all the elements we defined before. More precisely: db : Database adapter instance. auth_backends : List of authentication backends. See Authentication . user_model : Pydantic model of a user. user_create_model : Pydantic model for creating a user. user_update_model : Pydantic model for updating a user. user_db_model : Pydantic model of a DB representation of a user. from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , )","title":"Configure FastAPIUsers"},{"location":"configuration/routers/#available-routers","text":"This helper class will let you generate useful routers to setup the authentication system. Each of them is optional , so you can pick only the one that you are interested in! Here are the routers provided: Auth router : Provides /login and /logout routes for a given authentication backend . Register router : Provides /register routes to allow a user to create a new account. Reset password router : Provides /forgot-password and /reset-password routes to allow a user to reset its password. Verify router : Provides /request-verify-token and /verify routes to manage user e-mail verification. Users router : Provides routes to manage users. OAuth router : Provides routes to perform an OAuth authentication against a service provider (like Google or Facebook). You should check out each of them to understand how to use them.","title":"Available routers"},{"location":"configuration/routers/auth/","text":"Auth router \u00b6 The auth router will generate /login and /logout (if applicable) routes for a given authentication backend . Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], ) Optional: user verification \u00b6 You can require the user to be verified (i.e. is_verified property set to True ) to allow login. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_auth_router ( jwt_authentication , requires_verification = True ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], )","title":"Auth router"},{"location":"configuration/routers/auth/#auth-router","text":"The auth router will generate /login and /logout (if applicable) routes for a given authentication backend . Check the routes usage to learn how to use them.","title":"Auth router"},{"location":"configuration/routers/auth/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], )","title":"Setup"},{"location":"configuration/routers/auth/#optional-user-verification","text":"You can require the user to be verified (i.e. is_verified property set to True ) to allow login. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_auth_router ( jwt_authentication , requires_verification = True ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ], )","title":"Optional: user verification"},{"location":"configuration/routers/register/","text":"Register routes \u00b6 The register router will generate a /register route to allow a user to create a new account. Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_register_router (), prefix = \"/auth\" , tags = [ \"auth\" ], ) After register \u00b6 You can provide a custom function to be called after a successful registration. It is called with two arguments : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Register routes"},{"location":"configuration/routers/register/#register-routes","text":"The register router will generate a /register route to allow a user to create a new account. Check the routes usage to learn how to use them.","title":"Register routes"},{"location":"configuration/routers/register/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers from fastapi_users.authentication import JWTAuthentication SECRET = \"SECRET\" jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 )) fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_register_router (), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Setup"},{"location":"configuration/routers/register/#after-register","text":"You can provide a custom function to be called after a successful registration. It is called with two arguments : the user that has just registered, and the original Request object . Typically, you'll want to send a welcome e-mail or add it to your marketing analytics pipeline. You can define it as an async or standard method. Example: def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After register"},{"location":"configuration/routers/reset/","text":"Reset password router \u00b6 The reset password router will generate /forgot-password (the user asks for a token to reset its password) and /reset-password (the user changes its password given the token) routes. Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: reset_password_token_secret : Secret to encode reset password token. reset_password_token_lifetime_seconds : Lifetime of reset password token. Defaults to 3600 . after_forgot_password : Optional function called after a successful forgot password request. See below. After forgot password \u00b6 You can provide a custom function to be called after a successful forgot password request. It is called with three arguments : The user which has requested to reset their password. A ready-to-use JWT token that will be accepted by the reset password route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to reset their password. You can define it as an async or standard method. Example: def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) After reset password \u00b6 You can provide a custom function to be called after a successful password reset. It is called with two arguments : The user which has reset their password. The original Request object . For example, you may want to send an e-mail to the concerned user to warn him that their password has been changed and that they should take action if they think they have been hacked. You can define it as an async or standard method. Example: def on_after_reset_password ( user : UserDB , request : Request ): print ( f \"User { user . id } has reset their password.\" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_reset_password = on_after_reset_password ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Reset password router"},{"location":"configuration/routers/reset/#reset-password-router","text":"The reset password router will generate /forgot-password (the user asks for a token to reset its password) and /reset-password (the user changes its password given the token) routes. Check the routes usage to learn how to use them.","title":"Reset password router"},{"location":"configuration/routers/reset/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: reset_password_token_secret : Secret to encode reset password token. reset_password_token_lifetime_seconds : Lifetime of reset password token. Defaults to 3600 . after_forgot_password : Optional function called after a successful forgot password request. See below.","title":"Setup"},{"location":"configuration/routers/reset/#after-forgot-password","text":"You can provide a custom function to be called after a successful forgot password request. It is called with three arguments : The user which has requested to reset their password. A ready-to-use JWT token that will be accepted by the reset password route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to reset their password. You can define it as an async or standard method. Example: def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After forgot password"},{"location":"configuration/routers/reset/#after-reset-password","text":"You can provide a custom function to be called after a successful password reset. It is called with two arguments : The user which has reset their password. The original Request object . For example, you may want to send an e-mail to the concerned user to warn him that their password has been changed and that they should take action if they think they have been hacked. You can define it as an async or standard method. Example: def on_after_reset_password ( user : UserDB , request : Request ): print ( f \"User { user . id } has reset their password.\" ) app . include_router ( fastapi_users . get_reset_password_router ( \"SECRET\" , after_reset_password = on_after_reset_password ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After reset password"},{"location":"configuration/routers/users/","text":"Users router \u00b6 This router provides routes to manage users. Check the routes usage to learn how to use them. Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ], ) Optional: user verification \u00b6 You can require the user to be verified (i.e. is_verified property set to True ) to access those routes. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_users_router ( requires_verification = True ), prefix = \"/users\" , tags = [ \"users\" ], ) After update \u00b6 You can provide a custom function to be called after a successful update user request. It is called with three arguments : The user which was updated. The dictionary containing the updated fields. The original Request object . It may be useful, for example, if you wish to update your user in a data analytics or customer success platform. You can define it as an async or standard method. Example: def on_after_update ( user : UserDB , updated_user_data : Dict [ str , Any ], request : Request ): print ( f \"User { user . id } has been updated with the following data: { updated_user_data } \" ) app . include_router ( fastapi_users . get_users_router ( on_after_update ), prefix = \"/users\" , tags = [ \"users\" ], )","title":"Users router"},{"location":"configuration/routers/users/#users-router","text":"This router provides routes to manage users. Check the routes usage to learn how to use them.","title":"Users router"},{"location":"configuration/routers/users/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ], )","title":"Setup"},{"location":"configuration/routers/users/#optional-user-verification","text":"You can require the user to be verified (i.e. is_verified property set to True ) to access those routes. You have to set the requires_verification parameter to True on the router instantiation method: app . include_router ( fastapi_users . get_users_router ( requires_verification = True ), prefix = \"/users\" , tags = [ \"users\" ], )","title":"Optional: user verification"},{"location":"configuration/routers/users/#after-update","text":"You can provide a custom function to be called after a successful update user request. It is called with three arguments : The user which was updated. The dictionary containing the updated fields. The original Request object . It may be useful, for example, if you wish to update your user in a data analytics or customer success platform. You can define it as an async or standard method. Example: def on_after_update ( user : UserDB , updated_user_data : Dict [ str , Any ], request : Request ): print ( f \"User { user . id } has been updated with the following data: { updated_user_data } \" ) app . include_router ( fastapi_users . get_users_router ( on_after_update ), prefix = \"/users\" , tags = [ \"users\" ], )","title":"After update"},{"location":"configuration/routers/verify/","text":"Verify router \u00b6 This router provides routes to manage user email verification. Check the routes usage to learn how to use them. \ud83d\udc4f\ud83d\udc4f\ud83d\udc4f A big thank you to Edd Salkield and Mark Todd who worked hard on this feature! Setup \u00b6 from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: verification_token_secret : Secret to encode verify token. verification_token_lifetime_seconds : Lifetime of verify token. Defaults to 3600 . after_verification_request : Optional function called after a successful verify request. See below. after_verification : Optional function called after a successful verification. See below. After verification request \u00b6 You can provide a custom function to be called after a successful verification request. It is called with three arguments : The user for which the verification has been requested. A ready-to-use JWT token that will be accepted by the verify route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to verify their e-mail. You can define it as an async or standard method. Example: def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], ) After verification \u00b6 You can provide a custom function to be called after a successful user verification. It is called with two arguments : The user that has been verified. The original Request object . This may be useful if you wish to send another e-mail or store this information in a data analytics or customer success platform. You can define it as an async or standard method. Example: def after_verification ( user : UserDB , request : Request ): print ( f \" { user . id } is now verified.\" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification = after_verification ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"Verify router"},{"location":"configuration/routers/verify/#verify-router","text":"This router provides routes to manage user email verification. Check the routes usage to learn how to use them. \ud83d\udc4f\ud83d\udc4f\ud83d\udc4f A big thank you to Edd Salkield and Mark Todd who worked hard on this feature!","title":"Verify router"},{"location":"configuration/routers/verify/#setup","text":"from fastapi import FastAPI from fastapi_users import FastAPIUsers fastapi_users = FastAPIUsers ( user_db , auth_backends , User , UserCreate , UserUpdate , UserDB , ) app = FastAPI () app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" ), prefix = \"/auth\" , tags = [ \"auth\" ], ) Parameters: verification_token_secret : Secret to encode verify token. verification_token_lifetime_seconds : Lifetime of verify token. Defaults to 3600 . after_verification_request : Optional function called after a successful verify request. See below. after_verification : Optional function called after a successful verification. See below.","title":"Setup"},{"location":"configuration/routers/verify/#after-verification-request","text":"You can provide a custom function to be called after a successful verification request. It is called with three arguments : The user for which the verification has been requested. A ready-to-use JWT token that will be accepted by the verify route. The original Request object . Typically, you'll want to send an e-mail with the link (and the token) that allows the user to verify their e-mail. You can define it as an async or standard method. Example: def after_verification_request ( user : UserDB , token : str , request : Request ): print ( f \"Verification requested for user { user . id } . Verification token: { token } \" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification_request = after_verification_request ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After verification request"},{"location":"configuration/routers/verify/#after-verification","text":"You can provide a custom function to be called after a successful user verification. It is called with two arguments : The user that has been verified. The original Request object . This may be useful if you wish to send another e-mail or store this information in a data analytics or customer success platform. You can define it as an async or standard method. Example: def after_verification ( user : UserDB , request : Request ): print ( f \" { user . id } is now verified.\" ) app . include_router ( fastapi_users . get_verify_router ( \"SECRET\" , after_verification = after_verification ), prefix = \"/auth\" , tags = [ \"auth\" ], )","title":"After verification"},{"location":"migration/08_to_1x/","text":"0.8.x \u27a1\ufe0f 1.x.x \u00b6 1.0 version introduces major breaking changes that need you to update some of your code and migrate your data. Id. are UUID \u00b6 Users and OAuth accounts id. are now represented as real UUID objects instead of plain strings. This change was introduced to leverage efficient storage and indexing for DBMS that supports UUID (especially PostgreSQL and Mongo). In Python code \u00b6 If you were doing comparison betwen a user id. and a string (in unit tests for example), you should now cast the id. to string: # Before assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == user . id # Now assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == str ( user . id ) If you were refering to user id. in your Pydantic models, the field should now be of UUID4 type instead of str : from pydantic import BaseModel , UUID4 # Before class Model ( BaseModel ): user_id : str # After class Model ( BaseModel ): user_id : UUID4 MongoDB \u00b6 To avoid any issues, it's recommended to use the standard UUID representation when instantiating the MongoDB client: DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) This parameter controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this . In database \u00b6 Id. were before stored as strings in the database. You should make a migration to convert string data to UUID data. Danger Scripts below are provided as guidelines. Please review them carefully , adapt them and check that they are working on a test database before applying them to production. BE CAREFUL. THEY CAN DESTROY YOUR DATA. . PostgreSQL \u00b6 PostgreSQL supports UUID type. If not already, you should enable the uuid-ossp extension: CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\" ; To convert the existing id. string column, we can: Create a new column with UUID type. Fill it with the id. converted to UUID. Drop the original id. column. Make the new column a primary key and rename it. ALTER TABLE \"user\" ADD uuid_id UUID ; UPDATE \"user\" SET uuid_id = uuid ( id ); ALTER TABLE \"user\" DROP id ; ALTER TABLE \"user\" ADD PRIMARY KEY ( uuid_id ); ALTER TABLE \"user\" RENAME COLUMN uuid_id TO id ; MySQL \u00b6 MySQL doesn't support UUID type. We'll just convert the column to CHAR(36) type: ALTER TABLE \"user\" MODIFY id CHAR ( 36 ); MongoDB \u00b6 Mongo shell \u00b6 For MongoDB, we can use a forEach iterator to convert the id. for each document: db . getCollection ( 'users' ). find (). forEach ( function ( user ) { var uuid = UUID ( user . id ); db . getCollection ( 'users' ). update ({ _id : user . _id }, [{ $set : { id : uuid }}]); }); Python \u00b6 import uuid import motor.motor_asyncio async def migrate_uuid (): client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] users = db [ \"users\" ] async for user in users . find ({}): await users . update_one ( { \"_id\" : user [ \"_id\" ]}, { \"$set\" : { \"id\" : uuid . UUID ( user [ \"id\" ])}}, ) Splitted routers \u00b6 You now have the responsibility to wire the routers . FastAPI Users doesn't give a bloated users router anymore. Event handlers are also removed. You have to provide your \"after-\" logic as a parameter of the router generator. Before \u00b6 jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . router , prefix = \"/users\" , tags = [ \"users\" ]) @fastapi_users . on_after_register () def on_after_register ( user : User , request : Request ): print ( f \"User { user . id } has registered.\" ) @fastapi_users . on_after_forgot_password () def on_after_forgot_password ( user : User , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) After \u00b6 def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Important things to notice: FastAPIUsers takes two arguments less ( reset_password_token_secret and reset_password_token_lifetime_seconds ). You have more flexibility to choose the prefix and tags of the routers. The /login / /logout are now your responsibility to include for each backend. The path will change (before /login/jwt , after /jwt/login ). If you don't care about some of those routers, you can discard them.","title":"0.8.x \u27a1\ufe0f 1.x.x"},{"location":"migration/08_to_1x/#08x-1xx","text":"1.0 version introduces major breaking changes that need you to update some of your code and migrate your data.","title":"0.8.x \u27a1\ufe0f 1.x.x"},{"location":"migration/08_to_1x/#id-are-uuid","text":"Users and OAuth accounts id. are now represented as real UUID objects instead of plain strings. This change was introduced to leverage efficient storage and indexing for DBMS that supports UUID (especially PostgreSQL and Mongo).","title":"Id. are UUID"},{"location":"migration/08_to_1x/#in-python-code","text":"If you were doing comparison betwen a user id. and a string (in unit tests for example), you should now cast the id. to string: # Before assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == user . id # Now assert \"d35d213e-f3d8-4f08-954a-7e0d1bea286f\" == str ( user . id ) If you were refering to user id. in your Pydantic models, the field should now be of UUID4 type instead of str : from pydantic import BaseModel , UUID4 # Before class Model ( BaseModel ): user_id : str # After class Model ( BaseModel ): user_id : UUID4","title":"In Python code"},{"location":"migration/08_to_1x/#mongodb","text":"To avoid any issues, it's recommended to use the standard UUID representation when instantiating the MongoDB client: DATABASE_URL = \"mongodb://localhost:27017\" client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) This parameter controls how the UUID values will be encoded in the database. By default, it's set to pythonLegacy but new applications should consider setting this to standard for cross language compatibility. Read more about this .","title":"MongoDB"},{"location":"migration/08_to_1x/#in-database","text":"Id. were before stored as strings in the database. You should make a migration to convert string data to UUID data. Danger Scripts below are provided as guidelines. Please review them carefully , adapt them and check that they are working on a test database before applying them to production. BE CAREFUL. THEY CAN DESTROY YOUR DATA. .","title":"In database"},{"location":"migration/08_to_1x/#postgresql","text":"PostgreSQL supports UUID type. If not already, you should enable the uuid-ossp extension: CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\" ; To convert the existing id. string column, we can: Create a new column with UUID type. Fill it with the id. converted to UUID. Drop the original id. column. Make the new column a primary key and rename it. ALTER TABLE \"user\" ADD uuid_id UUID ; UPDATE \"user\" SET uuid_id = uuid ( id ); ALTER TABLE \"user\" DROP id ; ALTER TABLE \"user\" ADD PRIMARY KEY ( uuid_id ); ALTER TABLE \"user\" RENAME COLUMN uuid_id TO id ;","title":"PostgreSQL"},{"location":"migration/08_to_1x/#mysql","text":"MySQL doesn't support UUID type. We'll just convert the column to CHAR(36) type: ALTER TABLE \"user\" MODIFY id CHAR ( 36 );","title":"MySQL"},{"location":"migration/08_to_1x/#mongodb_1","text":"","title":"MongoDB"},{"location":"migration/08_to_1x/#mongo-shell","text":"For MongoDB, we can use a forEach iterator to convert the id. for each document: db . getCollection ( 'users' ). find (). forEach ( function ( user ) { var uuid = UUID ( user . id ); db . getCollection ( 'users' ). update ({ _id : user . _id }, [{ $set : { id : uuid }}]); });","title":"Mongo shell"},{"location":"migration/08_to_1x/#python","text":"import uuid import motor.motor_asyncio async def migrate_uuid (): client = motor . motor_asyncio . AsyncIOMotorClient ( DATABASE_URL , uuidRepresentation = \"standard\" ) db = client [ \"database_name\" ] users = db [ \"users\" ] async for user in users . find ({}): await users . update_one ( { \"_id\" : user [ \"_id\" ]}, { \"$set\" : { \"id\" : uuid . UUID ( user [ \"id\" ])}}, )","title":"Python"},{"location":"migration/08_to_1x/#splitted-routers","text":"You now have the responsibility to wire the routers . FastAPI Users doesn't give a bloated users router anymore. Event handlers are also removed. You have to provide your \"after-\" logic as a parameter of the router generator.","title":"Splitted routers"},{"location":"migration/08_to_1x/#before","text":"jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . router , prefix = \"/users\" , tags = [ \"users\" ]) @fastapi_users . on_after_register () def on_after_register ( user : User , request : Request ): print ( f \"User { user . id } has registered.\" ) @fastapi_users . on_after_forgot_password () def on_after_forgot_password ( user : User , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" )","title":"Before"},{"location":"migration/08_to_1x/#after","text":"def on_after_register ( user : UserDB , request : Request ): print ( f \"User { user . id } has registered.\" ) def on_after_forgot_password ( user : UserDB , token : str , request : Request ): print ( f \"User { user . id } has forgot their password. Reset token: { token } \" ) jwt_authentication = JWTAuthentication ( secret = SECRET , lifetime_seconds = 3600 ) app = FastAPI () fastapi_users = FastAPIUsers ( user_db , [ jwt_authentication ], User , UserCreate , UserUpdate , UserDB , ) app . include_router ( fastapi_users . get_auth_router ( jwt_authentication ), prefix = \"/auth/jwt\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_register_router ( on_after_register ), prefix = \"/auth\" , tags = [ \"auth\" ] ) app . include_router ( fastapi_users . get_reset_password_router ( SECRET , after_forgot_password = on_after_forgot_password ), prefix = \"/auth\" , tags = [ \"auth\" ], ) app . include_router ( fastapi_users . get_users_router (), prefix = \"/users\" , tags = [ \"users\" ]) Important things to notice: FastAPIUsers takes two arguments less ( reset_password_token_secret and reset_password_token_lifetime_seconds ). You have more flexibility to choose the prefix and tags of the routers. The /login / /logout are now your responsibility to include for each backend. The path will change (before /login/jwt , after /jwt/login ). If you don't care about some of those routers, you can discard them.","title":"After"},{"location":"migration/1x_to_2x/","text":"1.x.x \u27a1\ufe0f 2.x.x \u00b6 JWT authentication backend \u00b6 To be fully compatible with Swagger authentication, the output of a successful login operation with the JWT authentication backend has changed: Before { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } After { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Make sure to update your clients to read the token in the right property.","title":"1.x.x \u27a1\ufe0f 2.x.x"},{"location":"migration/1x_to_2x/#1xx-2xx","text":"","title":"1.x.x \u27a1\ufe0f 2.x.x"},{"location":"migration/1x_to_2x/#jwt-authentication-backend","text":"To be fully compatible with Swagger authentication, the output of a successful login operation with the JWT authentication backend has changed: Before { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } After { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"token_type\" : \"bearer\" } Make sure to update your clients to read the token in the right property.","title":"JWT authentication backend"},{"location":"migration/2x_to_3x/","text":"2.x.x \u27a1\ufe0f 3.x.x \u00b6 Emails are now case-insensitive \u00b6 Before 3.x.x, the local part (before the @) of the email address was case-sensitive. Therefore, king.arthur@camelot.bt and King.Arthur@camelot.bt were considered as two different users . This behaviour was a bit confusing and not consistent with 99% of web services out there. After 3.x.x, users are fetched from the database with a case-insensitive email search. Bear in mind though that if the user registers with the email King.Arthur@camelot.bt , it will be stored exactly like this in the database (with casing) ; but he will be able to login as king.arthur@camelot.bt . Danger It's super important then, before you upgrade to 3.x.x that you check if there are several users with the same email with different cases ; and that you merge or delete those accounts .","title":"2.x.x \u27a1\ufe0f 3.x.x"},{"location":"migration/2x_to_3x/#2xx-3xx","text":"","title":"2.x.x \u27a1\ufe0f 3.x.x"},{"location":"migration/2x_to_3x/#emails-are-now-case-insensitive","text":"Before 3.x.x, the local part (before the @) of the email address was case-sensitive. Therefore, king.arthur@camelot.bt and King.Arthur@camelot.bt were considered as two different users . This behaviour was a bit confusing and not consistent with 99% of web services out there. After 3.x.x, users are fetched from the database with a case-insensitive email search. Bear in mind though that if the user registers with the email King.Arthur@camelot.bt , it will be stored exactly like this in the database (with casing) ; but he will be able to login as king.arthur@camelot.bt . Danger It's super important then, before you upgrade to 3.x.x that you check if there are several users with the same email with different cases ; and that you merge or delete those accounts .","title":"Emails are now case-insensitive"},{"location":"migration/3x_to_4x/","text":"3.x.x \u27a1\ufe0f 4.x.x \u00b6 expires_at property in OAuthAccount is now optional \u00b6 Before 4.x.x, the expires_at property in OAuthAccount model was mandatory. It was causing issues with some services that don't have such expiration property. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"3.x.x \u27a1\ufe0f 4.x.x"},{"location":"migration/3x_to_4x/#3xx-4xx","text":"","title":"3.x.x \u27a1\ufe0f 4.x.x"},{"location":"migration/3x_to_4x/#expires_at-property-in-oauthaccount-is-now-optional","text":"Before 4.x.x, the expires_at property in OAuthAccount model was mandatory. It was causing issues with some services that don't have such expiration property. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"expires_at property in OAuthAccount is now optional"},{"location":"migration/4x_to_5x/","text":"4.x.x \u27a1\ufe0f 5.x.x \u00b6 New property is_verified in User model. \u00b6 Starting 5.x.x., there is a new e-mail verification feature . Even if optional, the is_verified property has been added to the User model. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"4.x.x \u27a1\ufe0f 5.x.x"},{"location":"migration/4x_to_5x/#4xx-5xx","text":"","title":"4.x.x \u27a1\ufe0f 5.x.x"},{"location":"migration/4x_to_5x/#new-property-is_verified-in-user-model","text":"Starting 5.x.x., there is a new e-mail verification feature . Even if optional, the is_verified property has been added to the User model. If you use SQLAlchemy or Tortoise databases adapters, you'll have to make a migration to update your database schema.","title":"New property is_verified in User model."},{"location":"usage/dependency-callables/","text":"Dependency callables \u00b6 FastAPI Users provides a dependency callable to easily inject authenticated user in your routes. They are available from your FastAPIUsers instance. Tip For more information about how to make an authenticated request to your API, check the documentation of your Authentication method . current_user \u00b6 Return a dependency callable to retrieve currently authenticated user, passing the following parameters: optional : If True , None is returned if there is no authenticated user or if it doesn't pass the other requirements. Otherwise, throw 401 Unauthorized . Defaults to False . active : If True , throw 401 Unauthorized if the authenticated user is inactive. Defaults to False . verified : If True , throw 401 Unauthorized if the authenticated user is not verified. Defaults to False . superuser : If True , throw 403 Forbidden if the authenticated user is not a superuser. Defaults to False . Examples \u00b6 Get the current user ( active or not ) \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ())): return f \"Hello, { user . email } \" Get the current active user \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True ))): return f \"Hello, { user . email } \" Get the current active and verified user \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , verified = True ))): return f \"Hello, { user . email } \" Get the current active superuser \u00b6 @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , superuser = True ))): return f \"Hello, { user . email } \" Reuse it \u00b6 If you use it often, you can of course set it in a variable and reuse it at will: current_active_user = fastapi_users . current_user ( active = True ) @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( current_active_user )): return f \"Hello, { user . email } \" Deprecated \u00b6 Warning Those ones are still provided for backward compatibility but are deprecated and will be removed in a future release. get_current_user \u00b6 Get the current user ( active or not ). Will throw a 401 Unauthorized if missing or wrong credentials. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_user )): return f \"Hello, { user . email } \" get_current_active_user \u00b6 Get the current active user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_active_user )): return f \"Hello, { user . email } \" get_current_verified_user \u00b6 Get the current active and verified user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_user )): return f \"Hello, { user . email } \" get_current_superuser \u00b6 Get the current superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_superuser )): return f \"Hello, { user . email } \" get_current_verified_superuser \u00b6 Get the current verified superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_superuser )): return f \"Hello, { user . email } \" get_optional_current_user \u00b6 Get the current user ( active or not ). Will return None if missing or wrong credentials. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : Optional [ User ] = Depends ( fastapi_users . get_optional_current_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_active_user \u00b6 Get the current active user. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_active_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_verified_user \u00b6 Get the current active and verified user. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_superuser \u00b6 Get the current superuser. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" get_optional_current_verified_superuser \u00b6 Get the current active and verified superuser. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\" In path operation \u00b6 If you don't need the user in the route logic, you can use this syntax: @app . get ( \"/protected-route\" , dependencies = [ Depends ( fastapi_users . get_current_superuser )]) def protected_route (): return \"Hello, some user.\" You can read more about this in FastAPI docs .","title":"Dependency callables"},{"location":"usage/dependency-callables/#dependency-callables","text":"FastAPI Users provides a dependency callable to easily inject authenticated user in your routes. They are available from your FastAPIUsers instance. Tip For more information about how to make an authenticated request to your API, check the documentation of your Authentication method .","title":"Dependency callables"},{"location":"usage/dependency-callables/#current_user","text":"Return a dependency callable to retrieve currently authenticated user, passing the following parameters: optional : If True , None is returned if there is no authenticated user or if it doesn't pass the other requirements. Otherwise, throw 401 Unauthorized . Defaults to False . active : If True , throw 401 Unauthorized if the authenticated user is inactive. Defaults to False . verified : If True , throw 401 Unauthorized if the authenticated user is not verified. Defaults to False . superuser : If True , throw 403 Forbidden if the authenticated user is not a superuser. Defaults to False .","title":"current_user"},{"location":"usage/dependency-callables/#examples","text":"","title":"Examples"},{"location":"usage/dependency-callables/#get-the-current-user-active-or-not","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ())): return f \"Hello, { user . email } \"","title":"Get the current user (active or not )"},{"location":"usage/dependency-callables/#get-the-current-active-user","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True ))): return f \"Hello, { user . email } \"","title":"Get the current active user"},{"location":"usage/dependency-callables/#get-the-current-active-and-verified-user","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , verified = True ))): return f \"Hello, { user . email } \"","title":"Get the current active and verified user"},{"location":"usage/dependency-callables/#get-the-current-active-superuser","text":"@app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . current_user ( active = True , superuser = True ))): return f \"Hello, { user . email } \"","title":"Get the current active superuser"},{"location":"usage/dependency-callables/#reuse-it","text":"If you use it often, you can of course set it in a variable and reuse it at will: current_active_user = fastapi_users . current_user ( active = True ) @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( current_active_user )): return f \"Hello, { user . email } \"","title":"Reuse it"},{"location":"usage/dependency-callables/#deprecated","text":"Warning Those ones are still provided for backward compatibility but are deprecated and will be removed in a future release.","title":"Deprecated"},{"location":"usage/dependency-callables/#get_current_user","text":"Get the current user ( active or not ). Will throw a 401 Unauthorized if missing or wrong credentials. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_user )): return f \"Hello, { user . email } \"","title":"get_current_user"},{"location":"usage/dependency-callables/#get_current_active_user","text":"Get the current active user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_active_user )): return f \"Hello, { user . email } \"","title":"get_current_active_user"},{"location":"usage/dependency-callables/#get_current_verified_user","text":"Get the current active and verified user. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_user )): return f \"Hello, { user . email } \"","title":"get_current_verified_user"},{"location":"usage/dependency-callables/#get_current_superuser","text":"Get the current superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_superuser )): return f \"Hello, { user . email } \"","title":"get_current_superuser"},{"location":"usage/dependency-callables/#get_current_verified_superuser","text":"Get the current verified superuser. Will throw a 401 Unauthorized if missing or wrong credentials or if the user is not active and verified. Will throw a 403 Forbidden if the user is not a superuser. @app . get ( \"/protected-route\" ) def protected_route ( user : User = Depends ( fastapi_users . get_current_verified_superuser )): return f \"Hello, { user . email } \"","title":"get_current_verified_superuser"},{"location":"usage/dependency-callables/#get_optional_current_user","text":"Get the current user ( active or not ). Will return None if missing or wrong credentials. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : Optional [ User ] = Depends ( fastapi_users . get_optional_current_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_user"},{"location":"usage/dependency-callables/#get_optional_current_active_user","text":"Get the current active user. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_active_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_active_user"},{"location":"usage/dependency-callables/#get_optional_current_verified_user","text":"Get the current active and verified user. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_user )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_verified_user"},{"location":"usage/dependency-callables/#get_optional_current_superuser","text":"Get the current superuser. Will return None if missing or wrong credentials or if the user is not active. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_superuser"},{"location":"usage/dependency-callables/#get_optional_current_verified_superuser","text":"Get the current active and verified superuser. Will return None if missing or wrong credentials or if the user is not active and verified. It can be useful if you wish to change the behaviour of your endpoint if a user is logged in or not. @app . get ( \"/optional-user-route\" ) def optional_user_route ( user : User = Depends ( fastapi_users . get_optional_current_verified_superuser )): if user : return f \"Hello, { user . email } \" else : return \"Hello, anonymous\"","title":"get_optional_current_verified_superuser"},{"location":"usage/dependency-callables/#in-path-operation","text":"If you don't need the user in the route logic, you can use this syntax: @app . get ( \"/protected-route\" , dependencies = [ Depends ( fastapi_users . get_current_superuser )]) def protected_route (): return \"Hello, some user.\" You can read more about this in FastAPI docs .","title":"In path operation"},{"location":"usage/flow/","text":"Flow \u00b6 This page will present you a complete registration and authentication flow once you've setup FastAPI Users . Each example will be presented with a cURL and an axios example. 1. Registration \u00b6 First step, of course, is to register as a user. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -X POST \\ -d \"{\\\"email\\\": \\\"king.arthur@camelot.bt\\\",\\\"password\\\": \\\"guinevere\\\"}\" \\ http://localhost:8000/auth/register axios axios . post ( 'http://localhost:8000/auth/register' , { email : 'king.arthur@camelot.bt' , password : 'guinevere' , }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Several things to bear in mind: If you have defined other required fields in your User model (like a first name or a birthdate), you'll have to provide them in the payload. The user is active by default. The user cannot set is_active or is_superuser itself at registration. Only a superuser can do it by PATCHing the user. 2. Login \u00b6 Now, you can login as this new user. You can generate a login route for each authentication backend . Each backend will have a different response. JWT backend \u00b6 Request \u00b6 cURL curl \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/jwt/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/jwt/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username . Response \u00b6 You'll get a JSON response looking like this: { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" , \"token_type\" : \"bearer\" } You can use this token to make authenticated requests as the user king.arthur@camelot.bt . We'll see how in the next section. Cookie backend \u00b6 Request \u00b6 cURL curl \\ -v \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/cookie/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/cookie/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username . Response \u00b6 You'll get an empty response. However, the response will come with a Set-Cookie header (that's why we added the -v option in cURL to see them). set-cookie: fastapiusersauth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYzYwNjBmMTEtNTM0OS00YTI0LThiNGEtYTJhODc1ZGM1Mzk1IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4OTQ3fQ.qNA4oPVYhoqrJIk-zvAyEfEVoEnP156G30H_SWEU0sU; HttpOnly; Max-Age=3600; Path=/; Secure You can make authenticated requests as the user king.arthur@camelot.bt by setting a Cookie header with this cookie. Tip The cookie backend is more suited for browsers, as they handle them automatically. This means that if you make a login request in the browser, it will automatically store the cookie and automatically send it in subsequent requests. 3. Get my profile \u00b6 Now that we can authenticate, we can get our own profile data. Depending on your authentication backend , the method to authenticate the request will vary. We'll stick with JWT from now on. Request \u00b6 cURL export TOKEN = \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" ; curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/me axios const TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM' ; axios . get ( 'http://localhost:8000/users/me' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Tip If you use one of the dependency callable to protect one of your own endpoint, you'll have to authenticate exactly in the same way. 4. Update my profile \u00b6 We can also update our own profile. For example, we can change our password like this. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"password\\\": \\\"lancelot\\\"}\" \\ http://localhost:8000/users/me axios axios . patch ( 'http://localhost:8000/users/me' , { password : 'lancelot' , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Once again, the user cannot set is_active or is_superuser itself. Only a superuser can do it by PATCHing the user. 5. Become a superuser \ud83e\uddb8\ud83c\udffb\u200d\u2642\ufe0f \u00b6 If you want to manage the users of your application, you'll have to become a superuser . The very first superuser can only be set at database level : open it through a CLI or a GUI, find your user and set the is_superuser column/property to true . 5.1. Get the profile of any user \u00b6 Now that you are a superuser, you can leverage the power of superuser routes . You can for example get the profile of any user in the database given its id. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . get ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 5.1. Update any user \u00b6 We can now update the profile of any user. For example, we can promote it as superuser. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"is_superuser\\\": true}\" \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . patch ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { is_superuser : true , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : true } 5.2. Delete any user \u00b6 Finally, we can delete a user. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X DELETE \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . delete ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get an empty response. 6. Logout \u00b6 We can also end the session. Note that it doesn't apply to every authentication backends . For JWT, it doesn't make sense to end the session, the token is valid until it expires. However, for Cookie backend, the server will clear the cookie. Request \u00b6 cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Cookie: fastapiusersauth= $TOKEN \" \\ -X POST \\ http://localhost:8000/auth/cookie/logout axios axios . post ( 'http://localhost:8000/auth/cookie/logout' , null , { headers : { 'Cookie' : `fastapiusersauth= ${ TOKEN } ` , }, } ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Response \u00b6 You'll get an empty response. Conclusion \u00b6 That's it! You now have a good overview of how you can manage the users through the API. Be sure to check the Routes page to have all the details about each endpoints.","title":"Flow"},{"location":"usage/flow/#flow","text":"This page will present you a complete registration and authentication flow once you've setup FastAPI Users . Each example will be presented with a cURL and an axios example.","title":"Flow"},{"location":"usage/flow/#1-registration","text":"First step, of course, is to register as a user.","title":"1. Registration"},{"location":"usage/flow/#request","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -X POST \\ -d \"{\\\"email\\\": \\\"king.arthur@camelot.bt\\\",\\\"password\\\": \\\"guinevere\\\"}\" \\ http://localhost:8000/auth/register axios axios . post ( 'http://localhost:8000/auth/register' , { email : 'king.arthur@camelot.bt' , password : 'guinevere' , }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Several things to bear in mind: If you have defined other required fields in your User model (like a first name or a birthdate), you'll have to provide them in the payload. The user is active by default. The user cannot set is_active or is_superuser itself at registration. Only a superuser can do it by PATCHing the user.","title":"Response"},{"location":"usage/flow/#2-login","text":"Now, you can login as this new user. You can generate a login route for each authentication backend . Each backend will have a different response.","title":"2. Login"},{"location":"usage/flow/#jwt-backend","text":"","title":"JWT backend"},{"location":"usage/flow/#request_1","text":"cURL curl \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/jwt/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/jwt/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username .","title":"Request"},{"location":"usage/flow/#response_1","text":"You'll get a JSON response looking like this: { \"access_token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" , \"token_type\" : \"bearer\" } You can use this token to make authenticated requests as the user king.arthur@camelot.bt . We'll see how in the next section.","title":"Response"},{"location":"usage/flow/#cookie-backend","text":"","title":"Cookie backend"},{"location":"usage/flow/#request_2","text":"cURL curl \\ -v \\ -H \"Content-Type: multipart/form-data\" \\ -X POST \\ -F \"username=king.arthur@camelot.bt\" \\ -F \"password=guinevere\" \\ http://localhost:8000/auth/cookie/login axios const formData = new FormData (); formData . set ( 'username' , 'king.arthur@camelot.bt' ); formData . set ( 'password' , 'guinevere' ); axios . post ( 'http://localhost:8000/auth/cookie/login' , formData , { headers : { 'Content-Type' : 'multipart/form-data' , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error )); Warning Notice that we don't send it as a JSON payload here but with form data instead. Also, the email is provided by a field named username .","title":"Request"},{"location":"usage/flow/#response_2","text":"You'll get an empty response. However, the response will come with a Set-Cookie header (that's why we added the -v option in cURL to see them). set-cookie: fastapiusersauth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYzYwNjBmMTEtNTM0OS00YTI0LThiNGEtYTJhODc1ZGM1Mzk1IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4OTQ3fQ.qNA4oPVYhoqrJIk-zvAyEfEVoEnP156G30H_SWEU0sU; HttpOnly; Max-Age=3600; Path=/; Secure You can make authenticated requests as the user king.arthur@camelot.bt by setting a Cookie header with this cookie. Tip The cookie backend is more suited for browsers, as they handle them automatically. This means that if you make a login request in the browser, it will automatically store the cookie and automatically send it in subsequent requests.","title":"Response"},{"location":"usage/flow/#3-get-my-profile","text":"Now that we can authenticate, we can get our own profile data. Depending on your authentication backend , the method to authenticate the request will vary. We'll stick with JWT from now on.","title":"3. Get my profile"},{"location":"usage/flow/#request_3","text":"cURL export TOKEN = \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM\" ; curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/me axios const TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNGZkMzQ3N2ItZWNjZi00ZWUzLThmN2QtNjhhZDcyMjYxNDc2IiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTg3ODE4NDI5fQ.anO3JR8-WYCozZ4_2-PQ2Ov9O38RaLP2RAzQIiZhteM' ; axios . get ( 'http://localhost:8000/users/me' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_3","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Tip If you use one of the dependency callable to protect one of your own endpoint, you'll have to authenticate exactly in the same way.","title":"Response"},{"location":"usage/flow/#4-update-my-profile","text":"We can also update our own profile. For example, we can change our password like this.","title":"4. Update my profile"},{"location":"usage/flow/#request_4","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"password\\\": \\\"lancelot\\\"}\" \\ http://localhost:8000/users/me axios axios . patch ( 'http://localhost:8000/users/me' , { password : 'lancelot' , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_4","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } Info Once again, the user cannot set is_active or is_superuser itself. Only a superuser can do it by PATCHing the user.","title":"Response"},{"location":"usage/flow/#5-become-a-superuser","text":"If you want to manage the users of your application, you'll have to become a superuser . The very first superuser can only be set at database level : open it through a CLI or a GUI, find your user and set the is_superuser column/property to true .","title":"5. Become a superuser \ud83e\uddb8\ud83c\udffb\u200d\u2642\ufe0f"},{"location":"usage/flow/#51-get-the-profile-of-any-user","text":"Now that you are a superuser, you can leverage the power of superuser routes . You can for example get the profile of any user in the database given its id.","title":"5.1. Get the profile of any user"},{"location":"usage/flow/#request_5","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X GET \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . get ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_5","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false }","title":"Response"},{"location":"usage/flow/#51-update-any-user","text":"We can now update the profile of any user. For example, we can promote it as superuser.","title":"5.1. Update any user"},{"location":"usage/flow/#request_6","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X PATCH \\ -d \"{\\\"is_superuser\\\": true}\" \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . patch ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { is_superuser : true , }, { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_6","text":"You'll get a JSON response looking like this: { \"id\" : \"4fd3477b-eccf-4ee3-8f7d-68ad72261476\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : true }","title":"Response"},{"location":"usage/flow/#52-delete-any-user","text":"Finally, we can delete a user.","title":"5.2. Delete any user"},{"location":"usage/flow/#request_7","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Authorization: Bearer $TOKEN \" \\ -X DELETE \\ http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476 axios axios . delete ( 'http://localhost:8000/users/4fd3477b-eccf-4ee3-8f7d-68ad72261476' , { headers : { 'Authorization' : `Bearer ${ TOKEN } ` , }, }, ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_7","text":"You'll get an empty response.","title":"Response"},{"location":"usage/flow/#6-logout","text":"We can also end the session. Note that it doesn't apply to every authentication backends . For JWT, it doesn't make sense to end the session, the token is valid until it expires. However, for Cookie backend, the server will clear the cookie.","title":"6. Logout"},{"location":"usage/flow/#request_8","text":"cURL curl \\ -H \"Content-Type: application/json\" \\ -H \"Cookie: fastapiusersauth= $TOKEN \" \\ -X POST \\ http://localhost:8000/auth/cookie/logout axios axios . post ( 'http://localhost:8000/auth/cookie/logout' , null , { headers : { 'Cookie' : `fastapiusersauth= ${ TOKEN } ` , }, } ) . then (( response ) => console . log ( response )) . catch (( error ) => console . log ( error ));","title":"Request"},{"location":"usage/flow/#response_8","text":"You'll get an empty response.","title":"Response"},{"location":"usage/flow/#conclusion","text":"That's it! You now have a good overview of how you can manage the users through the API. Be sure to check the Routes page to have all the details about each endpoints.","title":"Conclusion"},{"location":"usage/helpers/","text":"Helpers \u00b6 FastAPI Users provides some helper functions to perform some actions programmatically. They are available from your FastAPIUsers instance. Create user \u00b6 Create a user. regular_user = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , ) ) superuser = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , is_superuser = True , ) ) Verify user \u00b6 Verify a user. verified_user = await fastapi_users . verify_user ( non_verified_user ) assert verified_user . is_verified is True Get user \u00b6 Retrieve a user by e-mail. user = await fastapi_users . get_user ( \"king.arthur@camelot.bt\" )","title":"Helpers"},{"location":"usage/helpers/#helpers","text":"FastAPI Users provides some helper functions to perform some actions programmatically. They are available from your FastAPIUsers instance.","title":"Helpers"},{"location":"usage/helpers/#create-user","text":"Create a user. regular_user = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , ) ) superuser = await fastapi_users . create_user ( UserCreate ( email = \"king.arthur@camelot.bt\" , password = \"guinevere\" , is_superuser = True , ) )","title":"Create user"},{"location":"usage/helpers/#verify-user","text":"Verify a user. verified_user = await fastapi_users . verify_user ( non_verified_user ) assert verified_user . is_verified is True","title":"Verify user"},{"location":"usage/helpers/#get-user","text":"Retrieve a user by e-mail. user = await fastapi_users . get_user ( \"king.arthur@camelot.bt\" )","title":"Get user"},{"location":"usage/routes/","text":"Routes \u00b6 You'll find here the routes exposed by FastAPI Users . Note that you can also review them through the interactive API docs . Auth router \u00b6 Each authentication backend you generate a router for will produce the following routes. Take care about the prefix you gave it, especially if you have several backends. POST /login \u00b6 Login a user against the method named name . Check the corresponding authentication method to view the success response. Payload ( application/x-www-form-urlencoded ) username=king.arthur@camelot.bt&password=guinevere 422 Validation Error 400 Bad Request Bad credentials or the user is inactive. { \"detail\" : \"LOGIN_BAD_CREDENTIALS\" } 400 Bad Request The user is not verified. { \"detail\" : \"LOGIN_USER_NOT_VERIFIED\" } POST /logout \u00b6 Logout the authenticated user against the method named name . Check the corresponding authentication method to view the success response. 401 Unauthorized Missing token or inactive user. 200 OK The logout process was successful. Tip Some backend (like JWT) won't produce this route. Register router \u00b6 POST /register \u00b6 Register a new user. Will call the after_register handler on successful registration. Payload { \"email\" : \"king.arthur@camelot.bt\" , \"password\" : \"guinevere\" } 201 Created { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 422 Validation Error 400 Bad Request A user already exists with this email. { \"detail\" : \"REGISTER_USER_ALREADY_EXISTS\" } Reset password router \u00b6 POST /forgot-password \u00b6 Request a reset password procedure. Will generate a temporary token and call the after_forgot_password handler if the user exists. To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted POST /reset-password \u00b6 Reset a password. Requires the token generated by the /forgot-password route. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"password\" : \"merlin\" } 200 OK 422 Validation Error 400 Bad Request Bad or expired token. { \"detail\" : \"RESET_PASSWORD_BAD_TOKEN\" } Verify router \u00b6 POST /request-verify-token \u00b6 Request a user to verify their e-mail. Will generate a temporary token and call the after_verification_request handler if the user exists , active and not already verified . To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist, not active or already verified. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted POST /verify \u00b6 Verify a user. Requires the token generated by the /request-verify-token route. Will call the call the after_verification handler on success. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } 200 OK 422 Validation Error 400 Bad Request Expired token. { \"detail\" : \"VERIFY_USER_TOKEN_EXPIRED\" } 400 Bad Request Bad token, not existing user or not the e-mail currently set for the user. { \"detail\" : \"VERIFY_USER_BAD_TOKEN\" } 400 Bad Request The user is already verified. { \"detail\" : \"VERIFY_USER_ALREADY_VERIFIED\" } OAuth router \u00b6 Each OAuth router you define will expose the two following routes. GET /authorize \u00b6 Return the authorization URL for the OAuth service where you should redirect your user. Query parameters authentication_backend : name property of a defined authentication method to use to authenticate the user on successful callback. Usually jwt or cookie . scopes : Optional list of scopes to ask for. Expected format: scopes=a&scopes=b . 200 OK { \"authorization_url\" : \"https://www.tintagel.bt/oauth/authorize?client_id=CLIENT_ID&scopes=a+b&redirect_uri=https://www.camelot.bt/oauth/callback\" } 422 Validation Error 400 Bad Request Unknown authentication backend. GET /callback \u00b6 Handle the OAuth callback. Query parameters code : OAuth callback code. state : State token. error : OAuth error. Depending on the situation, several things can happen: The OAuth account exists in database and is linked to a user: OAuth account is updated in database with fresh access token. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database but a user with the same email address exists: OAuth account is linked to the user. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database and no user with the email address exists: A new user is created and linked to the OAuth account. The user is authenticated following the chosen authentication method . Users router \u00b6 GET /me \u00b6 Return the current authenticated active user. 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. PATCH /me \u00b6 Update the current authenticated active user. Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@tintagel.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. GET /{user_id} \u00b6 Return the user with id user_id . 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist. PATCH /{user_id} \u00b6 Update the user with id user_id . Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" , \"is_active\" : false , \"is_superuser\" : true } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : false , \"is_superuser\" : true } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist. DELETE /{user_id} \u00b6 Delete the user with id user_id . 204 No content 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"Routes"},{"location":"usage/routes/#routes","text":"You'll find here the routes exposed by FastAPI Users . Note that you can also review them through the interactive API docs .","title":"Routes"},{"location":"usage/routes/#auth-router","text":"Each authentication backend you generate a router for will produce the following routes. Take care about the prefix you gave it, especially if you have several backends.","title":"Auth router"},{"location":"usage/routes/#post-login","text":"Login a user against the method named name . Check the corresponding authentication method to view the success response. Payload ( application/x-www-form-urlencoded ) username=king.arthur@camelot.bt&password=guinevere 422 Validation Error 400 Bad Request Bad credentials or the user is inactive. { \"detail\" : \"LOGIN_BAD_CREDENTIALS\" } 400 Bad Request The user is not verified. { \"detail\" : \"LOGIN_USER_NOT_VERIFIED\" }","title":"POST /login"},{"location":"usage/routes/#post-logout","text":"Logout the authenticated user against the method named name . Check the corresponding authentication method to view the success response. 401 Unauthorized Missing token or inactive user. 200 OK The logout process was successful. Tip Some backend (like JWT) won't produce this route.","title":"POST /logout"},{"location":"usage/routes/#register-router","text":"","title":"Register router"},{"location":"usage/routes/#post-register","text":"Register a new user. Will call the after_register handler on successful registration. Payload { \"email\" : \"king.arthur@camelot.bt\" , \"password\" : \"guinevere\" } 201 Created { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 422 Validation Error 400 Bad Request A user already exists with this email. { \"detail\" : \"REGISTER_USER_ALREADY_EXISTS\" }","title":"POST /register"},{"location":"usage/routes/#reset-password-router","text":"","title":"Reset password router"},{"location":"usage/routes/#post-forgot-password","text":"Request a reset password procedure. Will generate a temporary token and call the after_forgot_password handler if the user exists. To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted","title":"POST /forgot-password"},{"location":"usage/routes/#post-reset-password","text":"Reset a password. Requires the token generated by the /forgot-password route. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" , \"password\" : \"merlin\" } 200 OK 422 Validation Error 400 Bad Request Bad or expired token. { \"detail\" : \"RESET_PASSWORD_BAD_TOKEN\" }","title":"POST /reset-password"},{"location":"usage/routes/#verify-router","text":"","title":"Verify router"},{"location":"usage/routes/#post-request-verify-token","text":"Request a user to verify their e-mail. Will generate a temporary token and call the after_verification_request handler if the user exists , active and not already verified . To prevent malicious users from guessing existing users in your database, the route will always return a 202 Accepted response, even if the user requested does not exist, not active or already verified. Payload { \"email\" : \"king.arthur@camelot.bt\" } 202 Accepted","title":"POST /request-verify-token"},{"location":"usage/routes/#post-verify","text":"Verify a user. Requires the token generated by the /request-verify-token route. Will call the call the after_verification handler on success. Payload { \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOTIyMWZmYzktNjQwZi00MzcyLTg2ZDMtY2U2NDJjYmE1NjAzIiwiYXVkIjoiZmFzdGFwaS11c2VyczphdXRoIiwiZXhwIjoxNTcxNTA0MTkzfQ.M10bjOe45I5Ncu_uXvOmVV8QxnL-nZfcH96U90JaocI\" } 200 OK 422 Validation Error 400 Bad Request Expired token. { \"detail\" : \"VERIFY_USER_TOKEN_EXPIRED\" } 400 Bad Request Bad token, not existing user or not the e-mail currently set for the user. { \"detail\" : \"VERIFY_USER_BAD_TOKEN\" } 400 Bad Request The user is already verified. { \"detail\" : \"VERIFY_USER_ALREADY_VERIFIED\" }","title":"POST /verify"},{"location":"usage/routes/#oauth-router","text":"Each OAuth router you define will expose the two following routes.","title":"OAuth router"},{"location":"usage/routes/#get-authorize","text":"Return the authorization URL for the OAuth service where you should redirect your user. Query parameters authentication_backend : name property of a defined authentication method to use to authenticate the user on successful callback. Usually jwt or cookie . scopes : Optional list of scopes to ask for. Expected format: scopes=a&scopes=b . 200 OK { \"authorization_url\" : \"https://www.tintagel.bt/oauth/authorize?client_id=CLIENT_ID&scopes=a+b&redirect_uri=https://www.camelot.bt/oauth/callback\" } 422 Validation Error 400 Bad Request Unknown authentication backend.","title":"GET /authorize"},{"location":"usage/routes/#get-callback","text":"Handle the OAuth callback. Query parameters code : OAuth callback code. state : State token. error : OAuth error. Depending on the situation, several things can happen: The OAuth account exists in database and is linked to a user: OAuth account is updated in database with fresh access token. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database but a user with the same email address exists: OAuth account is linked to the user. The user is authenticated following the chosen authentication method . The OAuth account doesn't exist in database and no user with the email address exists: A new user is created and linked to the OAuth account. The user is authenticated following the chosen authentication method .","title":"GET /callback"},{"location":"usage/routes/#users-router","text":"","title":"Users router"},{"location":"usage/routes/#get-me","text":"Return the current authenticated active user. 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user.","title":"GET /me"},{"location":"usage/routes/#patch-me","text":"Update the current authenticated active user. Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@tintagel.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user.","title":"PATCH /me"},{"location":"usage/routes/#get-user_id","text":"Return the user with id user_id . 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : true , \"is_superuser\" : false } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"GET /{user_id}"},{"location":"usage/routes/#patch-user_id","text":"Update the user with id user_id . Payload { \"email\" : \"king.arthur@tintagel.bt\" , \"password\" : \"merlin\" , \"is_active\" : false , \"is_superuser\" : true } 200 OK { \"id\" : \"57cbb51a-ab71-4009-8802-3f54b4f2e23\" , \"email\" : \"king.arthur@camelot.bt\" , \"is_active\" : false , \"is_superuser\" : true } 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"PATCH /{user_id}"},{"location":"usage/routes/#delete-user_id","text":"Delete the user with id user_id . 204 No content 401 Unauthorized Missing token or inactive user. 403 Forbidden Not a superuser. 404 Not found The user does not exist.","title":"DELETE /{user_id}"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index ec2b8865..465472f3 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,111 +1,111 @@ None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily None - 2021-04-20 + 2021-05-02 daily \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 8230400f..7fff5b1b 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/usage/dependency-callables/index.html b/usage/dependency-callables/index.html index cb11ee1d..415d9ac8 100644 --- a/usage/dependency-callables/index.html +++ b/usage/dependency-callables/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/usage/flow/index.html b/usage/flow/index.html index c4e33ccc..3471c7dd 100644 --- a/usage/flow/index.html +++ b/usage/flow/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/usage/helpers/index.html b/usage/helpers/index.html index b7adcea5..d6ad28d4 100644 --- a/usage/helpers/index.html +++ b/usage/helpers/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/usage/routes/index.html b/usage/routes/index.html index bc50cef5..5a773f5d 100644 --- a/usage/routes/index.html +++ b/usage/routes/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - +