From bff57363c46fe1bb2945c279dd2a249ccdb1fe57 Mon Sep 17 00:00:00 2001 From: William Kennedy Date: Mon, 22 Jan 2024 11:36:19 -0500 Subject: [PATCH] upgraded routes code --- app/services/engine/handlers/handlers.go | 139 -- .../engine/handlers/v1/checkgrp/checkgrp.go | 96 -- app/services/engine/main.go | 43 +- app/services/engine/v1/build/all/all.go | 37 + .../engine/v1/handlers/checkgrp/checkgrp.go | 77 + .../engine/v1/handlers/checkgrp/route.go | 23 + .../v1 => v1/handlers}/gamegrp/gamegrp.go | 89 +- .../v1 => v1/handlers}/gamegrp/models.go | 0 .../v1/v1.go => v1/handlers/gamegrp/route.go} | 40 +- business/web/v1/debug/debug.go | 29 + business/web/{ => v1}/metrics/metrics.go | 36 +- business/web/v1/mid/cors.go | 7 +- business/web/v1/mid/metrics.go | 2 +- business/web/v1/mid/panics.go | 2 +- business/web/v1/mux/mux.go | 74 + business/web/v1/order/order.go | 74 - foundation/events/events.go | 2 +- foundation/web/context.go | 4 + foundation/web/middleware.go | 6 +- foundation/web/request.go | 15 +- foundation/web/web.go | 70 +- go.mod | 1 + go.sum | 2 + vendor/github.com/ardanlabs/conf/v3/conf.go | 6 + vendor/github.com/arl/statsviz/.gitattributes | 1 + vendor/github.com/arl/statsviz/.gitignore | 2 + vendor/github.com/arl/statsviz/CHANGELOG.md | 85 ++ .../github.com/arl/statsviz/CONTRIBUTING.md | 66 + vendor/github.com/arl/statsviz/LICENSE | 21 + vendor/github.com/arl/statsviz/README.md | 230 +++ vendor/github.com/arl/statsviz/codecov.yml | 14 + .../arl/statsviz/internal/plot/color.go | 64 + .../arl/statsviz/internal/plot/hist.go | 87 ++ .../arl/statsviz/internal/plot/layout.go | 69 + .../arl/statsviz/internal/plot/plots.go | 1320 +++++++++++++++++ .../arl/statsviz/internal/plot/plots_list.go | 162 ++ .../arl/statsviz/internal/plot/user.go | 48 + .../statsviz/internal/static/css/statsviz.css | 62 + .../arl/statsviz/internal/static/fs.go | 6 + .../arl/statsviz/internal/static/index.html | 61 + .../arl/statsviz/internal/static/js/app.js | 145 ++ .../arl/statsviz/internal/static/js/buffer.js | 46 + .../arl/statsviz/internal/static/js/plot.js | 400 +++++ .../arl/statsviz/internal/static/js/stats.js | 95 ++ .../arl/statsviz/internal/static/js/theme.js | 36 + .../static/libs/css/bootstrap-toggle.min.css | 28 + .../static/libs/css/bootstrap.min.css | 6 + .../libs/css/font.awesome-6.1.2-all.min.css | 6 + .../static/libs/js/bootstrap-toggle.min.js | 8 + .../static/libs/js/bootstrap.bundle.min.js | 6 + .../libs/js/font.awesome-6.1.2-all.min.js | 6 + .../static/libs/js/jquery-3.6.0.min.js | 2 + .../static/libs/js/plotly-basic-2.12.1.min.js | 24 + .../libs/js/plotly-cartesian-2.12.1.min.js | 37 + .../internal/static/libs/js/popperjs-core2 | 5 + .../internal/static/libs/js/tippy.js@6 | 1 + .../static/libs/webfonts/fa-solid-900.woff2 | Bin 0 -> 79072 bytes vendor/github.com/arl/statsviz/statsviz.go | 283 ++++ vendor/github.com/arl/statsviz/userplot.go | 183 +++ vendor/modules.txt | 5 + 60 files changed, 4047 insertions(+), 447 deletions(-) delete mode 100644 app/services/engine/handlers/handlers.go delete mode 100644 app/services/engine/handlers/v1/checkgrp/checkgrp.go create mode 100644 app/services/engine/v1/build/all/all.go create mode 100644 app/services/engine/v1/handlers/checkgrp/checkgrp.go create mode 100644 app/services/engine/v1/handlers/checkgrp/route.go rename app/services/engine/{handlers/v1 => v1/handlers}/gamegrp/gamegrp.go (83%) rename app/services/engine/{handlers/v1 => v1/handlers}/gamegrp/models.go (100%) rename app/services/engine/{handlers/v1/v1.go => v1/handlers/gamegrp/route.go} (56%) create mode 100644 business/web/v1/debug/debug.go rename business/web/{ => v1}/metrics/metrics.go (72%) create mode 100644 business/web/v1/mux/mux.go delete mode 100644 business/web/v1/order/order.go create mode 100644 vendor/github.com/arl/statsviz/.gitattributes create mode 100644 vendor/github.com/arl/statsviz/.gitignore create mode 100644 vendor/github.com/arl/statsviz/CHANGELOG.md create mode 100644 vendor/github.com/arl/statsviz/CONTRIBUTING.md create mode 100644 vendor/github.com/arl/statsviz/LICENSE create mode 100644 vendor/github.com/arl/statsviz/README.md create mode 100644 vendor/github.com/arl/statsviz/codecov.yml create mode 100644 vendor/github.com/arl/statsviz/internal/plot/color.go create mode 100644 vendor/github.com/arl/statsviz/internal/plot/hist.go create mode 100644 vendor/github.com/arl/statsviz/internal/plot/layout.go create mode 100644 vendor/github.com/arl/statsviz/internal/plot/plots.go create mode 100644 vendor/github.com/arl/statsviz/internal/plot/plots_list.go create mode 100644 vendor/github.com/arl/statsviz/internal/plot/user.go create mode 100644 vendor/github.com/arl/statsviz/internal/static/css/statsviz.css create mode 100644 vendor/github.com/arl/statsviz/internal/static/fs.go create mode 100644 vendor/github.com/arl/statsviz/internal/static/index.html create mode 100644 vendor/github.com/arl/statsviz/internal/static/js/app.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/js/buffer.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/js/plot.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/js/stats.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/js/theme.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap-toggle.min.css create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap.min.css create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/css/font.awesome-6.1.2-all.min.css create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap-toggle.min.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap.bundle.min.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/font.awesome-6.1.2-all.min.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/jquery-3.6.0.min.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/plotly-basic-2.12.1.min.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/plotly-cartesian-2.12.1.min.js create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/popperjs-core2 create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/js/tippy.js@6 create mode 100644 vendor/github.com/arl/statsviz/internal/static/libs/webfonts/fa-solid-900.woff2 create mode 100644 vendor/github.com/arl/statsviz/statsviz.go create mode 100644 vendor/github.com/arl/statsviz/userplot.go diff --git a/app/services/engine/handlers/handlers.go b/app/services/engine/handlers/handlers.go deleted file mode 100644 index e08d352a..00000000 --- a/app/services/engine/handlers/handlers.go +++ /dev/null @@ -1,139 +0,0 @@ -// Package handlers manages the different versions of the API. -package handlers - -import ( - "context" - "expvar" - "net/http" - "net/http/pprof" - "os" - "time" - - "github.com/ardanlabs/ethereum/currency" - v1 "github.com/ardanlabs/liarsdice/app/services/engine/handlers/v1" - "github.com/ardanlabs/liarsdice/app/services/engine/handlers/v1/checkgrp" - "github.com/ardanlabs/liarsdice/business/core/bank" - "github.com/ardanlabs/liarsdice/business/web/v1/auth" - "github.com/ardanlabs/liarsdice/business/web/v1/mid" - "github.com/ardanlabs/liarsdice/foundation/events" - "github.com/ardanlabs/liarsdice/foundation/logger" - "github.com/ardanlabs/liarsdice/foundation/web" -) - -// Options represent optional parameters. -type Options struct { - corsOrigin string -} - -// WithCORS provides configuration options for CORS. -func WithCORS(origin string) func(opts *Options) { - return func(opts *Options) { - opts.corsOrigin = origin - } -} - -// APIMuxConfig contains all the mandatory systems required by handlers. -type APIMuxConfig struct { - Shutdown chan os.Signal - Log *logger.Logger - Auth *auth.Auth - Converter *currency.Converter - Bank *bank.Bank - Evts *events.Events - AnteUSD float64 - ActiveKID string - BankTimeout time.Duration - ConnectTimeout time.Duration -} - -// APIMux constructs a http.Handler with all application routes defined. -func APIMux(cfg APIMuxConfig, options ...func(opts *Options)) http.Handler { - var opts Options - for _, option := range options { - option(&opts) - } - - // Construct the web.App which holds all routes as well as common Middleware. - var app *web.App - - // Do we need CORS? - if opts.corsOrigin != "" { - app = web.NewApp( - cfg.Shutdown, - mid.Logger(cfg.Log), - mid.Errors(cfg.Log), - mid.Metrics(), - mid.Cors(opts.corsOrigin), - mid.Panics(), - ) - - // Accept CORS 'OPTIONS' preflight requests if config has been provided. - // Don't forget to apply the CORS middleware to the routes that need it. - // Example Config: `conf:"default:https://MY_DOMAIN.COM"` - h := func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - return nil - } - app.Handle(http.MethodOptions, "", "/*", h, mid.Cors(opts.corsOrigin)) - } - - if app == nil { - app = web.NewApp( - cfg.Shutdown, - mid.Logger(cfg.Log), - mid.Errors(cfg.Log), - mid.Metrics(), - mid.Panics(), - ) - } - - // Load the v1 routes. - v1.Routes(app, v1.Config{ - Log: cfg.Log, - Auth: cfg.Auth, - Converter: cfg.Converter, - Bank: cfg.Bank, - Evts: cfg.Evts, - AnteUSD: cfg.AnteUSD, - ActiveKID: cfg.ActiveKID, - BankTimeout: cfg.BankTimeout, - ConnectTimeout: cfg.ConnectTimeout, - }) - - return app -} - -// DebugStandardLibraryMux registers all the debug routes from the standard library -// into a new mux bypassing the use of the DefaultServerMux. Using the -// DefaultServerMux would be a security risk since a dependency could inject a -// handler into our service without us knowing it. -func DebugStandardLibraryMux() *http.ServeMux { - mux := http.NewServeMux() - - // Register all the standard library debug endpoints. - mux.HandleFunc("/debug/pprof/", pprof.Index) - mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) - mux.HandleFunc("/debug/pprof/profile", pprof.Profile) - mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) - mux.HandleFunc("/debug/pprof/trace", pprof.Trace) - mux.Handle("/debug/vars", expvar.Handler()) - - return mux -} - -// DebugMux registers all the debug standard library routes and then custom -// debug application routes for the service. This bypassing the use of the -// DefaultServerMux. Using the DefaultServerMux would be a security risk since -// a dependency could inject a handler into our service without us knowing it. -func DebugMux(build string, log *logger.Logger) http.Handler { - mux := DebugStandardLibraryMux() - - // Register debug check endpoints. - cgh := checkgrp.Handlers{ - Build: build, - Log: log, - } - mux.HandleFunc("/debug/readiness", cgh.Readiness) - mux.HandleFunc("/debug/liveness", cgh.Liveness) - - return mux -} diff --git a/app/services/engine/handlers/v1/checkgrp/checkgrp.go b/app/services/engine/handlers/v1/checkgrp/checkgrp.go deleted file mode 100644 index 15ee2841..00000000 --- a/app/services/engine/handlers/v1/checkgrp/checkgrp.go +++ /dev/null @@ -1,96 +0,0 @@ -// Package checkgrp maintains the group of handlers for health checking. -package checkgrp - -import ( - "encoding/json" - "net/http" - "os" - - "github.com/ardanlabs/liarsdice/foundation/logger" -) - -// Handlers manages the set of check endpoints. -type Handlers struct { - Build string - Log *logger.Logger -} - -// Readiness checks if the database is ready and if not will return a 500 status. -// Do not respond by just returning an error because further up in the call -// stack it will interpret that as a non-trusted error. -func (h Handlers) Readiness(w http.ResponseWriter, r *http.Request) { - status := "ok" - statusCode := http.StatusOK - - data := struct { - Status string `json:"status"` - }{ - Status: status, - } - - if err := response(w, statusCode, data); err != nil { - h.Log.Error(r.Context(), "readiness", "ERROR", err) - } - - h.Log.Info(r.Context(), "readiness", "statusCode", statusCode, "method", r.Method, "path", r.URL.Path, "remoteaddr", r.RemoteAddr) -} - -// Liveness returns simple status info if the service is alive. If the -// app is deployed to a Kubernetes cluster, it will also return pod, node, and -// namespace details via the Downward API. The Kubernetes environment variables -// need to be set within your Pod/Deployment manifest. -func (h Handlers) Liveness(w http.ResponseWriter, r *http.Request) { - host, err := os.Hostname() - if err != nil { - host = "unavailable" - } - - data := struct { - Status string `json:"status,omitempty"` - Build string `json:"build,omitempty"` - Host string `json:"host,omitempty"` - Pod string `json:"pod,omitempty"` - PodIP string `json:"podIP,omitempty"` - Node string `json:"node,omitempty"` - Namespace string `json:"namespace,omitempty"` - }{ - Status: "up", - Build: h.Build, - Host: host, - Pod: os.Getenv("KUBERNETES_PODNAME"), - PodIP: os.Getenv("KUBERNETES_NAMESPACE_POD_IP"), - Node: os.Getenv("KUBERNETES_NODENAME"), - Namespace: os.Getenv("KUBERNETES_NAMESPACE"), - } - - statusCode := http.StatusOK - if err := response(w, statusCode, data); err != nil { - h.Log.Error(r.Context(), "liveness", "ERROR", err) - } - - // THIS IS A FREE TIMER. WE COULD UPDATE THE METRIC GOROUTINE COUNT HERE. - - h.Log.Info(r.Context(), "liveness", "statusCode", statusCode, "method", r.Method, "path", r.URL.Path, "remoteaddr", r.RemoteAddr) -} - -func response(w http.ResponseWriter, statusCode int, data any) error { - - // Convert the response value to JSON. - jsonData, err := json.Marshal(data) - if err != nil { - return err - } - - // Set the content type and headers once we know marshaling has succeeded. - w.Header().Set("Content-Type", "application/json") - - // Write the status code to the response. - w.WriteHeader(statusCode) - - // Send the result back to the client. - if _, err := w.Write(jsonData); err != nil { - return err - } - - return nil -} diff --git a/app/services/engine/main.go b/app/services/engine/main.go index c0c466e9..4ddb43d6 100644 --- a/app/services/engine/main.go +++ b/app/services/engine/main.go @@ -16,10 +16,12 @@ import ( "github.com/ardanlabs/conf/v3" "github.com/ardanlabs/ethereum" "github.com/ardanlabs/ethereum/currency" - "github.com/ardanlabs/liarsdice/app/services/engine/handlers" + "github.com/ardanlabs/liarsdice/app/services/engine/v1/build/all" scbank "github.com/ardanlabs/liarsdice/business/contract/go/bank" "github.com/ardanlabs/liarsdice/business/core/bank" "github.com/ardanlabs/liarsdice/business/web/v1/auth" + "github.com/ardanlabs/liarsdice/business/web/v1/debug" + "github.com/ardanlabs/liarsdice/business/web/v1/mux" "github.com/ardanlabs/liarsdice/foundation/events" "github.com/ardanlabs/liarsdice/foundation/keystore" "github.com/ardanlabs/liarsdice/foundation/logger" @@ -82,12 +84,13 @@ func run(ctx context.Context, log *logger.Logger) error { cfg := struct { conf.Version Web struct { - ReadTimeout time.Duration `conf:"default:5s"` - WriteTimeout time.Duration `conf:"default:10s"` - IdleTimeout time.Duration `conf:"default:120s"` - ShutdownTimeout time.Duration `conf:"default:20s"` - APIHost string `conf:"default:0.0.0.0:3000"` - DebugHost string `conf:"default:0.0.0.0:4000"` + ReadTimeout time.Duration `conf:"default:5s"` + WriteTimeout time.Duration `conf:"default:10s"` + IdleTimeout time.Duration `conf:"default:120s"` + ShutdownTimeout time.Duration `conf:"default:20s"` + APIHost string `conf:"default:0.0.0.0:3000"` + DebugHost string `conf:"default:0.0.0.0:4000"` + CORSAllowedOrigins []string `conf:"default:*"` } Vault struct { Address string `conf:"default:http://vault-service.liars-system.svc.cluster.local:8200"` @@ -219,16 +222,8 @@ func run(ctx context.Context, log *logger.Logger) error { log.Info(ctx, "startup", "status", "debug v1 router started", "host", cfg.Web.DebugHost) - // The Debug function returns a mux to listen and serve on for all the debug - // related endpoints. This includes the standard library endpoints. - - // Construct the mux for the debug calls. - debugMux := handlers.DebugMux(build, log) - - // Start the service listening for debug requests. - // Not concerned with shutting this down with load shedding. go func() { - if err := http.ListenAndServe(cfg.Web.DebugHost, debugMux); err != nil { + if err := http.ListenAndServe(cfg.Web.DebugHost, debug.Mux()); err != nil { log.Error(ctx, "shutdown", "status", "debug v1 router closed", "host", cfg.Web.DebugHost, "ERROR", err) } }() @@ -238,13 +233,10 @@ func run(ctx context.Context, log *logger.Logger) error { log.Info(ctx, "startup", "status", "initializing V1 API support") - // Make a channel to listen for an interrupt or terminate signal from the OS. - // Use a buffered channel because the signal package requires it. shutdown := make(chan os.Signal, 1) signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM) - // Construct the mux for the API calls. - apiMux := handlers.APIMux(handlers.APIMuxConfig{ + cfgMux := mux.Config{ Shutdown: shutdown, Log: log, Auth: authClient, @@ -255,23 +247,19 @@ func run(ctx context.Context, log *logger.Logger) error { ActiveKID: cfg.Auth.ActiveKID, BankTimeout: cfg.Bank.Timeout, ConnectTimeout: cfg.Game.ConnectTimeout, - }, handlers.WithCORS("*")) + } - // Construct a server to service the requests against the mux. api := http.Server{ Addr: cfg.Web.APIHost, - Handler: apiMux, + Handler: mux.WebAPI(cfgMux, all.Routes(), mux.WithCORS(cfg.Web.CORSAllowedOrigins)), ReadTimeout: cfg.Web.ReadTimeout, WriteTimeout: cfg.Web.WriteTimeout, IdleTimeout: cfg.Web.IdleTimeout, ErrorLog: logger.NewStdLogger(log, logger.LevelError), } - // Make a channel to listen for errors coming from the listener. Use a - // buffered channel so the goroutine can exit if we don't collect this error. serverErrors := make(chan error, 1) - // Start the service listening for api requests. go func() { log.Info(ctx, "startup", "status", "api router started", "host", api.Addr) serverErrors <- api.ListenAndServe() @@ -280,7 +268,6 @@ func run(ctx context.Context, log *logger.Logger) error { // ------------------------------------------------------------------------- // Shutdown - // Blocking main and waiting for shutdown. select { case err := <-serverErrors: return fmt.Errorf("server error: %w", err) @@ -293,11 +280,9 @@ func run(ctx context.Context, log *logger.Logger) error { log.Info(ctx, "shutdown", "status", "shutdown web socket channels") evts.Shutdown() - // Give outstanding requests a deadline for completion. ctx, cancel := context.WithTimeout(context.Background(), cfg.Web.ShutdownTimeout) defer cancel() - // Asking listener to shut down and shed load. if err := api.Shutdown(ctx); err != nil { api.Close() return fmt.Errorf("could not stop server gracefully: %w", err) diff --git a/app/services/engine/v1/build/all/all.go b/app/services/engine/v1/build/all/all.go new file mode 100644 index 00000000..1ee3adba --- /dev/null +++ b/app/services/engine/v1/build/all/all.go @@ -0,0 +1,37 @@ +// Package all binds all the routes into the specified app. +package all + +import ( + "github.com/ardanlabs/liarsdice/app/services/engine/v1/handlers/checkgrp" + "github.com/ardanlabs/liarsdice/app/services/engine/v1/handlers/gamegrp" + "github.com/ardanlabs/liarsdice/business/web/v1/mux" + "github.com/ardanlabs/liarsdice/foundation/web" +) + +// Routes constructs the add value which provides the implementation of +// of RouteAdder for specifying what routes to bind to this instance. +func Routes() add { + return add{} +} + +type add struct{} + +// Add implements the RouterAdder interface. +func (add) Add(app *web.App, cfg mux.Config) { + checkgrp.Routes(app, checkgrp.Config{ + Build: cfg.Build, + Log: cfg.Log, + }) + + gamegrp.Routes(app, gamegrp.Config{ + Log: cfg.Log, + Auth: cfg.Auth, + Converter: cfg.Converter, + Bank: cfg.Bank, + Evts: cfg.Evts, + AnteUSD: cfg.AnteUSD, + ActiveKID: cfg.ActiveKID, + BankTimeout: cfg.BankTimeout, + ConnectTimeout: cfg.ConnectTimeout, + }) +} diff --git a/app/services/engine/v1/handlers/checkgrp/checkgrp.go b/app/services/engine/v1/handlers/checkgrp/checkgrp.go new file mode 100644 index 00000000..780fba88 --- /dev/null +++ b/app/services/engine/v1/handlers/checkgrp/checkgrp.go @@ -0,0 +1,77 @@ +// Package checkgrp maintains the group of handlers for health checking. +package checkgrp + +import ( + "context" + "net/http" + "os" + "runtime" + "time" + + "github.com/ardanlabs/liarsdice/foundation/logger" + "github.com/ardanlabs/liarsdice/foundation/web" +) + +type handlers struct { + build string + log *logger.Logger +} + +func new(build string, log *logger.Logger) *handlers { + return &handlers{ + build: build, + log: log, + } +} + +// readiness checks if the database is ready and if not will return a 500 status. +// Do not respond by just returning an error because further up in the call +// stack it will interpret that as a non-trusted error. +func (h *handlers) readiness(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + status := "ok" + statusCode := http.StatusOK + + data := struct { + Status string `json:"status"` + }{ + Status: status, + } + + return web.Respond(ctx, w, data, statusCode) +} + +// liveness returns simple status info if the service is alive. If the +// app is deployed to a Kubernetes cluster, it will also return pod, node, and +// namespace details via the Downward API. The Kubernetes environment variables +// need to be set within your Pod/Deployment manifest. +func (h *handlers) liveness(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + host, err := os.Hostname() + if err != nil { + host = "unavailable" + } + + data := struct { + Status string `json:"status,omitempty"` + Build string `json:"build,omitempty"` + Host string `json:"host,omitempty"` + Name string `json:"name,omitempty"` + PodIP string `json:"podIP,omitempty"` + Node string `json:"node,omitempty"` + Namespace string `json:"namespace,omitempty"` + GOMAXPROCS int `json:"GOMAXPROCS,omitempty"` + }{ + Status: "up", + Build: h.build, + Host: host, + Name: os.Getenv("KUBERNETES_NAME"), + PodIP: os.Getenv("KUBERNETES_POD_IP"), + Node: os.Getenv("KUBERNETES_NODE_NAME"), + Namespace: os.Getenv("KUBERNETES_NAMESPACE"), + GOMAXPROCS: runtime.GOMAXPROCS(0), + } + + return web.Respond(ctx, w, data, http.StatusOK) +} diff --git a/app/services/engine/v1/handlers/checkgrp/route.go b/app/services/engine/v1/handlers/checkgrp/route.go new file mode 100644 index 00000000..993a6b7b --- /dev/null +++ b/app/services/engine/v1/handlers/checkgrp/route.go @@ -0,0 +1,23 @@ +package checkgrp + +import ( + "net/http" + + "github.com/ardanlabs/liarsdice/foundation/logger" + "github.com/ardanlabs/liarsdice/foundation/web" +) + +// Config contains all the mandatory systems required by handlers. +type Config struct { + Build string + Log *logger.Logger +} + +// Routes adds specific routes for this group. +func Routes(app *web.App, cfg Config) { + const version = "v1" + + hdl := new(cfg.Build, cfg.Log) + app.HandleNoMiddleware(http.MethodGet, version, "/readiness", hdl.readiness) + app.HandleNoMiddleware(http.MethodGet, version, "/liveness", hdl.liveness) +} diff --git a/app/services/engine/handlers/v1/gamegrp/gamegrp.go b/app/services/engine/v1/handlers/gamegrp/gamegrp.go similarity index 83% rename from app/services/engine/handlers/v1/gamegrp/gamegrp.go rename to app/services/engine/v1/handlers/gamegrp/gamegrp.go index 2a96fb0a..cfee82f1 100644 --- a/app/services/engine/handlers/v1/gamegrp/gamegrp.go +++ b/app/services/engine/v1/handlers/gamegrp/gamegrp.go @@ -27,8 +27,7 @@ import ( "github.com/gorilla/websocket" ) -// Handlers manages the set of user endpoints. -type Handlers struct { +type handlers struct { Converter *currency.Converter Bank *bank.Bank Log *logger.Logger @@ -44,8 +43,8 @@ type Handlers struct { mu sync.RWMutex } -// Connect is used to return a game token for API usage. -func (h *Handlers) Connect(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// connect is used to return a game token for API usage. +func (h *handlers) connect(ctx context.Context, w http.ResponseWriter, r *http.Request) error { address, err := validateSignature(r, h.ConnectTimeout) if err != nil { return v1.NewTrustedError(err, http.StatusBadRequest) @@ -67,8 +66,8 @@ func (h *Handlers) Connect(ctx context.Context, w http.ResponseWriter, r *http.R return web.Respond(ctx, w, data, http.StatusOK) } -// Events handles a web socket to provide events to a client. -func (h *Handlers) Events(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// events handles a web socket to provide events to a client. +func (h *handlers) events(ctx context.Context, w http.ResponseWriter, r *http.Request) error { v := web.GetValues(ctx) // Need this to handle CORS on the websocket. @@ -151,8 +150,8 @@ func (h *Handlers) Events(ctx context.Context, w http.ResponseWriter, r *http.Re } } -// Configuration returns the basic configuration the front end needs to use. -func (h *Handlers) Configuration(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// configuration returns the basic configuration the front end needs to use. +func (h *handlers) configuration(ctx context.Context, w http.ResponseWriter, r *http.Request) error { info := struct { Network string `json:"network"` ChainID int `json:"chainId"` @@ -166,9 +165,9 @@ func (h *Handlers) Configuration(ctx context.Context, w http.ResponseWriter, r * return web.Respond(ctx, w, info, http.StatusOK) } -// USD2Wei converts the us dollar amount to wei based on the game engine's +// usd2Wei converts the us dollar amount to wei based on the game engine's // conversion rate. -func (h *Handlers) USD2Wei(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +func (h *handlers) usd2Wei(ctx context.Context, w http.ResponseWriter, r *http.Request) error { usd, err := strconv.ParseFloat(web.Param(r, "usd"), 64) if err != nil { return v1.NewTrustedError(fmt.Errorf("converting usd: %s", err), http.StatusBadRequest) @@ -187,8 +186,8 @@ func (h *Handlers) USD2Wei(ctx context.Context, w http.ResponseWriter, r *http.R return web.Respond(ctx, w, data, http.StatusOK) } -// Status will return information about the game. -func (h *Handlers) Status(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// status will return information about the game. +func (h *handlers) status(ctx context.Context, w http.ResponseWriter, r *http.Request) error { claims := mid.GetClaims(ctx) address := common.HexToAddress(claims.Subject) @@ -236,9 +235,9 @@ func (h *Handlers) Status(ctx context.Context, w http.ResponseWriter, r *http.Re return web.Respond(ctx, w, resp, http.StatusOK) } -// NewGame creates a new game if there is no game or the status of the current game +// newGame creates a new game if there is no game or the status of the current game // is GameOver. -func (h *Handlers) NewGame(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +func (h *handlers) newGame(ctx context.Context, w http.ResponseWriter, r *http.Request) error { claims := mid.GetClaims(ctx) address := claims.Subject @@ -248,11 +247,11 @@ func (h *Handlers) NewGame(ctx context.Context, w http.ResponseWriter, r *http.R h.Evts.Send(fmt.Sprintf(`{"type":"newgame","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// Join adds the given player to the game. -func (h *Handlers) Join(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// join adds the given player to the game. +func (h *handlers) join(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -267,11 +266,11 @@ func (h *Handlers) Join(ctx context.Context, w http.ResponseWriter, r *http.Requ h.Evts.Send(fmt.Sprintf(`{"type":"join","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// StartGame changes the status of the game so players can begin to play. -func (h *Handlers) StartGame(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// startGame changes the status of the game so players can begin to play. +func (h *handlers) startGame(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -286,11 +285,11 @@ func (h *Handlers) StartGame(ctx context.Context, w http.ResponseWriter, r *http h.Evts.Send(fmt.Sprintf(`{"type":"start","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// RollDice will roll 5 dice for the given player and game. -func (h *Handlers) RollDice(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// rollDice will roll 5 dice for the given player and game. +func (h *handlers) rollDice(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -305,11 +304,11 @@ func (h *Handlers) RollDice(ctx context.Context, w http.ResponseWriter, r *http. h.Evts.Send(fmt.Sprintf(`{"type":"rolldice","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// Bet processes a bet made by a player in a game. -func (h *Handlers) Bet(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// bet processes a bet made by a player in a game. +func (h *handlers) bet(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -334,11 +333,11 @@ func (h *Handlers) Bet(ctx context.Context, w http.ResponseWriter, r *http.Reque h.Evts.Send(fmt.Sprintf(`{"type":"bet","address":%q,"index":%d}`, address, g.Info(ctx).Cups[address].OrderIdx)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// CallLiar processes the claims and defines a winner and a loser for the round. -func (h *Handlers) CallLiar(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// callLiar processes the claims and defines a winner and a loser for the round. +func (h *handlers) callLiar(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -357,11 +356,11 @@ func (h *Handlers) CallLiar(ctx context.Context, w http.ResponseWriter, r *http. h.Evts.Send(fmt.Sprintf(`{"type":"callliar","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// Reconcile calls the smart contract reconcile method. -func (h *Handlers) Reconcile(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// reconcile calls the smart contract reconcile method. +func (h *handlers) reconcile(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -379,11 +378,11 @@ func (h *Handlers) Reconcile(ctx context.Context, w http.ResponseWriter, r *http h.Evts.Send(fmt.Sprintf(`{"type":"reconcile","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// Balance returns the player balance from the smart contract. -func (h *Handlers) Balance(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// balance returns the player balance from the smart contract. +func (h *handlers) balance(ctx context.Context, w http.ResponseWriter, r *http.Request) error { claims := mid.GetClaims(ctx) address := claims.Subject @@ -404,8 +403,8 @@ func (h *Handlers) Balance(ctx context.Context, w http.ResponseWriter, r *http.R return web.Respond(ctx, w, resp, http.StatusOK) } -// NextTurn changes the account that will make the next move. -func (h *Handlers) NextTurn(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +// nextTurn changes the account that will make the next move. +func (h *handlers) nextTurn(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -420,13 +419,13 @@ func (h *Handlers) NextTurn(ctx context.Context, w http.ResponseWriter, r *http. h.Evts.Send(fmt.Sprintf(`{"type":"nextturn","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// UpdateOut replaces the current out amount of the player. This call is not +// updateOut replaces the current out amount of the player. This call is not // part of the game flow, it is used to control when a player should be removed // from the game. -func (h *Handlers) UpdateOut(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +func (h *handlers) updateOut(ctx context.Context, w http.ResponseWriter, r *http.Request) error { g, err := h.getGame() if err != nil { return err @@ -446,12 +445,12 @@ func (h *Handlers) UpdateOut(ctx context.Context, w http.ResponseWriter, r *http h.Evts.Send(fmt.Sprintf(`{"type":"outs","address":%q}`, address)) - return h.Status(ctx, w, r) + return h.status(ctx, w, r) } -// SetGame resets the existing game. At this time we let this happen at any +// createGame resets the existing game. At this time we let this happen at any // time regardless of game state. -func (h *Handlers) createGame(ctx context.Context, address string) error { +func (h *handlers) createGame(ctx context.Context, address string) error { h.mu.Lock() defer h.mu.Unlock() @@ -465,8 +464,8 @@ func (h *Handlers) createGame(ctx context.Context, address string) error { return nil } -// GetGame safely returns a copy of the game pointer. -func (h *Handlers) getGame() (*game.Game, error) { +// getGame safely returns a copy of the game pointer. +func (h *handlers) getGame() (*game.Game, error) { h.mu.RLock() defer h.mu.RUnlock() diff --git a/app/services/engine/handlers/v1/gamegrp/models.go b/app/services/engine/v1/handlers/gamegrp/models.go similarity index 100% rename from app/services/engine/handlers/v1/gamegrp/models.go rename to app/services/engine/v1/handlers/gamegrp/models.go diff --git a/app/services/engine/handlers/v1/v1.go b/app/services/engine/v1/handlers/gamegrp/route.go similarity index 56% rename from app/services/engine/handlers/v1/v1.go rename to app/services/engine/v1/handlers/gamegrp/route.go index 65271536..e778e1d2 100644 --- a/app/services/engine/handlers/v1/v1.go +++ b/app/services/engine/v1/handlers/gamegrp/route.go @@ -1,13 +1,10 @@ -// Package v1 contains the full set of handler functions and routes -// supported by the v1 web api. -package v1 +package gamegrp import ( "net/http" "time" "github.com/ardanlabs/ethereum/currency" - "github.com/ardanlabs/liarsdice/app/services/engine/handlers/v1/gamegrp" "github.com/ardanlabs/liarsdice/business/core/bank" "github.com/ardanlabs/liarsdice/business/web/v1/auth" "github.com/ardanlabs/liarsdice/business/web/v1/mid" @@ -30,12 +27,11 @@ type Config struct { ConnectTimeout time.Duration } -// Routes binds all the version 1 routes. +// Routes adds specific routes for this group. func Routes(app *web.App, cfg Config) { const version = "v1" - // Register group endpoints. - ggh := gamegrp.Handlers{ + hdl := handlers{ Converter: cfg.Converter, Bank: cfg.Bank, Log: cfg.Log, @@ -48,23 +44,23 @@ func Routes(app *web.App, cfg Config) { ConnectTimeout: cfg.ConnectTimeout, } - app.Handle(http.MethodPost, version, "/game/connect", ggh.Connect) + app.Handle(http.MethodPost, version, "/game/connect", hdl.connect) - app.Handle(http.MethodGet, version, "/game/events", ggh.Events) - app.Handle(http.MethodGet, version, "/game/config", ggh.Configuration) - app.Handle(http.MethodGet, version, "/game/usd2wei/:usd", ggh.USD2Wei) + app.Handle(http.MethodGet, version, "/game/events", hdl.events) + app.Handle(http.MethodGet, version, "/game/config", hdl.configuration) + app.Handle(http.MethodGet, version, "/game/usd2wei/:usd", hdl.usd2Wei) - app.Handle(http.MethodGet, version, "/game/status", ggh.Status, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/new", ggh.NewGame, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/join", ggh.Join, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/start", ggh.StartGame, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/rolldice", ggh.RollDice, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/bet/:number/:suite", ggh.Bet, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/liar", ggh.CallLiar, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/reconcile", ggh.Reconcile, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/balance", ggh.Balance, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/status", hdl.status, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/new", hdl.newGame, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/join", hdl.join, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/start", hdl.startGame, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/rolldice", hdl.rollDice, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/bet/:number/:suite", hdl.bet, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/liar", hdl.callLiar, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/reconcile", hdl.reconcile, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/balance", hdl.balance, mid.Authenticate(cfg.Auth)) // Timeout Situations with a player - app.Handle(http.MethodGet, version, "/game/next", ggh.NextTurn, mid.Authenticate(cfg.Auth)) - app.Handle(http.MethodGet, version, "/game/out/:outs", ggh.UpdateOut, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/next", hdl.nextTurn, mid.Authenticate(cfg.Auth)) + app.Handle(http.MethodGet, version, "/game/out/:outs", hdl.updateOut, mid.Authenticate(cfg.Auth)) } diff --git a/business/web/v1/debug/debug.go b/business/web/v1/debug/debug.go new file mode 100644 index 00000000..1f16e018 --- /dev/null +++ b/business/web/v1/debug/debug.go @@ -0,0 +1,29 @@ +// Package debug provides handler support for the debugging endpoints. +package debug + +import ( + "expvar" + "net/http" + "net/http/pprof" + + "github.com/arl/statsviz" +) + +// Mux registers all the debug routes from the standard library into a new mux +// bypassing the use of the DefaultServerMux. Using the DefaultServerMux would +// be a security risk since a dependency could inject a handler into our service +// without us knowing it. +func Mux() *http.ServeMux { + mux := http.NewServeMux() + + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + mux.Handle("/debug/vars/", expvar.Handler()) + + statsviz.Register(mux) + + return mux +} diff --git a/business/web/metrics/metrics.go b/business/web/v1/metrics/metrics.go similarity index 72% rename from business/web/metrics/metrics.go rename to business/web/v1/metrics/metrics.go index c462850b..7d0f950b 100644 --- a/business/web/metrics/metrics.go +++ b/business/web/v1/metrics/metrics.go @@ -35,12 +35,8 @@ func init() { } } -// Metrics will be supported through the context. - -// ctxKeyMetric represents the type of value for the context key. type ctxKey int -// key is how metric values are stored/retrieved. const key ctxKey = 1 // Set sets the metrics data into the context. @@ -48,36 +44,44 @@ func Set(ctx context.Context) context.Context { return context.WithValue(ctx, key, m) } -// Add more of these functions when a metric needs to be collected in -// different parts of the codebase. This will keep this package the -// central authority for metrics and metrics won't get lost. - // AddGoroutines refreshes the goroutine metric every 100 requests. -func AddGoroutines(ctx context.Context) { +func AddGoroutines(ctx context.Context) int64 { if v, ok := ctx.Value(key).(*metrics); ok { - if v.requests.Value()%100 == 0 { - v.goroutines.Set(int64(runtime.NumGoroutine())) - } + g := int64(runtime.NumGoroutine()) + v.goroutines.Set(g) + return g } + + return 0 } // AddRequests increments the request metric by 1. -func AddRequests(ctx context.Context) { - if v, ok := ctx.Value(key).(*metrics); ok { +func AddRequests(ctx context.Context) int64 { + v, ok := ctx.Value(key).(*metrics) + if ok { v.requests.Add(1) + return v.goroutines.Value() } + + return 0 } // AddErrors increments the errors metric by 1. -func AddErrors(ctx context.Context) { +func AddErrors(ctx context.Context) int64 { if v, ok := ctx.Value(key).(*metrics); ok { v.errors.Add(1) + return v.errors.Value() } + + return 0 } // AddPanics increments the panics metric by 1. -func AddPanics(ctx context.Context) { +func AddPanics(ctx context.Context) int64 { if v, ok := ctx.Value(key).(*metrics); ok { v.panics.Add(1) + return v.panics.Value() } + + return 0 } diff --git a/business/web/v1/mid/cors.go b/business/web/v1/mid/cors.go index 6ac4be6f..1683fb3f 100644 --- a/business/web/v1/mid/cors.go +++ b/business/web/v1/mid/cors.go @@ -8,12 +8,15 @@ import ( ) // Cors sets the response headers needed for Cross-Origin Resource Sharing -func Cors(origin string) web.Middleware { +func Cors(origins []string) web.Middleware { m := func(handler web.Handler) web.Handler { h := func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - w.Header().Set("Access-Control-Allow-Origin", origin) + for _, origin := range origins { + w.Header().Set("Access-Control-Allow-Origin", origin) + } w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + w.Header().Set("Access-Control-Max-Age", "86400") return handler(ctx, w, r) } diff --git a/business/web/v1/mid/metrics.go b/business/web/v1/mid/metrics.go index b34359ba..c16fedef 100644 --- a/business/web/v1/mid/metrics.go +++ b/business/web/v1/mid/metrics.go @@ -4,7 +4,7 @@ import ( "context" "net/http" - "github.com/ardanlabs/liarsdice/business/web/metrics" + "github.com/ardanlabs/liarsdice/business/web/v1/metrics" "github.com/ardanlabs/liarsdice/foundation/web" ) diff --git a/business/web/v1/mid/panics.go b/business/web/v1/mid/panics.go index a22c572f..bed504c2 100644 --- a/business/web/v1/mid/panics.go +++ b/business/web/v1/mid/panics.go @@ -6,7 +6,7 @@ import ( "net/http" "runtime/debug" - "github.com/ardanlabs/liarsdice/business/web/metrics" + "github.com/ardanlabs/liarsdice/business/web/v1/metrics" "github.com/ardanlabs/liarsdice/foundation/web" ) diff --git a/business/web/v1/mux/mux.go b/business/web/v1/mux/mux.go new file mode 100644 index 00000000..d6c71536 --- /dev/null +++ b/business/web/v1/mux/mux.go @@ -0,0 +1,74 @@ +// Package mux provides support to bind domain level routes +// to the application mux. +package mux + +import ( + "net/http" + "os" + "time" + + "github.com/ardanlabs/ethereum/currency" + "github.com/ardanlabs/liarsdice/business/core/bank" + "github.com/ardanlabs/liarsdice/business/web/v1/auth" + "github.com/ardanlabs/liarsdice/business/web/v1/mid" + "github.com/ardanlabs/liarsdice/foundation/events" + "github.com/ardanlabs/liarsdice/foundation/logger" + "github.com/ardanlabs/liarsdice/foundation/web" +) + +// Options represent optional parameters. +type Options struct { + corsOrigin []string +} + +// WithCORS provides configuration options for CORS. +func WithCORS(origins []string) func(opts *Options) { + return func(opts *Options) { + opts.corsOrigin = origins + } +} + +// Config contains all the mandatory systems required by handlers. +type Config struct { + Build string + Shutdown chan os.Signal + Log *logger.Logger + Auth *auth.Auth + Converter *currency.Converter + Bank *bank.Bank + Evts *events.Events + AnteUSD float64 + ActiveKID string + BankTimeout time.Duration + ConnectTimeout time.Duration +} + +// RouteAdder defines behavior that sets the routes to bind for an instance +// of the service. +type RouteAdder interface { + Add(app *web.App, cfg Config) +} + +// WebAPI constructs a http.Handler with all application routes bound. +func WebAPI(cfg Config, routeAdder RouteAdder, options ...func(opts *Options)) http.Handler { + var opts Options + for _, option := range options { + option(&opts) + } + + app := web.NewApp( + cfg.Shutdown, + mid.Logger(cfg.Log), + mid.Errors(cfg.Log), + mid.Metrics(), + mid.Panics(), + ) + + if len(opts.corsOrigin) > 0 { + app.EnableCORS(mid.Cors(opts.corsOrigin)) + } + + routeAdder.Add(app, cfg) + + return app +} diff --git a/business/web/v1/order/order.go b/business/web/v1/order/order.go deleted file mode 100644 index 25db4365..00000000 --- a/business/web/v1/order/order.go +++ /dev/null @@ -1,74 +0,0 @@ -// Package order provides support for describing the ordering of data. -package order - -import ( - "errors" - "fmt" - "net/http" - "strings" - - "github.com/ardanlabs/liarsdice/foundation/validate" -) - -// Set of directions for data ordering. -const ( - ASC = "ASC" - DESC = "DESC" -) - -var directions = map[string]string{ - ASC: "ASC", - DESC: "DESC", -} - -// By represents a field used to order by and direction. -type By struct { - Field string - Direction string -} - -// NewBy constructs a new By value with no checks. -func NewBy(field string, direction string) By { - if _, exists := directions[direction]; !exists { - return By{ - Field: field, - Direction: ASC, - } - } - - return By{ - Field: field, - Direction: direction, - } -} - -// Parse constructs a order.By value by parsing a string in the form -// of "field,direction". -func Parse(r *http.Request, defaultOrder By) (By, error) { - v := r.URL.Query().Get("orderBy") - - if v == "" { - return defaultOrder, nil - } - - orderParts := strings.Split(v, ",") - - var by By - switch len(orderParts) { - case 1: - by = NewBy(strings.TrimSpace(orderParts[0]), ASC) - - case 2: - direction := strings.Trim(orderParts[1], " ") - if _, exists := directions[direction]; !exists { - return By{}, validate.NewFieldsError(v, fmt.Errorf("unknown direction: %s", by.Direction)) - } - - by = NewBy(strings.Trim(orderParts[0], " "), direction) - - default: - return By{}, validate.NewFieldsError(v, errors.New("unknown order field")) - } - - return by, nil -} diff --git a/foundation/events/events.go b/foundation/events/events.go index 98a796c6..baa48b57 100644 --- a/foundation/events/events.go +++ b/foundation/events/events.go @@ -15,7 +15,6 @@ type Events struct { // New constructs an events for registering and receiving events. func New() *Events { - return &Events{ m: make(map[string]chan string), } @@ -66,6 +65,7 @@ func (evt *Events) Release(id string) error { delete(evt.m, id) close(ch) + return nil } diff --git a/foundation/web/context.go b/foundation/web/context.go index f7b5e0e8..98983491 100644 --- a/foundation/web/context.go +++ b/foundation/web/context.go @@ -56,3 +56,7 @@ func SetStatusCode(ctx context.Context, statusCode int) { v.StatusCode = statusCode } + +func setValues(ctx context.Context, v *Values) context.Context { + return context.WithValue(ctx, key, v) +} diff --git a/foundation/web/middleware.go b/foundation/web/middleware.go index 398337ba..3824880e 100644 --- a/foundation/web/middleware.go +++ b/foundation/web/middleware.go @@ -14,9 +14,9 @@ func wrapMiddleware(mw []Middleware, handler Handler) Handler { // handler with the new wrapped handler. Looping backwards ensures that the // first middleware of the slice is the first to be executed by requests. for i := len(mw) - 1; i >= 0; i-- { - h := mw[i] - if h != nil { - handler = h(handler) + mwFunc := mw[i] + if mwFunc != nil { + handler = mwFunc(handler) } } diff --git a/foundation/web/request.go b/foundation/web/request.go index 2de01e24..606f9493 100644 --- a/foundation/web/request.go +++ b/foundation/web/request.go @@ -2,11 +2,16 @@ package web import ( "encoding/json" + "fmt" "net/http" "github.com/dimfeld/httptreemux/v5" ) +type validator interface { + Validate() error +} + // Param returns the web call parameters from the request. func Param(r *http.Request, key string) string { m := httptreemux.ContextParams(r.Context()) @@ -15,13 +20,19 @@ func Param(r *http.Request, key string) string { // Decode reads the body of an HTTP request looking for a JSON document. The // body is decoded into the provided value. -// // If the provided value is a struct then it is checked for validation tags. +// If the value implements a validate function, it is executed. func Decode(r *http.Request, val any) error { decoder := json.NewDecoder(r.Body) decoder.DisallowUnknownFields() if err := decoder.Decode(val); err != nil { - return err + return fmt.Errorf("unable to decode payload: %w", err) + } + + if v, ok := val.(validator); ok { + if err := v.Validate(); err != nil { + return fmt.Errorf("unable to validate payload: %w", err) + } } return nil diff --git a/foundation/web/web.go b/foundation/web/web.go index f1850394..e752a783 100644 --- a/foundation/web/web.go +++ b/foundation/web/web.go @@ -41,33 +41,70 @@ func (a *App) SignalShutdown() { a.shutdown <- syscall.SIGTERM } +// EnableCORS enables CORS preflight requests to work in the middleware. It +// prevents the MethodNotAllowedHandler from being called. This must be enabled +// for the CORS middleware to work. +func (a *App) EnableCORS(mw Middleware) { + a.mw = append(a.mw, mw) + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return Respond(ctx, w, "OK", http.StatusOK) + } + handler = wrapMiddleware(a.mw, handler) + + a.ContextMux.OptionsHandler = func(w http.ResponseWriter, r *http.Request, params map[string]string) { + v := Values{ + TraceID: uuid.NewString(), + Now: time.Now().UTC(), + } + ctx := setValues(r.Context(), &v) + + handler(ctx, w, r) + } +} + +// HandleNoMiddleware sets a handler function for a given HTTP method and path pair +// to the application server mux. Does not include the application middleware or +// OTEL tracing. +func (a *App) HandleNoMiddleware(method string, group string, path string, handler Handler) { + h := func(w http.ResponseWriter, r *http.Request) { + v := Values{ + TraceID: uuid.NewString(), + Now: time.Now().UTC(), + } + ctx := setValues(r.Context(), &v) + + if err := handler(ctx, w, r); err != nil { + if validateError(err) { + a.SignalShutdown() + return + } + } + } + + finalPath := path + if group != "" { + finalPath = "/" + group + path + } + + a.ContextMux.Handle(method, finalPath, h) +} + // Handle sets a handler function for a given HTTP method and path pair // to the application server mux. func (a *App) Handle(method string, group string, path string, handler Handler, mw ...Middleware) { - - // First wrap handler specific middleware around this handler. handler = wrapMiddleware(mw, handler) - - // Add the application's general middleware to the handler chain. handler = wrapMiddleware(a.mw, handler) - // The function to execute for each request. h := func(w http.ResponseWriter, r *http.Request) { - - // Set the context with the required values to - // process the request. v := Values{ TraceID: uuid.NewString(), Now: time.Now().UTC(), } - ctx := context.WithValue(r.Context(), key, &v) + ctx := setValues(r.Context(), &v) - // Call the wrapped handler functions. if err := handler(ctx, w, r); err != nil { - - // Since there was an error, validate the condition of this - // error and determine if we need to actually shutdown or not. - if validateShutdown(err) { + if validateError(err) { a.SignalShutdown() return } @@ -78,12 +115,13 @@ func (a *App) Handle(method string, group string, path string, handler Handler, if group != "" { finalPath = "/" + group + path } + a.ContextMux.Handle(method, finalPath, h) } -// validateShutdown validates the error for special conditions that do not +// validateError validates the error for special conditions that do not // warrant an actual shutdown by the system. -func validateShutdown(err error) bool { +func validateError(err error) bool { // Ignore syscall.EPIPE and syscall.ECONNRESET errors which occurs // when a write operation happens on the http.ResponseWriter that diff --git a/go.mod b/go.mod index c1d99148..7c6ba188 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/ardanlabs/conf/v3 v3.1.7 github.com/ardanlabs/darwin/v2 v2.0.0 github.com/ardanlabs/ethereum v0.17.0 + github.com/arl/statsviz v0.6.0 github.com/dimfeld/httptreemux/v5 v5.5.0 github.com/ethereum/go-ethereum v1.13.10 github.com/gdamore/tcell/v2 v2.7.0 diff --git a/go.sum b/go.sum index d3f9a6ac..8e36a5ea 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/ardanlabs/darwin/v2 v2.0.0 h1:XCisQMgQ5EG+ZvSEcADEo+pyfIMKyWAGnn5o2Tg github.com/ardanlabs/darwin/v2 v2.0.0/go.mod h1:MubZ2e9DAYGaym0mClSOi183NYahrrfKxvSy1HMhoes= github.com/ardanlabs/ethereum v0.17.0 h1:3JeZoud2vM2Ykkid/JCnJW8Nxrwewp4sZsUg5GM9jz8= github.com/ardanlabs/ethereum v0.17.0/go.mod h1:+OIUAK8YF50DcRPkkdR3bzQCdb+ElYCP1PDv6izZ85Q= +github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE= +github.com/arl/statsviz v0.6.0/go.mod h1:0toboo+YGSUXDaS4g1D5TVS4dXs7S7YYT5J/qnW2h8s= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= diff --git a/vendor/github.com/ardanlabs/conf/v3/conf.go b/vendor/github.com/ardanlabs/conf/v3/conf.go index 704320a4..768608cc 100644 --- a/vendor/github.com/ardanlabs/conf/v3/conf.go +++ b/vendor/github.com/ardanlabs/conf/v3/conf.go @@ -24,6 +24,8 @@ type Parsers interface { Process(prefix string, cfg interface{}) error } +// ============================================================================= + // Parse parses the specified config struct. This function will // apply the defaults first and then apply environment variables and // command line argument overrides to the struct. ErrHelpWanted is @@ -146,6 +148,8 @@ func VersionInfo(namespace string, v interface{}) (string, error) { return str.String(), nil } +// ============================================================================= + // parse parses configuration into the provided struct. func parse(args []string, namespace string, cfgStruct interface{}) error { // Create the flag and env sources. @@ -232,6 +236,8 @@ func parse(args []string, namespace string, cfgStruct interface{}) error { return nil } +// ============================================================================= + // Args holds command line arguments after flags have been parsed. type Args []string diff --git a/vendor/github.com/arl/statsviz/.gitattributes b/vendor/github.com/arl/statsviz/.gitattributes new file mode 100644 index 00000000..9c6c8482 --- /dev/null +++ b/vendor/github.com/arl/statsviz/.gitattributes @@ -0,0 +1 @@ +internal/static/** linguist-detectable=false diff --git a/vendor/github.com/arl/statsviz/.gitignore b/vendor/github.com/arl/statsviz/.gitignore new file mode 100644 index 00000000..befa1383 --- /dev/null +++ b/vendor/github.com/arl/statsviz/.gitignore @@ -0,0 +1,2 @@ +*.test +out diff --git a/vendor/github.com/arl/statsviz/CHANGELOG.md b/vendor/github.com/arl/statsviz/CHANGELOG.md new file mode 100644 index 00000000..7930ee61 --- /dev/null +++ b/vendor/github.com/arl/statsviz/CHANGELOG.md @@ -0,0 +1,85 @@ +v0.6.0 / 2023-10-08 +============== + * New plots showing new go1.20/go1.21 runtime/metrics: (#111) + + GC Cycles + + GC Scan + + Memory classes + + CPU classes + + Mutex wait + * Users can now add their own plots (#111) + * Add light/dark mode selector (#108) + +v0.5.2 / 2023-03-29 +============== + * Ensure all files have a correct Content-Type (#106) + +v0.5.1 / 2022-09-30 +============== + * Fix UI on Firefox (#83) + +v0.5.0 / 2022-09-05 +============== + * Switch to runtime/metrics as source, major refactor (#75) + + New heatmap UI component + + Dynamic plots definition based on server side generated config + + Add many new plots (scheduler latency, scheduling events, and more) + + Add play/pause switch button + + Add show/hide GC events switch button + + Add time range selector (1m, 5m, 10m) + * Switch javascript code to ES6 (#65) + * Build and test all examples (#63) + +v0.4.1 / 2021-12-12 +============== + * Assets are `go:embed`ed, so the minimum go version is now go1.16 (#55) + * Polishing (README, small UI improvements) (#54) + * Small ui improvements: link to go.dev rather than golang.org + +v0.4.0 / 2021-05-08 +================== + + * Auto-reconnect to new server from GUI after closed websocket connection (#49) + * Reorganize examples (#51) + * Make `IndexAtRoot` returns an `http.HandlerFunc` instead of `http.Handler` (#52) + +v0.3.0 / 2021-02-14 +================== + + * Enable 'save as png' button on plots (#44) + +v0.2.2 / 2020-12-13 +================== + + * Use Go Modules for 'github.com/gorilla/websocket' (#39) + * Support custom frequency (#37) + * Added fixed go-chi example (#38) + * `_example`: add echo (#22) + * `_example`: add gin example (#34) + * ci: track coverage + * RegisterDefault returns an error now + * Ensure send frequency is a strictly positive integer + * Don't log if we can't upgrade to websocket + * `_example`_example: add chi router (#38) + * `_example`_example: change structure to have one example per directory + +v0.2.1 / 2020-10-29 +=================== + + * Fix websocket handler now working with https (#25) + +v0.2.0 / 2020-10-25 +=================== + + * `Register` now accepts options (functional options API) (#20) + + `Root` allows to root statsviz at a path different than `/debug/statsviz` + + `SendFrequency` allows to set the frequency at which stats are emitted. + +v0.1.1 / 2020-10-12 +=================== + + * Do not leak timer in sendStats + +v0.1.0 / 2020-10-10 +=================== + + * First released version diff --git a/vendor/github.com/arl/statsviz/CONTRIBUTING.md b/vendor/github.com/arl/statsviz/CONTRIBUTING.md new file mode 100644 index 00000000..048b206f --- /dev/null +++ b/vendor/github.com/arl/statsviz/CONTRIBUTING.md @@ -0,0 +1,66 @@ +Contributing +============ + +First of all, thank you for considering to contribute to Statsviz! + +Pull-requests are welcome! + + +## Go library + +The Statsviz Go public API is relatively light so there's not much to do and at +the moment it's unlikely that the API will change. However new options can be +added to `statsviz.Register` and `statsviz.NewServer` without breaking +compatibility. + +That being said, there may be things to improve in the implementation, any +contribution is very welcome! + +Big changes should be discussed on the issue tracker prior to start working on +the code. + +If you've decided to contribute, thank you so much, please comment on the existing +issue or create one stating what you want to tackle and why. + + +## User interface (html/css/javascript) + +The user interface aims to be simple, light and minimal. + +Assets are located in the `internal/static` directory and are embedded with +[`go:embed`](https://pkg.go.dev/embed). + +Depending on what your modifications are, it's always a good idea to check that +some of the examples in [./_example](./_example/) work with your modifications +to Statsviz. To do so `cd` to the directory of the example and run: + + go mod edit -replace=github.com/arl/statsviz=../../ + + +## Documentation + +No contribution is too small, improvements to code comments and/or README +are welcome! + + +## Examples + +There are many Go libraries to handle HTTP requests, routing, etc.. + +Feel free to add an example to show how to register Statsviz with your favourite +library. + +To do so, please add a directory under `./_example`. For instance, if you want to add an +example showing how to register Statsviz within library `foobar`: + + - create a directory `./_example/foobar/` + - create a file `./_example/foobar/main.go` + - call `go example.Work()` as the first line of your example (see other + examples). This forces the garbage collector to _do something_ so that + Statsviz interface won't remain static when an user runs your example. + - the code should be `gofmt`ed + - the example should compile and run + - when ran, Statsviz interface should be accessible at http://localhost:8080/debug/statsviz + + +Thank you! diff --git a/vendor/github.com/arl/statsviz/LICENSE b/vendor/github.com/arl/statsviz/LICENSE new file mode 100644 index 00000000..3cc5a5bf --- /dev/null +++ b/vendor/github.com/arl/statsviz/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Aurélien Rainone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/arl/statsviz/README.md b/vendor/github.com/arl/statsviz/README.md new file mode 100644 index 00000000..496cb6f3 --- /dev/null +++ b/vendor/github.com/arl/statsviz/README.md @@ -0,0 +1,230 @@ +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=round-square)](https://pkg.go.dev/github.com/arl/statsviz) +[![Latest tag](https://img.shields.io/github/tag/arl/statsviz.svg)](https://github.com/arl/statsviz/tag/) +[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) + +[![Test Actions Status](https://github.com/arl/statsviz/workflows/Tests-linux/badge.svg)](https://github.com/arl/statsviz/actions) +[![Test Actions Status](https://github.com/arl/statsviz/workflows/Tests-others/badge.svg)](https://github.com/arl/statsviz/actions) +[![codecov](https://codecov.io/gh/arl/statsviz/branch/main/graph/badge.svg)](https://codecov.io/gh/arl/statsviz) + +# Statsviz + +

+ Statsviz Gopher Logo + statsviz ui +

+
+ +Visualize real time plots of your Go program runtime metrics, including heap, objects, goroutines, GC pauses, scheduler and more, in your browser. + +
+ +- [Statsviz](#statsviz) + - [Install](#install) + - [Usage](#usage) + - [Advanced Usage](#advanced-usage) + - [How Does That Work?](#how-does-that-work) + - [Documentation](#documentation) + - [Go API](#go-api) + - [User interface](#user-interface) + - [Plots](#plots) + - [User Plots](#user-plots) + - [Examples](#examples) + - [Questions / Troubleshooting](#questions--troubleshooting) + - [Contributing](#contributing) + - [Changelog](#changelog) + - [License: MIT](#license-mit) + +## Install + +Download the latest version: + +``` +go get github.com/arl/statsviz@latest +``` + +Please note that, as new metrics are added to the `/runtime/metrics` package, new plots are added to Statsviz. +This also means that the presence of some plots on the dashboard depends on the Go version you're using. + +When in doubt, use the latest ;-) + + +## Usage + +Register `Statsviz` HTTP handlers with your application `http.ServeMux`. + +```go +mux := http.NewServeMux() +statsviz.Register(mux) + +go func() { + log.Println(http.ListenAndServe("localhost:8080", mux)) +}() +``` + +Open your browser at http://localhost:8080/debug/statsviz + + +## Advanced Usage + +If you want more control over Statsviz HTTP handlers, examples are: + - you're using some HTTP framework + - you want to place Statsviz handler behind some middleware + +then use `statsviz.NewServer` to obtain a `Server` instance. Both the `Index()` and `Ws()` methods return `http.HandlerFunc`. + +```go +srv, err := statsviz.NewServer(); // Create server or handle error +srv.Index() // UI (dashboard) http.HandlerFunc +srv.Ws() // Websocket http.HandlerFunc +``` + +Please look at examples of usage in the [Examples](_example) directory. + + +## How Does That Work? + +`statsviz.Register` registers 2 HTTP handlers within the given `http.ServeMux`: + +- the `Index` handler serves Statsviz user interface at `/debug/statsviz` at the address served by your program. + +- The `Ws` serves a Websocket endpoint. When the browser connects to that endpoint, [runtime/metrics](https://pkg.go.dev/runtime/metrics) are sent to the browser, once per second. + +Data points are in a browser-side circular-buffer. + + +## Documentation + +### Go API + +Check out the API reference on [pkg.go.dev](https://pkg.go.dev/github.com/arl/statsviz#section-documentation). + +### User interface + +Controls at the top of the page act on all plots: + +menu + +- the groom shows/hides the vertical lines representing garbage collections. +- the time range selector defines the visualized time span. +- the play/pause icons stops and resume the refresh of the plots. +- the light/dark selector switches between light and dark modes. + +On top of each plot there are 2 icons: + +menu + +- the camera downloads a PNG image of the plot. +- the info icon shows details about the metrics displayed. + +### Plots + +Depending on your go version, some plots may not be available. + +#### Heap (global) + +heap-global + +#### Heap (details) + +heap-details + +#### Live Objects in Heap + +live-objects + +#### Live Bytes in Heap + +live-bytes + +#### MSpan/MCache + +mspan-mcache + +#### Memory classes + +memory-classes + +#### Goroutines + +goroutines + +#### Size Classes + +size-classes + +#### GC Scan + +gc-scan + +#### GC Cycles + +gc-cycles + +#### Stop-the-world Pause Latencies + +gc-pauses + +#### CPU Classes (GC) + +cpu-classes-gc + +#### Time Goroutines Spend in 'Runnable' state + +runnable-time + +#### Time Goroutines Spend Blocked on Mutexes + +mutex-wait + +#### Starting Size of Goroutines Stacks + +gc-stack-size + +#### Goroutine Scheduling Events + +sched-events + +#### CGO Calls + +cgo + + +### User Plots + +Since `v0.6` you can add your own plots to Statsviz dashboard, in order to easily +visualize your application metrics next to runtime metrics. + +Please see the [userplots example](_example/userplots/main.go). + +## Examples + +Check out the [\_example](./_example/README.md) directory to see various ways to use Statsviz, such as: + +- use of `http.DefaultServeMux` or your own `http.ServeMux` +- wrap HTTP handler behind a middleware +- register the web page at `/foo/bar` instead of `/debug/statsviz` +- use `https://` rather than `http://` +- register Statsviz handlers with various Go HTTP libraries/frameworks: + - [echo](https://github.com/labstack/echo/) + - [fasthttp](https://github.com/valyala/fasthttp) + - [fiber](https://github.com/gofiber/fiber/) + - [gin](https://github.com/gin-gonic/gin) + - and many others thanks to many contributors! + +## Questions / Troubleshooting + +Either use GitHub's [discussions](https://github.com/arl/statsviz/discussions) or come to say hi and ask a live question on [#statsviz channel on Gopher's slack](https://gophers.slack.com/archives/C043DU4NZ9D). + +## Contributing + +Please use [issues](https://github.com/arl/statsviz/issues/new/choose) for bugs and feature requests. +Pull-requests are always welcome! +More details in [CONTRIBUTING.md](CONTRIBUTING.md). + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md). + +## License: MIT + +See [LICENSE](LICENSE) diff --git a/vendor/github.com/arl/statsviz/codecov.yml b/vendor/github.com/arl/statsviz/codecov.yml new file mode 100644 index 00000000..e6829ed7 --- /dev/null +++ b/vendor/github.com/arl/statsviz/codecov.yml @@ -0,0 +1,14 @@ +codecov: + require_ci_to_pass: yes + +coverage: + round: down + precision: 2 + + status: + project: + default: + informational: true + patch: + default: + informational: true diff --git a/vendor/github.com/arl/statsviz/internal/plot/color.go b/vendor/github.com/arl/statsviz/internal/plot/color.go new file mode 100644 index 00000000..6366fc20 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/plot/color.go @@ -0,0 +1,64 @@ +package plot + +import ( + "fmt" + "image/color" +) + +func RGBString(r, g, b uint8) string { + return fmt.Sprintf(`"rgb(%d,%d,%d,0)"`, r, g, b) +} + +type WeightedColor struct { + Value float64 + Color color.RGBA +} + +func (c WeightedColor) MarshalJSON() ([]byte, error) { + str := fmt.Sprintf(`[%f,"rgb(%d,%d,%d,%f)"]`, + c.Value, c.Color.R, c.Color.G, c.Color.B, float64(c.Color.A)/255) + return []byte(str), nil +} + +// https://mdigi.tools/color-shades/ +var BlueShades = []WeightedColor{ + {Value: 0.0, Color: color.RGBA{0xea, 0xf8, 0xfd, 1}}, + {Value: 0.1, Color: color.RGBA{0xbf, 0xeb, 0xfa, 1}}, + {Value: 0.2, Color: color.RGBA{0x94, 0xdd, 0xf6, 1}}, + {Value: 0.3, Color: color.RGBA{0x69, 0xd0, 0xf2, 1}}, + {Value: 0.4, Color: color.RGBA{0x3f, 0xc2, 0xef, 1}}, + {Value: 0.5, Color: color.RGBA{0x14, 0xb5, 0xeb, 1}}, + {Value: 0.6, Color: color.RGBA{0x10, 0x94, 0xc0, 1}}, + {Value: 0.7, Color: color.RGBA{0x0d, 0x73, 0x96, 1}}, + {Value: 0.8, Color: color.RGBA{0x09, 0x52, 0x6b, 1}}, + {Value: 0.9, Color: color.RGBA{0x05, 0x31, 0x40, 1}}, + {Value: 1.0, Color: color.RGBA{0x02, 0x10, 0x15, 1}}, +} + +var PinkShades = []WeightedColor{ + {Value: 0.0, Color: color.RGBA{0xfe, 0xe7, 0xf3, 1}}, + {Value: 0.1, Color: color.RGBA{0xfc, 0xb6, 0xdc, 1}}, + {Value: 0.2, Color: color.RGBA{0xf9, 0x85, 0xc5, 1}}, + {Value: 0.3, Color: color.RGBA{0xf7, 0x55, 0xae, 1}}, + {Value: 0.4, Color: color.RGBA{0xf5, 0x24, 0x96, 1}}, + {Value: 0.5, Color: color.RGBA{0xdb, 0x0a, 0x7d, 1}}, + {Value: 0.6, Color: color.RGBA{0xaa, 0x08, 0x61, 1}}, + {Value: 0.7, Color: color.RGBA{0x7a, 0x06, 0x45, 1}}, + {Value: 0.8, Color: color.RGBA{0x49, 0x03, 0x2a, 1}}, + {Value: 0.9, Color: color.RGBA{0x18, 0x01, 0x0e, 1}}, + {Value: 1.0, Color: color.RGBA{0x00, 0x00, 0x00, 1}}, +} + +var GreenShades = []WeightedColor{ + {Value: 0.0, Color: color.RGBA{0xed, 0xf7, 0xf2, 0}}, + {Value: 0.1, Color: color.RGBA{0xc9, 0xe8, 0xd7, 0}}, + {Value: 0.2, Color: color.RGBA{0xa5, 0xd9, 0xbc, 0}}, + {Value: 0.3, Color: color.RGBA{0x81, 0xca, 0xa2, 0}}, + {Value: 0.4, Color: color.RGBA{0x5e, 0xbb, 0x87, 0}}, + {Value: 0.5, Color: color.RGBA{0x44, 0xa1, 0x6e, 0}}, + {Value: 0.6, Color: color.RGBA{0x35, 0x7e, 0x55, 0}}, + {Value: 0.7, Color: color.RGBA{0x26, 0x5a, 0x3d, 0}}, + {Value: 0.8, Color: color.RGBA{0x17, 0x36, 0x25, 0}}, + {Value: 0.9, Color: color.RGBA{0x08, 0x12, 0x0c, 0}}, + {Value: 1.0, Color: color.RGBA{0x00, 0x00, 0x00, 0}}, +} diff --git a/vendor/github.com/arl/statsviz/internal/plot/hist.go b/vendor/github.com/arl/statsviz/internal/plot/hist.go new file mode 100644 index 00000000..e46616c1 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/plot/hist.go @@ -0,0 +1,87 @@ +package plot + +import ( + "math" + "runtime/metrics" +) + +// maxBuckets is the maximum number of buckets we'll plots in heatmaps. +// Histograms with more buckets than that are going to be downsampled. +const maxBuckets = 100 + +// downsampleFactor computes the downsampling factor to use with +// downsampleCounts and downsampleBuckets. nstart and nfinal are the number of +// buckets in the original and final bucket. +func downsampleFactor(norg, nfinal int) int { + mod := norg % nfinal + if mod == 0 { + return norg / nfinal + } + return 1 + norg/nfinal +} + +// downsampleBuckets downsamples the number of buckets in the provided +// histogram, using the given dividing factor, and returns a slice of bucket +// widths. +// +// Given that metrics.Float64Histogram contains the boundaries of histogram +// buckets, the first bucket is not even considered since we're only interested +// in upper bounds. Also, since we can't draw an infinitely large bucket, if h +// last bucket holds +Inf, the width of the last returned bucket will +// extrapolated from the previous 2 buckets. +func downsampleBuckets(h *metrics.Float64Histogram, factor int) []float64 { + var ret []float64 + vals := h.Buckets[1:] + + for i := 0; i < len(vals); i++ { + if (i+1)%factor == 0 { + ret = append(ret, vals[i]) + } + } + if len(vals)%factor != 0 { + // If the number of bucket is not divisible by the factor, let's make a + // last downsampled bucket, even if it doesn't 'contain' the same number + // of original buckets. + ret = append(ret, vals[len(vals)-1]) + } + + if len(ret) > 2 && math.IsInf(ret[len(ret)-1], 1) { + // Plotly doesn't support a +Inf bound for the last bucket. So we make it + // so that the last bucket has the same 'width' than the penultimate one. + ret[len(ret)-1] = ret[len(ret)-2] - ret[len(ret)-3] + ret[len(ret)-2] + } + + return ret +} + +// downsampleCounts downsamples the counts in the provided histogram, using the +// given factor. Every 'factor' buckets are merged into one, larger, bucket. If +// the number of buckets is not divisible by 'factor', then an additional last +// bucket will contain the sum of the counts in all relainbing buckets. +// +// Note: slice should be a slice of maxBuckets elements, so that it can be +// reused across calls. +func downsampleCounts(h *metrics.Float64Histogram, factor int, slice []uint64) []uint64 { + vals := h.Counts + + if factor == 1 { + copy(slice, vals) + slice = slice[:len(vals)] + return slice + } + + slice = slice[:0] + + var sum uint64 + for i := 0; i < len(vals); i++ { + if i%factor == 0 && i > 1 { + slice = append(slice, sum) + sum = vals[i] + } else { + sum += vals[i] + } + } + + // Whatever sum remains, it goes to the last bucket. + return append(slice, sum) +} diff --git a/vendor/github.com/arl/statsviz/internal/plot/layout.go b/vendor/github.com/arl/statsviz/internal/plot/layout.go new file mode 100644 index 00000000..bf7122b6 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/plot/layout.go @@ -0,0 +1,69 @@ +package plot + +type ( + Config struct { + // Series contains the plots we want to show and how we want to show them. + Series []any `json:"series"` + // Events contains a list of 'events time series' names. Series with + // these names must be sent alongside other series. An event time series + // is just made of timestamps with no associated value, each of which + // gets plotted as a vertical line over another plot. + Events []string `json:"events"` + } + + Scatter struct { + Name string `json:"name"` + Title string `json:"title"` + Type string `json:"type"` + UpdateFreq int `json:"updateFreq"` + InfoText string `json:"infoText"` + Events string `json:"events"` + Layout ScatterLayout `json:"layout"` + Subplots []Subplot `json:"subplots"` + } + ScatterLayout struct { + Yaxis ScatterYAxis `json:"yaxis"` + BarMode string `json:"barmode"` + } + ScatterYAxis struct { + Title string `json:"title"` + TickSuffix string `json:"ticksuffix"` + } + + Subplot struct { + Name string `json:"name"` + Unitfmt string `json:"unitfmt"` + StackGroup string `json:"stackgroup"` + HoverOn string `json:"hoveron"` + Color string `json:"color"` + Type string `json:"type"` + } + + Heatmap struct { + Name string `json:"name"` + Title string `json:"title"` + Type string `json:"type"` + UpdateFreq int `json:"updateFreq"` + InfoText string `json:"infoText"` + Events string `json:"events"` + Layout HeatmapLayout `json:"layout"` + Colorscale []WeightedColor `json:"colorscale"` + Buckets []float64 `json:"buckets"` + CustomData []float64 `json:"custom_data"` + Hover HeapmapHover `json:"hover"` + } + HeatmapLayout struct { + YAxis HeatmapYaxis `json:"yaxis"` + } + HeatmapYaxis struct { + Title string `json:"title"` + TickMode string `json:"tickmode"` + TickVals []float64 `json:"tickvals"` + TickText []float64 `json:"ticktext"` + } + HeapmapHover struct { + YName string `json:"yname"` + YUnit string `json:"yunit"` // 'duration', 'bytes' or custom + ZName string `json:"zname"` + } +) diff --git a/vendor/github.com/arl/statsviz/internal/plot/plots.go b/vendor/github.com/arl/statsviz/internal/plot/plots.go new file mode 100644 index 00000000..68cfd824 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/plot/plots.go @@ -0,0 +1,1320 @@ +package plot + +import ( + "math" + "runtime/metrics" + "time" +) + +func init() { + // lastgc and timestamp are both special cases. + + // lastgc draws vertical lines represeting GCs on certain plots. + registerRuntimePlot("lastgc") + // timestamp is the metric is the data for the time axis used on all plots. + registerRuntimePlot("timestamp") + + registerPlotFunc(makeHeapGlobalPlot) + registerPlotFunc(makeHeapDetailsPlot) + registerPlotFunc(makeLiveObjectsPlot) + registerPlotFunc(makeLiveBytesPlot) + registerPlotFunc(makeMSpanMCachePlot) + registerPlotFunc(makeMemoryClassesPlot) + registerPlotFunc(makeGoroutinesPlot) + registerPlotFunc(makeSizeClassesPlot) + registerPlotFunc(makeGCScanPlot) + registerPlotFunc(makeGCCyclesPlot) + registerPlotFunc(makeGCPausesPlot) + registerPlotFunc(makeCPUClassesGCPlot) + registerPlotFunc(makeRunnableTimePlot) + registerPlotFunc(makeMutexWaitPlot) + registerPlotFunc(makeGCStackSizePlot) + registerPlotFunc(makeSchedEventsPlot) + registerPlotFunc(makeCGOPlot) +} + +/* + * heap (global) + */ +var _ = registerRuntimePlot("heap-global", + "/memory/classes/heap/objects:bytes", + "/memory/classes/heap/unused:bytes", + "/memory/classes/heap/free:bytes", + "/memory/classes/heap/released:bytes", +) + +type heapGlobal struct { + enabled bool + + idxobj int + idxunused int + idxfree int + idxreleased int +} + +func makeHeapGlobalPlot(idxs map[string]int) runtimeMetric { + idxobj, ok1 := idxs["/memory/classes/heap/objects:bytes"] + idxunused, ok2 := idxs["/memory/classes/heap/unused:bytes"] + idxfree, ok3 := idxs["/memory/classes/heap/free:bytes"] + idxreleased, ok4 := idxs["/memory/classes/heap/released:bytes"] + + return &heapGlobal{ + enabled: ok1 && ok2 && ok3 && ok4, + idxobj: idxobj, + idxunused: idxunused, + idxfree: idxfree, + idxreleased: idxreleased, + } +} + +func (p *heapGlobal) name() string { return "heap-global" } +func (p *heapGlobal) isEnabled() bool { return p.enabled } + +func (p *heapGlobal) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Heap (global)", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "heap in-use", + Unitfmt: "%{y:.4s}B", + HoverOn: "points+fills", + StackGroup: "one", + }, + { + Name: "heap free", + Unitfmt: "%{y:.4s}B", + HoverOn: "points+fills", + StackGroup: "one", + }, + { + Name: "heap released", + Unitfmt: "%{y:.4s}B", + HoverOn: "points+fills", + StackGroup: "one", + }, + }, + InfoText: `Heap in use is /memory/classes/heap/objects + /memory/classes/heap/unused. It amounts to the memory occupied by live objects and dead objects that are not yet marked free by the GC, plus some memory reserved for heap objects. +Heap free is /memory/classes/heap/free, that is free memory that could be returned to the OS, but has not been. +Heap released is /memory/classes/heap/free, memory that is free memory that has been returned to the OS.`, + } + s.Layout.Yaxis.TickSuffix = "B" + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *heapGlobal) values(samples []metrics.Sample) any { + heapObjects := samples[p.idxobj].Value.Uint64() + heapUnused := samples[p.idxunused].Value.Uint64() + + heapInUse := heapObjects + heapUnused + heapFree := samples[p.idxfree].Value.Uint64() + heapReleased := samples[p.idxreleased].Value.Uint64() + return []uint64{ + heapInUse, + heapFree, + heapReleased, + } +} + +/* + * heap (details) + */ +var _ = registerRuntimePlot("heap-details", + "/memory/classes/heap/objects:bytes", + "/memory/classes/heap/unused:bytes", + "/memory/classes/heap/free:bytes", + "/memory/classes/heap/released:bytes", + "/memory/classes/heap/stacks:bytes", + "/gc/heap/goal:bytes", +) + +type heapDetails struct { + enabled bool + + idxobj int + idxunused int + idxfree int + idxreleased int + idxstacks int + idxgoal int +} + +func makeHeapDetailsPlot(idxs map[string]int) runtimeMetric { + idxobj, ok1 := idxs["/memory/classes/heap/objects:bytes"] + idxunused, ok2 := idxs["/memory/classes/heap/unused:bytes"] + idxfree, ok3 := idxs["/memory/classes/heap/free:bytes"] + idxreleased, ok4 := idxs["/memory/classes/heap/released:bytes"] + idxstacks, ok5 := idxs["/memory/classes/heap/stacks:bytes"] + idxgoal, ok6 := idxs["/gc/heap/goal:bytes"] + + return &heapDetails{ + enabled: ok1 && ok2 && ok3 && ok4 && ok5 && ok6, + idxobj: idxobj, + idxunused: idxunused, + idxfree: idxfree, + idxreleased: idxreleased, + idxstacks: idxstacks, + idxgoal: idxgoal, + } +} + +func (p *heapDetails) name() string { return "heap-details" } +func (p *heapDetails) isEnabled() bool { return p.enabled } + +func (p *heapDetails) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Heap (details)", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "heap sys", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "heap objects", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "heap stacks", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "heap goal", + Unitfmt: "%{y:.4s}B", + }, + }, + InfoText: `Heap sys is /memory/classes/heap/objects + /memory/classes/heap/unused + /memory/classes/heap/released + /memory/classes/heap/free. It's an estimate of all the heap memory obtained form the OS. +Heap objects is /memory/classes/heap/objects, the memory occupied by live objects and dead objects that have not yet been marked free by the GC. +Heap stacks is /memory/classes/heap/stacks, the memory used for stack space. +Heap goal is gc/heap/goal, the heap size target for the end of the GC cycle.`, + } + s.Layout.Yaxis.TickSuffix = "B" + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *heapDetails) values(samples []metrics.Sample) any { + heapObjects := samples[p.idxobj].Value.Uint64() + heapUnused := samples[p.idxunused].Value.Uint64() + + heapInUse := heapObjects + heapUnused + heapFree := samples[p.idxfree].Value.Uint64() + heapReleased := samples[p.idxreleased].Value.Uint64() + + heapIdle := heapReleased + heapFree + heapSys := heapInUse + heapIdle + heapStacks := samples[p.idxstacks].Value.Uint64() + nextGC := samples[p.idxgoal].Value.Uint64() + + return []uint64{ + heapSys, + heapObjects, + heapStacks, + nextGC, + } +} + +/* + * live objects + */ +var _ = registerRuntimePlot("live-objects", "/gc/heap/objects:objects") + +type liveObjects struct { + enabled bool + + idxobjects int +} + +func makeLiveObjectsPlot(idxs map[string]int) runtimeMetric { + idxobjects, ok := idxs["/gc/heap/objects:objects"] + + return &liveObjects{ + enabled: ok, + idxobjects: idxobjects, + } +} + +func (p *liveObjects) name() string { return "live-objects" } +func (p *liveObjects) isEnabled() bool { return p.enabled } + +func (p *liveObjects) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Live Objects in Heap", + Type: "bar", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "live objects", + Unitfmt: "%{y:.4s}", + Color: RGBString(255, 195, 128), + }, + }, + InfoText: `Live objects is /gc/heap/objects. It's the number of objects, live or unswept, occupying heap memory.`, + } + s.Layout.Yaxis.Title = "objects" + return s +} + +func (p *liveObjects) values(samples []metrics.Sample) any { + gcHeapObjects := samples[p.idxobjects].Value.Uint64() + return []uint64{ + gcHeapObjects, + } +} + +/* + * live bytes + */ +var _ = registerRuntimePlot("live-bytes", + "/gc/heap/allocs:bytes", + "/gc/heap/frees:bytes", +) + +type liveBytes struct { + enabled bool + + idxallocs int + idxfrees int +} + +func makeLiveBytesPlot(idxs map[string]int) runtimeMetric { + idxallocs, ok1 := idxs["/gc/heap/allocs:bytes"] + idxfrees, ok2 := idxs["/gc/heap/frees:bytes"] + + return &liveBytes{ + enabled: ok1 && ok2, + idxallocs: idxallocs, + idxfrees: idxfrees, + } +} + +func (p *liveBytes) name() string { return "live-bytes" } +func (p *liveBytes) isEnabled() bool { return p.enabled } + +func (p *liveBytes) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Live Bytes in Heap", + Type: "bar", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "live bytes", + Unitfmt: "%{y:.4s}B", + Color: RGBString(135, 182, 218), + }, + }, + InfoText: `Live bytes is /gc/heap/allocs - /gc/heap/frees. It's the number of bytes currently allocated (and not yet GC'ec) to the heap by the application.`, + } + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *liveBytes) values(samples []metrics.Sample) any { + allocBytes := samples[p.idxallocs].Value.Uint64() + freedBytes := samples[p.idxfrees].Value.Uint64() + return []uint64{ + allocBytes - freedBytes, + } +} + +/* + * mspan mcache + */ +var _ = registerRuntimePlot("mspan-mcache", + "/memory/classes/metadata/mspan/inuse:bytes", + "/memory/classes/metadata/mspan/free:bytes", + "/memory/classes/metadata/mcache/inuse:bytes", + "/memory/classes/metadata/mcache/free:bytes", +) + +type mspanMcache struct { + enabled bool + + idxmspanInuse int + idxmspanFree int + idxmcacheInuse int + idxmcacheFree int +} + +func makeMSpanMCachePlot(idxs map[string]int) runtimeMetric { + idxmspanInuse, ok1 := idxs["/memory/classes/metadata/mspan/inuse:bytes"] + idxmspanFree, ok2 := idxs["/memory/classes/metadata/mspan/free:bytes"] + idxmcacheInuse, ok3 := idxs["/memory/classes/metadata/mcache/inuse:bytes"] + idxmcacheFree, ok4 := idxs["/memory/classes/metadata/mcache/free:bytes"] + + return &mspanMcache{ + enabled: ok1 && ok2 && ok3 && ok4, + idxmspanInuse: idxmspanInuse, + idxmspanFree: idxmspanFree, + idxmcacheInuse: idxmcacheInuse, + idxmcacheFree: idxmcacheFree, + } +} + +func (p *mspanMcache) name() string { return "mspan-mcache" } +func (p *mspanMcache) isEnabled() bool { return p.enabled } + +func (p *mspanMcache) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "MSpan/MCache", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "mspan in-use", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "mspan free", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "mcache in-use", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "mcache free", + Unitfmt: "%{y:.4s}B", + }, + }, + InfoText: `Mspan in-use is /memory/classes/metadata/mspan/inuse, the memory that is occupied by runtime mspan structures that are currently being used. +Mspan free is /memory/classes/metadata/mspan/free, the memory that is reserved for runtime mspan structures, but not in-use. +Mcache in-use is /memory/classes/metadata/mcache/inuse, the memory that is occupied by runtime mcache structures that are currently being used. +Mcache free is /memory/classes/metadata/mcache/free, the memory that is reserved for runtime mcache structures, but not in-use. +`, + } + s.Layout.Yaxis.Title = "bytes" + s.Layout.Yaxis.TickSuffix = "B" + return s +} + +func (p *mspanMcache) values(samples []metrics.Sample) any { + mspanInUse := samples[p.idxmspanInuse].Value.Uint64() + mspanSys := samples[p.idxmspanFree].Value.Uint64() + mcacheInUse := samples[p.idxmcacheInuse].Value.Uint64() + mcacheSys := samples[p.idxmcacheFree].Value.Uint64() + return []uint64{ + mspanInUse, + mspanSys, + mcacheInUse, + mcacheSys, + } +} + +/* + * goroutines + */ +var _ = registerRuntimePlot("goroutines", "/sched/goroutines:goroutines") + +type goroutines struct { + enabled bool + + idxgs int +} + +func makeGoroutinesPlot(idxs map[string]int) runtimeMetric { + idxgs, ok := idxs["/sched/goroutines:goroutines"] + + return &goroutines{ + enabled: ok, + idxgs: idxgs, + } +} + +func (p *goroutines) name() string { return "goroutines" } +func (p *goroutines) isEnabled() bool { return p.enabled } + +func (p *goroutines) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Goroutines", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "goroutines", + Unitfmt: "%{y}", + }, + }, + InfoText: "Goroutines is /sched/goroutines, the count of live goroutines.", + } + + s.Layout.Yaxis.Title = "goroutines" + return s +} + +func (p *goroutines) values(samples []metrics.Sample) any { + return []uint64{samples[p.idxgs].Value.Uint64()} +} + +/* + * size classes + */ +var _ = registerRuntimePlot("size-classes", + "/gc/heap/allocs-by-size:bytes", + "/gc/heap/frees-by-size:bytes", +) + +type sizeClasses struct { + enabled bool + sizeClasses []uint64 + + idxallocs int + idxfrees int +} + +func makeSizeClassesPlot(idxs map[string]int) runtimeMetric { + idxallocs, ok1 := idxs["/gc/heap/allocs-by-size:bytes"] + idxfrees, ok2 := idxs["/gc/heap/frees-by-size:bytes"] + + return &sizeClasses{ + enabled: ok1 && ok2, + idxallocs: idxallocs, + idxfrees: idxfrees, + } +} + +func (p *sizeClasses) name() string { return "size-classes" } +func (p *sizeClasses) isEnabled() bool { return p.enabled } + +func (p *sizeClasses) layout(samples []metrics.Sample) any { + // Perform a sanity check on the number of buckets on the 'allocs' and + // 'frees' size classes histograms. Statsviz plots a single histogram based + // on those 2 so we want them to have the same number of buckets, which + // should be true. + allocsBySize := samples[p.idxallocs].Value.Float64Histogram() + freesBySize := samples[p.idxfrees].Value.Float64Histogram() + if len(allocsBySize.Buckets) != len(freesBySize.Buckets) { + panic("different number of buckets in allocs and frees size classes histograms!") + } + + // Pre-allocate here so we never do it in values. + p.sizeClasses = make([]uint64, len(allocsBySize.Counts)) + + // No downsampling for the size classes histogram (factor=1) but we still + // need to adapt boundaries for plotly heatmaps. + buckets := downsampleBuckets(allocsBySize, 1) + + h := Heatmap{ + Name: p.name(), + Title: "Size Classes", + Type: "heatmap", + UpdateFreq: 5, + Colorscale: BlueShades, + Buckets: floatseq(len(buckets)), + CustomData: buckets, + Hover: HeapmapHover{ + YName: "size class", + YUnit: "bytes", + ZName: "objects", + }, + InfoText: `This heatmap shows the distribution of size classes, using /gc/heap/allocs-by-size and /gc/heap/frees-by-size.`, + Layout: HeatmapLayout{ + YAxis: HeatmapYaxis{ + Title: "size class", + TickMode: "array", + TickVals: []float64{1, 9, 17, 25, 31, 37, 43, 50, 58, 66}, + TickText: []float64{1 << 4, 1 << 7, 1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15}, + }, + }, + } + return h +} + +func (p *sizeClasses) values(samples []metrics.Sample) any { + allocsBySize := samples[p.idxallocs].Value.Float64Histogram() + freesBySize := samples[p.idxfrees].Value.Float64Histogram() + + for i := 0; i < len(p.sizeClasses); i++ { + p.sizeClasses[i] = allocsBySize.Counts[i] - freesBySize.Counts[i] + } + return p.sizeClasses +} + +/* + * gc pauses + */ +var _ = registerRuntimePlot("gc-pauses", "/gc/pauses:seconds") + +type gcpauses struct { + enabled bool + histfactor int + counts [maxBuckets]uint64 + + idxgcpauses int +} + +func makeGCPausesPlot(idxs map[string]int) runtimeMetric { + idxgcpauses, ok := idxs["/gc/pauses:seconds"] + + return &gcpauses{ + enabled: ok, + idxgcpauses: idxgcpauses, + } +} + +func (p *gcpauses) name() string { return "gc-pauses" } +func (p *gcpauses) isEnabled() bool { return p.enabled } + +func (p *gcpauses) layout(samples []metrics.Sample) any { + gcpauses := samples[p.idxgcpauses].Value.Float64Histogram() + p.histfactor = downsampleFactor(len(gcpauses.Buckets), maxBuckets) + buckets := downsampleBuckets(gcpauses, p.histfactor) + + h := Heatmap{ + Name: p.name(), + Title: "Stop-the-world Pause Latencies", + Type: "heatmap", + UpdateFreq: 5, + Colorscale: PinkShades, + Buckets: floatseq(len(buckets)), + CustomData: buckets, + Hover: HeapmapHover{ + YName: "pause duration", + YUnit: "duration", + ZName: "pauses", + }, + Layout: HeatmapLayout{ + YAxis: HeatmapYaxis{ + Title: "pause duration", + TickMode: "array", + TickVals: []float64{6, 13, 20, 26, 33, 39.5, 46, 53, 60, 66, 73, 79, 86}, + TickText: []float64{1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 5e-3, 1e-2, 5e-2, 1e-1, 5e-1, 1, 5, 10}, + }, + }, + InfoText: `This heatmap shows the distribution of individual GC-related stop-the-world pause latencies, uses /gc/pauses:seconds,.`, + } + return h +} + +func (p *gcpauses) values(samples []metrics.Sample) any { + gcpauses := samples[p.idxgcpauses].Value.Float64Histogram() + return downsampleCounts(gcpauses, p.histfactor, p.counts[:]) +} + +/* + * time spent in runnable state + */ +var _ = registerRuntimePlot("runnable-time", "/sched/latencies:seconds") + +type runnableTime struct { + enabled bool + histfactor int + counts [maxBuckets]uint64 + + idxschedlat int +} + +func makeRunnableTimePlot(idxs map[string]int) runtimeMetric { + idxschedlat, ok := idxs["/sched/latencies:seconds"] + + return &runnableTime{ + enabled: ok, + idxschedlat: idxschedlat, + } +} + +func (p *runnableTime) name() string { return "runnable-time" } +func (p *runnableTime) isEnabled() bool { return p.enabled } + +func (p *runnableTime) layout(samples []metrics.Sample) any { + schedlat := samples[p.idxschedlat].Value.Float64Histogram() + p.histfactor = downsampleFactor(len(schedlat.Buckets), maxBuckets) + buckets := downsampleBuckets(schedlat, p.histfactor) + + h := Heatmap{ + Name: p.name(), + Title: "Time Goroutines Spend in 'Runnable' state", + Type: "heatmap", + UpdateFreq: 5, + Colorscale: GreenShades, + Buckets: floatseq(len(buckets)), + CustomData: buckets, + Hover: HeapmapHover{ + YName: "duration", + YUnit: "duration", + ZName: "goroutines", + }, + Layout: HeatmapLayout{ + YAxis: HeatmapYaxis{ + Title: "duration", + TickMode: "array", + TickVals: []float64{6, 13, 20, 26, 33, 39.5, 46, 53, 60, 66, 73, 79, 86}, + TickText: []float64{1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 5e-3, 1e-2, 5e-2, 1e-1, 5e-1, 1, 5, 10}, + }, + }, + InfoText: `This heatmap shows the distribution of the time goroutines have spent in the scheduler in a runnable state before actually running, uses /sched/latencies:seconds.`, + } + + return h +} + +func (p *runnableTime) values(samples []metrics.Sample) any { + schedlat := samples[p.idxschedlat].Value.Float64Histogram() + + return downsampleCounts(schedlat, p.histfactor, p.counts[:]) +} + +/* + * scheduling events + */ +var _ = registerRuntimePlot("sched-events", + "/sched/latencies:seconds", + "/sched/gomaxprocs:threads", +) + +type schedEvents struct { + enabled bool + + idxschedlat int + idxGomaxprocs int + lasttot uint64 +} + +func makeSchedEventsPlot(idxs map[string]int) runtimeMetric { + idxschedlat, ok1 := idxs["/sched/latencies:seconds"] + idxGomaxprocs, ok2 := idxs["/sched/gomaxprocs:threads"] + + return &schedEvents{ + enabled: ok1 && ok2, + idxschedlat: idxschedlat, + idxGomaxprocs: idxGomaxprocs, + lasttot: math.MaxUint64, + } +} + +func (p *schedEvents) name() string { return "sched-events" } +func (p *schedEvents) isEnabled() bool { return p.enabled } + +func (p *schedEvents) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Goroutine Scheduling Events", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "events per unit of time", + Unitfmt: "%{y}", + }, + { + Name: "events per unit of time, per P", + Unitfmt: "%{y}", + }, + }, + InfoText: `Events per second is the sum of all buckets in /sched/latencies:seconds, that is, it tracks the total number of goroutine scheduling events. That number is multiplied by the constant 8. +Events per second per P (processor) is Events per second divided by current GOMAXPROCS, from /sched/gomaxprocs:threads. +NOTE: the multiplying factor comes from internal Go runtime source code and might change from version to version.`, + } + s.Layout.Yaxis.Title = "events" + return s +} + +// gTrackingPeriod is currently always 8. Guard it behind build tags when that +// changes. See https://github.com/golang/go/blob/go1.18.4/src/runtime/runtime2.go#L502-L504 +const currentGtrackingPeriod = 8 + +// TODO show scheduling events per seconds +func (p *schedEvents) values(samples []metrics.Sample) any { + schedlat := samples[p.idxschedlat].Value.Float64Histogram() + gomaxprocs := samples[p.idxGomaxprocs].Value.Uint64() + + total := uint64(0) + for _, v := range schedlat.Counts { + total += v + } + total *= currentGtrackingPeriod + + curtot := total - p.lasttot + if p.lasttot == math.MaxUint64 { + // We don't want a big spike at statsviz launch in case the process has + // been running for some time and curtot is high. + curtot = 0 + } + p.lasttot = total + + ftot := float64(curtot) + + return []float64{ + ftot, + ftot / float64(gomaxprocs), + } +} + +/* + * cgo + */ +var _ = registerRuntimePlot("cgo", "/cgo/go-to-c-calls:calls") + +type cgo struct { + enabled bool + idxgo2c int + lastgo2c uint64 +} + +func makeCGOPlot(idxs map[string]int) runtimeMetric { + idxgo2c, ok := idxs["/cgo/go-to-c-calls:calls"] + + return &cgo{ + enabled: ok, + idxgo2c: idxgo2c, + lastgo2c: math.MaxUint64, + } +} + +func (p *cgo) name() string { return "cgo" } +func (p *cgo) isEnabled() bool { return p.enabled } + +func (p *cgo) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "CGO Calls", + Type: "bar", + Subplots: []Subplot{ + { + Name: "calls from go to c", + Unitfmt: "%{y}", + Color: "red", + }, + }, + InfoText: "Shows the count of calls made from Go to C by the current process, per unit of time. Uses /cgo/go-to-c-calls:calls", + } + + s.Layout.Yaxis.Title = "calls" + return s +} + +// TODO show cgo calls per second +func (p *cgo) values(samples []metrics.Sample) any { + go2c := samples[p.idxgo2c].Value.Uint64() + curgo2c := go2c - p.lastgo2c + if p.lastgo2c == math.MaxUint64 { + curgo2c = 0 + } + p.lastgo2c = go2c + + return []uint64{curgo2c} +} + +/* + * gc stack size + */ +var _ = registerRuntimePlot("gc-stack-size", "/gc/stack/starting-size:bytes") + +type gcStackSize struct { + enabled bool + idxstack int +} + +func makeGCStackSizePlot(idxs map[string]int) runtimeMetric { + idxstack, ok := idxs["/gc/stack/starting-size:bytes"] + + return &gcStackSize{ + enabled: ok, + idxstack: idxstack, + } +} + +func (p *gcStackSize) name() string { return "gc-stack-size" } +func (p *gcStackSize) isEnabled() bool { return p.enabled } + +func (p *gcStackSize) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Starting Size of Goroutines Stacks", + Type: "scatter", + Subplots: []Subplot{ + { + Name: "new goroutines stack size", + Unitfmt: "%{y:.4s}B", + }, + }, + InfoText: "Shows the stack size of new goroutines, uses /gc/stack/starting-size:bytes", + } + + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *gcStackSize) values(samples []metrics.Sample) any { + stackSize := samples[p.idxstack].Value.Uint64() + return []uint64{stackSize} +} + +/* + * GC cycles + */ +var _ = registerRuntimePlot("gc-cycles", + "/gc/cycles/automatic:gc-cycles", + "/gc/cycles/forced:gc-cycles", + "/gc/cycles/total:gc-cycles", +) + +type gcCycles struct { + enabled bool + + idxAutomatic int + idxForced int + idxTotal int + + lastAuto, lastForced, lastTotal uint64 +} + +func makeGCCyclesPlot(idxs map[string]int) runtimeMetric { + idxAutomatic, ok1 := idxs["/gc/cycles/automatic:gc-cycles"] + idxForced, ok2 := idxs["/gc/cycles/forced:gc-cycles"] + idxTotal, ok3 := idxs["/gc/cycles/total:gc-cycles"] + + return &gcCycles{ + enabled: ok1 && ok2 && ok3, + idxAutomatic: idxAutomatic, + idxForced: idxForced, + idxTotal: idxTotal, + } +} + +func (p *gcCycles) name() string { return "gc-cycles" } +func (p *gcCycles) isEnabled() bool { return p.enabled } + +func (p *gcCycles) layout(_ []metrics.Sample) any { + return Scatter{ + Name: p.name(), + Title: "Completed GC Cycles", + Type: "bar", + Subplots: []Subplot{ + { + Name: "automatic", + Unitfmt: "%{y}", + Type: "bar", + }, + { + Name: "forced", + Unitfmt: "%{y}", + Type: "bar", + }, + }, + InfoText: `Number of completed GC cycles, either forced of generated by the Go runtime.`, + Layout: ScatterLayout{ + BarMode: "stack", + Yaxis: ScatterYAxis{ + Title: "cycles", + }, + }, + } +} + +func (p *gcCycles) values(samples []metrics.Sample) any { + total := samples[p.idxTotal].Value.Uint64() + auto := samples[p.idxAutomatic].Value.Uint64() + forced := samples[p.idxForced].Value.Uint64() + + if p.lastTotal == 0 { + p.lastTotal = total + p.lastForced = forced + p.lastAuto = auto + return []uint64{0, 0} + } + + ret := []uint64{ + auto - p.lastAuto, + forced - p.lastForced, + } + + p.lastForced = forced + p.lastAuto = auto + + return ret +} + +/* +* mspan mcache + */ +var _ = registerRuntimePlot("memory-classes", + "/memory/classes/os-stacks:bytes", + "/memory/classes/other:bytes", + "/memory/classes/profiling/buckets:bytes", + "/memory/classes/total:bytes", +) + +type memoryClasses struct { + enabled bool + + idxOSStacks int + idxOther int + idxProfBuckets int + idxTotal int +} + +func makeMemoryClassesPlot(idxs map[string]int) runtimeMetric { + idxOSStacks, ok1 := idxs["/memory/classes/os-stacks:bytes"] + idxOther, ok2 := idxs["/memory/classes/other:bytes"] + idxProfBuckets, ok3 := idxs["/memory/classes/profiling/buckets:bytes"] + idxTotal, ok4 := idxs["/memory/classes/total:bytes"] + + return &memoryClasses{ + enabled: ok1 && ok2 && ok3 && ok4, + idxOSStacks: idxOSStacks, + idxOther: idxOther, + idxProfBuckets: idxProfBuckets, + idxTotal: idxTotal, + } +} + +func (p *memoryClasses) name() string { return "memory-classes" } +func (p *memoryClasses) isEnabled() bool { return p.enabled } + +func (p *memoryClasses) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Memory classes", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "os stacks", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "other", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "profiling buckets", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "total", + Unitfmt: "%{y:.4s}B", + }, + }, + + InfoText: `OS stacks is /memory/classes/os-stacks, stack memory allocated by the underlying operating system. +Other is /memory/classes/other, memory used by execution trace buffers, structures for debugging the runtime, finalizer and profiler specials, and more. +Profiling buckets is /memory/classes/profiling/buckets, memory that is used by the stack trace hash map used for profiling. +Total is /memory/classes/total, all memory mapped by the Go runtime into the current process as read-write.`, + } + s.Layout.Yaxis.Title = "bytes" + s.Layout.Yaxis.TickSuffix = "B" + return s +} + +func (p *memoryClasses) values(samples []metrics.Sample) any { + osStacks := samples[p.idxOSStacks].Value.Uint64() + other := samples[p.idxOther].Value.Uint64() + profBuckets := samples[p.idxProfBuckets].Value.Uint64() + total := samples[p.idxTotal].Value.Uint64() + + return []uint64{ + osStacks, + other, + profBuckets, + total, + } +} + +/* +* cpu classes (gc) + */ +var _ = registerRuntimePlot("cpu-classes-gc", + "/cpu/classes/gc/mark/assist:cpu-seconds", + "/cpu/classes/gc/mark/dedicated:cpu-seconds", + "/cpu/classes/gc/mark/idle:cpu-seconds", + "/cpu/classes/gc/pause:cpu-seconds", + "/cpu/classes/gc/total:cpu-seconds", +) + +type cpuClassesGC struct { + enabled bool + + idxMarkAssist int + idxMarkDedicated int + idxMarkIdle int + idxPause int + idxTotal int + + lastTime time.Time + + lastMarkAssist float64 + lastMarkDedicated float64 + lastMarkIdle float64 + lastPause float64 + lastTotal float64 +} + +func makeCPUClassesGCPlot(idxs map[string]int) runtimeMetric { + idxMarkAssist, ok1 := idxs["/cpu/classes/gc/mark/assist:cpu-seconds"] + idxMarkDedicated, ok2 := idxs["/cpu/classes/gc/mark/dedicated:cpu-seconds"] + idxMarkIdle, ok3 := idxs["/cpu/classes/gc/mark/idle:cpu-seconds"] + idxPause, ok4 := idxs["/cpu/classes/gc/pause:cpu-seconds"] + idxTotal, ok5 := idxs["/cpu/classes/gc/total:cpu-seconds"] + + return &cpuClassesGC{ + enabled: ok1 && ok2 && ok3 && ok4 && ok5, + idxMarkAssist: idxMarkAssist, + idxMarkDedicated: idxMarkDedicated, + idxMarkIdle: idxMarkIdle, + idxPause: idxPause, + idxTotal: idxTotal, + } +} + +func (p *cpuClassesGC) name() string { return "cpu-classes-gc" } +func (p *cpuClassesGC) isEnabled() bool { return p.enabled } + +func (p *cpuClassesGC) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "CPU classes (GC)", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "mark assist", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "mark dedicated", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "mark idle", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "pause", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "total", + Unitfmt: "%{y:.4s}s", + }, + }, + + InfoText: `Cumulative metrics are converted to rates by Statsviz so as to be more easily comparable and readable. +All this metrics are overestimates, and not directly comparable to system CPU time measurements. Compare only with other /cpu/classes metrics. + +mark assist is /cpu/classes/gc/mark/assist, estimated total CPU time goroutines spent performing GC tasks to assist the GC and prevent it from falling behind the application. +mark dedicated is /cpu/classes/gc/mark/dedicated, Estimated total CPU time spent performing GC tasks on processors (as defined by GOMAXPROCS) dedicated to those tasks. +mark idle is /cpu/classes/gc/mark/idle, estimated total CPU time spent performing GC tasks on spare CPU resources that the Go scheduler could not otherwise find a use for. +pause is /cpu/classes/gc/pause, estimated total CPU time spent with the application paused by the GC. +total is /cpu/classes/gc/total, estimated total CPU time spent performing GC tasks.`, + } + s.Layout.Yaxis.Title = "cpu-seconds per seconds" + s.Layout.Yaxis.TickSuffix = "s" + return s +} + +func (p *cpuClassesGC) values(samples []metrics.Sample) any { + curMarkAssist := samples[p.idxMarkAssist].Value.Float64() + curMarkDedicated := samples[p.idxMarkDedicated].Value.Float64() + curMarkIdle := samples[p.idxMarkIdle].Value.Float64() + curPause := samples[p.idxPause].Value.Float64() + curTotal := samples[p.idxTotal].Value.Float64() + + if p.lastTime.IsZero() { + p.lastMarkAssist = curMarkAssist + p.lastMarkDedicated = curMarkDedicated + p.lastMarkIdle = curMarkIdle + p.lastPause = curPause + p.lastTotal = curTotal + p.lastTime = time.Now() + + return []float64{0, 0, 0, 0, 0} + } + + t := time.Since(p.lastTime).Seconds() + + markAssist := (curMarkAssist - p.lastMarkAssist) / t + markDedicated := (curMarkDedicated - p.lastMarkDedicated) / t + markIdle := (curMarkIdle - p.lastMarkIdle) / t + pause := (curPause - p.lastPause) / t + total := (curTotal - p.lastTotal) / t + + p.lastMarkAssist = curMarkAssist + p.lastMarkDedicated = curMarkDedicated + p.lastMarkIdle = curMarkIdle + p.lastPause = curPause + p.lastTotal = curTotal + p.lastTime = time.Now() + + return []float64{ + markAssist, + markDedicated, + markIdle, + pause, + total, + } +} + +/* +* mutex wait + */ +var _ = registerRuntimePlot("mutex-wait", + "/sync/mutex/wait/total:seconds", +) + +type mutexWait struct { + enabled bool + idxMutexWait int + + lastTime time.Time + lastMutexWait float64 +} + +func makeMutexWaitPlot(idxs map[string]int) runtimeMetric { + idxMutexWait, ok := idxs["/cpu/classes/gc/mark/assist:cpu-seconds"] + + return &mutexWait{ + enabled: ok, + idxMutexWait: idxMutexWait, + } +} + +func (p *mutexWait) name() string { return "mutex-wait" } +func (p *mutexWait) isEnabled() bool { return p.enabled } + +func (p *mutexWait) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Time Goroutines Spend Blocked on Mutexes", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "mutex wait", + Unitfmt: "%{y:.4s}s", + }, + }, + + InfoText: `Cumulative metrics are converted to rates by Statsviz so as to be more easily comparable and readable. +mutex wait is /sync/mutex/wait/total, approximate cumulative time goroutines have spent blocked on a sync.Mutex or sync.RWMutex. + +This metric is useful for identifying global changes in lock contention. Collect a mutex or block profile using the runtime/pprof package for more detailed contention data.`, + } + s.Layout.Yaxis.Title = "seconds per seconds" + s.Layout.Yaxis.TickSuffix = "s" + return s +} + +func (p *mutexWait) values(samples []metrics.Sample) any { + if p.lastTime.IsZero() { + p.lastTime = time.Now() + p.lastMutexWait = samples[p.idxMutexWait].Value.Float64() + + return []float64{0} + } + + t := time.Since(p.lastTime).Seconds() + + mutexWait := (samples[p.idxMutexWait].Value.Float64() - p.lastMutexWait) / t + + p.lastMutexWait = samples[p.idxMutexWait].Value.Float64() + p.lastTime = time.Now() + + return []float64{ + mutexWait, + } +} + +/* + * gc scan + */ +var _ = registerRuntimePlot("gc-scan", + "/gc/scan/globals:bytes", + "/gc/scan/heap:bytes", + "/gc/scan/stack:bytes", + "/gc/scan/total:bytes", +) + +type gcScan struct { + enabled bool + + idxGlobals int + idxHeap int + idxStack int +} + +func makeGCScanPlot(idxs map[string]int) runtimeMetric { + idxGlobals, ok1 := idxs["/gc/scan/globals:bytes"] + idxHeap, ok2 := idxs["/gc/scan/heap:bytes"] + idxStack, ok3 := idxs["/gc/scan/stack:bytes"] + + return &gcScan{ + enabled: ok1 && ok2 && ok3, + idxGlobals: idxGlobals, + idxHeap: idxHeap, + idxStack: idxStack, + } +} + +func (p *gcScan) name() string { return "gc-scan" } +func (p *gcScan) isEnabled() bool { return p.enabled } + +func (p *gcScan) layout(_ []metrics.Sample) any { + return Scatter{ + Name: p.name(), + Title: "GC Scan", + Type: "bar", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "scannable globals", + Unitfmt: "%{y:.4s}B", + Type: "bar", + }, + { + Name: "scannable heap", + Unitfmt: "%{y:.4s}B", + Type: "bar", + }, + { + Name: "scanned stack", + Unitfmt: "%{y:.4s}B", + Type: "bar", + }, + }, + InfoText: `This plot shows the amount of memory that is scannable by the GC. +scannable globals is /gc/scan/globals, the total amount of global variable space that is scannable. +scannable heap is /gc/scan/heap, the total amount of heap space that is scannable. +scanned stack is /gc/scan/stack, the number of bytes of stack that were scanned last GC cycle. +`, + Layout: ScatterLayout{ + BarMode: "stack", + Yaxis: ScatterYAxis{ + TickSuffix: "B", + Title: "bytes", + }, + }, + } +} + +func (p *gcScan) values(samples []metrics.Sample) any { + globals := samples[p.idxGlobals].Value.Uint64() + heap := samples[p.idxHeap].Value.Uint64() + stack := samples[p.idxStack].Value.Uint64() + return []uint64{ + globals, + heap, + stack, + } +} + +/* + * helpers + */ + +func floatseq(n int) []float64 { + seq := make([]float64, n) + for i := 0; i < n; i++ { + seq[i] = float64(i) + } + return seq +} diff --git a/vendor/github.com/arl/statsviz/internal/plot/plots_list.go b/vendor/github.com/arl/statsviz/internal/plot/plots_list.go new file mode 100644 index 00000000..a2534209 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/plot/plots_list.go @@ -0,0 +1,162 @@ +package plot + +import ( + "encoding/json" + "fmt" + "io" + "runtime/debug" + "runtime/metrics" + "sync" + "time" +) + +var ( + names map[string]bool + usedMetrics map[string]struct{} +) + +type plotFunc func(idxs map[string]int) runtimeMetric + +var plotFuncs []plotFunc + +func registerPlotFunc(f plotFunc) { + plotFuncs = append(plotFuncs, f) +} + +func registerRuntimePlot(name string, metrics ...string) bool { + if names == nil { + names = make(map[string]bool) + usedMetrics = make(map[string]struct{}) + } + if names[name] { + panic(name + " is an already reserved plot name") + } + names[name] = true + + // Record the metrics we use. + for _, m := range metrics { + usedMetrics[m] = struct{}{} + } + + return true +} + +func IsReservedPlotName(name string) bool { + return names[name] +} + +type runtimeMetric interface { + name() string + isEnabled() bool + layout([]metrics.Sample) any + values([]metrics.Sample) any +} + +// List holds all the plots that statsviz knows about. Some plots might be +// disabled, if they rely on metrics that are unknown to the current Go version. +type List struct { + rtPlots []runtimeMetric + userPlots []UserPlot + + once sync.Once // ensure Config is called once + cfg *Config + + idxs map[string]int // map metrics name to idx in samples and descs + descs []metrics.Description + + mu sync.Mutex // protects samples in case of concurrent calls to WriteValues + samples []metrics.Sample +} + +func NewList(userPlots []UserPlot) (*List, error) { + if name := hasDuplicatePlotNames(userPlots); name != "" { + return nil, fmt.Errorf("duplicate plot name %s", name) + } + + descs := metrics.All() + pl := &List{ + idxs: make(map[string]int), + descs: descs, + samples: make([]metrics.Sample, len(descs)), + userPlots: userPlots, + } + for i := range pl.samples { + pl.samples[i].Name = pl.descs[i].Name + pl.idxs[pl.samples[i].Name] = i + } + metrics.Read(pl.samples) + + return pl, nil +} + +func (pl *List) Config() *Config { + pl.once.Do(func() { + pl.rtPlots = make([]runtimeMetric, 0, len(plotFuncs)) + for _, f := range plotFuncs { + pl.rtPlots = append(pl.rtPlots, f(pl.idxs)) + } + + layouts := make([]any, 0, len(pl.rtPlots)) + for i := range pl.rtPlots { + if pl.rtPlots[i].isEnabled() { + layouts = append(layouts, pl.rtPlots[i].layout(pl.samples)) + } + } + + pl.cfg = &Config{ + Events: []string{"lastgc"}, + Series: layouts, + } + + // User plots go at the back of the list for now. + for i := range pl.userPlots { + pl.cfg.Series = append(pl.cfg.Series, pl.userPlots[i].Layout()) + } + }) + return pl.cfg +} + +// WriteValues writes into w a JSON object containing the data points for all +// plots at the current instant. +func (pl *List) WriteValues(w io.Writer) error { + pl.mu.Lock() + defer pl.mu.Unlock() + + metrics.Read(pl.samples) + + // lastgc time series is used as source to represent garbage collection + // timestamps as vertical bars on certain plots. + gcStats := debug.GCStats{} + debug.ReadGCStats(&gcStats) + + m := map[string]any{ + // javascript timestampts are in milliseconds + "lastgc": []int64{gcStats.LastGC.UnixMilli()}, + "timestamp": time.Now().UnixMilli(), + } + + for _, p := range pl.rtPlots { + if p.isEnabled() { + m[p.name()] = p.values(pl.samples) + } + } + + for i := range pl.userPlots { + up := &pl.userPlots[i] + switch { + case up.Scatter != nil: + vals := make([]float64, len(up.Scatter.Funcs)) + for i := range up.Scatter.Funcs { + vals[i] = up.Scatter.Funcs[i]() + } + m[up.Scatter.Plot.Name] = vals + case up.Heatmap != nil: + panic("unimplemented") + } + } + + if err := json.NewEncoder(w).Encode(m); err != nil { + return fmt.Errorf("failed to write/convert metrics values to json: %v", err) + } + return nil +} diff --git a/vendor/github.com/arl/statsviz/internal/plot/user.go b/vendor/github.com/arl/statsviz/internal/plot/user.go new file mode 100644 index 00000000..08d13b3f --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/plot/user.go @@ -0,0 +1,48 @@ +package plot + +type ScatterUserPlot struct { + Plot Scatter + Funcs []func() float64 +} + +type HeatmapUserPlot struct { + Plot Heatmap + // TODO(arl): heatmap get value func +} + +type UserPlot struct { + Scatter *ScatterUserPlot + Heatmap *HeatmapUserPlot +} + +func (up UserPlot) Layout() any { + switch { + case (up.Scatter != nil) == (up.Heatmap != nil): + panic("userplot must be a timeseries or a heatmap") + case up.Scatter != nil: + return up.Scatter.Plot + case up.Heatmap != nil: + return up.Heatmap.Plot + } + + panic("unreachable") +} + +func hasDuplicatePlotNames(userPlots []UserPlot) string { + names := map[string]bool{} + for _, p := range userPlots { + name := "" + if p.Scatter != nil { + name = p.Scatter.Plot.Name + } else if p.Heatmap != nil { + name = p.Heatmap.Plot.Name + } else { + panic("both heapmap and scatter are nil") + } + if names[name] { + return name + } + names[name] = true + } + return "" +} diff --git a/vendor/github.com/arl/statsviz/internal/static/css/statsviz.css b/vendor/github.com/arl/statsviz/internal/static/css/statsviz.css new file mode 100644 index 00000000..d9c6e2ab --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/css/statsviz.css @@ -0,0 +1,62 @@ +html, +body { + height: 100%; + margin: 0; +} + +nav.navbar { + background-color: #f8f9fa; +} + +body.dark-theme { + color: white; + background-color: #282a2c; +} + +body.dark-theme nav.navbar span.action svg { + color: lightgray; +} + +#header { + text-align: center; +} + +.container-xxl { + width: 2190px; +} + +.tooltip-table { + display: table; + width: 160px; + border-collapse: collapse; +} + +.tooltip-style { + font-family: "Open Sans", verdana, arial, sans-serif; + font-size: 12px; + fill: rgb(68, 68, 68); + fill-opacity: 1; + white-space: pre; +} + +.tooltip-row { + display: table-row; +} + +.tooltip-value, +.tooltip-label { + display: table-cell; + padding: 0.3em; + /* border: #f0f0f0 1px solid; */ +} + +.tooltip-label { + font-weight: bold; +} + +.plots-wrapper { + display: grid; + height: 100%; + /* column must be same as plotWidth (plot.js) */ + grid-template-columns: repeat(auto-fill, minmax(0px, 630px)); +} \ No newline at end of file diff --git a/vendor/github.com/arl/statsviz/internal/static/fs.go b/vendor/github.com/arl/statsviz/internal/static/fs.go new file mode 100644 index 00000000..835ca00f --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/fs.go @@ -0,0 +1,6 @@ +package static + +import "embed" + +//go:embed * +var Assets embed.FS diff --git a/vendor/github.com/arl/statsviz/internal/static/index.html b/vendor/github.com/arl/statsviz/internal/static/index.html new file mode 100644 index 00000000..f032b78c --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/index.html @@ -0,0 +1,61 @@ + + + + + Statsviz + + + + + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/vendor/github.com/arl/statsviz/internal/static/js/app.js b/vendor/github.com/arl/statsviz/internal/static/js/app.js new file mode 100644 index 00000000..72ba5b2c --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/js/app.js @@ -0,0 +1,145 @@ +import * as stats from './stats.js'; +import * as plot from "./plot.js"; +import * as theme from "./theme.js"; +import PlotsDef from './plotsdef.js'; + +const buildWebsocketURI = () => { + var loc = window.location, + ws_prot = "ws:"; + if (loc.protocol === "https:") { + ws_prot = "wss:"; + } + return ws_prot + "//" + loc.host + loc.pathname + "ws" +} + +const dataRetentionSeconds = 600; +var timeout = 250; + +const clamp = (val, min, max) => { + if (val < min) return min; + if (val > max) return max; + return val; +} + +/* nav bar ui management */ +let paused = false; +let show_gc = true; +let timerange = 60; + +/* WebSocket connection handling */ +const connect = () => { + const uri = buildWebsocketURI(); + let ws = new WebSocket(uri); + console.info(`Attempting websocket connection to server at ${uri}`); + + ws.onopen = () => { + console.info("Successfully connected"); + timeout = 250; // reset connection timeout for next time + }; + + ws.onclose = event => { + console.error(`Closed websocket connection: code ${event.code}`); + setTimeout(connect, clamp(timeout += timeout, 250, 5000)); + }; + + ws.onerror = err => { + console.error(`Websocket error, closing connection.`); + ws.close(); + }; + + let initDone = false; + ws.onmessage = event => { + let data = JSON.parse(event.data) + + if (!initDone) { + configurePlots(PlotsDef); + stats.init(PlotsDef, dataRetentionSeconds); + + attachPlots(); + + $('#play_pause').change(() => { paused = !paused; }); + $('#show_gc').change(() => { + show_gc = !show_gc; + updatePlots(); + }); + $('#select_timerange').click(() => { + const val = parseInt($("#select_timerange option:selected").val(), 10); + timerange = val; + updatePlots(); + }); + initDone = true; + return; + } + + stats.pushData(data); + if (paused) { + return + } + updatePlots(PlotsDef.events); + } +} + +connect(); + +let plots = []; + +const configurePlots = (plotdefs) => { + plots = []; + plotdefs.series.forEach(plotdef => { + plots.push(new plot.Plot(plotdef)); + }); +} + +const attachPlots = () => { + let plotsDiv = $('#plots'); + plotsDiv.empty(); + + for (let i = 0; i < plots.length; i++) { + const plot = plots[i]; + let div = $(`
`); + plot.createElement(div[0], i) + plotsDiv.append(div); + } +} + +const updatePlots = () => { + // Create shapes. + let shapes = new Map(); + + let data = stats.slice(timerange); + + if (show_gc) { + for (const [name, serie] of data.events) { + shapes.set(name, plot.createVerticalLines(serie)); + } + } + + // Always show the full range on x axis. + const now = data.times[data.times.length - 1]; + let xrange = [now - timerange * 1000, now]; + + plots.forEach(plot => { + if (!plot.hidden) { + plot.update(xrange, data, shapes); + } + }); +} + +const updatePlotsLayout = () => { + plots.forEach(plot => { + plot.updateTheme(); + }); +} + +theme.updateThemeMode(); + +/** + * Change color theme when the user presses the theme switch button + */ +$('#color_theme_sw').change(() => { + const themeMode = theme.getThemeMode(); + const newTheme = themeMode === "dark" && "light" || "dark"; + localStorage.setItem("theme-mode", newTheme); + theme.updateThemeMode(); + updatePlotsLayout(); +}); diff --git a/vendor/github.com/arl/statsviz/internal/static/js/buffer.js b/vendor/github.com/arl/statsviz/internal/static/js/buffer.js new file mode 100644 index 00000000..7b205aea --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/js/buffer.js @@ -0,0 +1,46 @@ +// Buffer declares the Buffer class. +export default class Buffer { + constructor(len, cap) { + if (cap - len < 0) { + throw "cap - len must be positive"; + } + // TODO(arl): value using TypedArray rather than Array here + this._buf = new Array(cap); + this._pos = 0; + this._len = len; + this._cap = cap; + } + last() { + if (this.length() == 0) { + throw 'Cannot call last() on an empty Buffer'; + } + return this._buf[this._pos]; + } + push(pt) { + if (this._pos >= this._cap) { + // move data to the beginning of the buffer, effectively discarding + // the cap-len oldest elements + this._buf.copyWithin(0, this._cap - this._len); + this._pos = this._len; + } + this._buf[this._pos] = pt; + this._pos++; + } + length() { + if (this._pos > this._len) { + return this._len; + } + return this._pos; + } + + // slice returns a slice of the len latest datapoints present in the buffer. + slice(len) { + // Cap the dimension of the returned slice to the data available + if (len > this.length()) { + len = this.length(); + } + + let start = this._pos - len; + return this._buf.slice(start, start + len); + } +}; diff --git a/vendor/github.com/arl/statsviz/internal/static/js/plot.js b/vendor/github.com/arl/statsviz/internal/static/js/plot.js new file mode 100644 index 00000000..ae478749 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/js/plot.js @@ -0,0 +1,400 @@ +import * as theme from "./theme.js"; + +var infoIcon = { + 'width': 470, + 'height': 530, + 'path': 'M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z' +} + +const newConfigObject = (cfg) => { + return { + // showEditInChartStudio: true, + // plotlyServerURL: "https://chart-studio.plotly.com", + displaylogo: false, + modeBarButtonsToRemove: ['2D', 'zoom2d', 'pan2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d', 'toggleSpikelines'], + modeBarButtonsToAdd: [{ + name: 'info', + title: "Plot info", + icon: infoIcon, + val: false, + click: handleInfoButton, + },], + toImageButtonOptions: { + format: 'png', + filename: cfg.name, + } + } +} + +const copyArrayOrNull = (o) => { + return Array.isArray(o) && [...o] || null; +} + +const newLayoutObject = (cfg) => { + const layout = { + title: { + y: 0.88, + font: { + family: "Roboto", + size: 18, + }, + text: cfg.title, + }, + margin: { + t: 80, + }, + paper_bgcolor: cfg.layout.paper_bgcolor, + plot_bgcolor: cfg.layout.plot_bgcolor, + font: { + color: cfg.layout.font_color + }, + width: 630, + height: 450, + hovermode: 'x', + barmode: cfg.layout.barmode, + xaxis: { + tickformat: '%H:%M:%S', + type: "date", + fixedrange: true, + autorange: false, + }, + yaxis: { + exponentformat: 'SI', + tickmode: cfg.layout.yaxis.tickmode, + ticktext: copyArrayOrNull(cfg.layout.yaxis.ticktext), + tickvals: copyArrayOrNull(cfg.layout.yaxis.tickvals), + title: cfg.layout.yaxis.title, + ticksuffix: cfg.layout.yaxis.ticksuffix, + fixedrange: true, + }, + showlegend: true, + legend: { + "orientation": "h" + } + }; + + if (layout.yaxis.tickmode == "array") { + // Format yaxis ticks + const formatYUnit = formatFunction(cfg.hover.yunit); + for (let i = 0; i < layout.yaxis.ticktext.length; i++) { + layout.yaxis.ticktext[i] = formatYUnit(layout.yaxis.ticktext[i]); + } + } + + return layout; +} + +const handleInfoButton = (gd, ev) => { + let button = ev.currentTarget; + let val = (button.getAttribute('data-val') === 'true'); + + const options = { + allowHTML: true, + trigger: 'click', + }; + + const instance = tippy(ev.currentTarget, options); + instance.setContent("
" + gd.infoText + "
"); + if (val) { + instance.hide(); + } else { + instance.show(); + } + button.setAttribute('data-val', !val); +} + +const themeColors = { + light: { + paper_bgcolor: '#f8f8f8', + plot_bgcolor: '#ffffdd', + font_color: '#434343' + }, + dark: { + paper_bgcolor: '#181a1c', + plot_bgcolor: '#282a2c', + font_color: '#fff' + } +}; + +/* + Plot configuration object: + { + "name": string, // internal name + "title": string, // plot title + "type": 'scatter'|'bar'|'heatmap' + "updateFreq": int, // datapoints to receive before redrawing the plot. (default: 1) + "infoText": string, // text showed in the plot 'info' tooltip + "events": "lastgc", // source of vertical lines (example: 'lastgc') + "layout": object, // (depends on plot type) + "subplots": array, // describe 'traces', only for 'scatter' or 'bar' plots + "heatmap": object, // heatmap details + } + + Layout for 'scatter' and 'bar' plots: + { + "yaxis": { + "title": { + "text": "bytes" // yaxis title + }, + "ticksuffix": "B", // base unit for ticks + }, + "barmode": "stack", // 'stack' or 'group' (only for bar plots) + }, + + Layout" for heatmaps: + { + "yaxis": { + tickmode: string (supports 'array' only) + tickvals: []float64 + ticktext: []float64 + "title": { + "text": "size class" + } + } + + Subplots show the potentially multiple trace objects for 'scatter' and 'bar' + plots. Each trace is an object: + { + "name": string; // internal name + "unitfmt": string, // d3 format string for tooltip + "stackgroup": string, // stackgroup (if stacked line any) + "hoveron": string // useful for stacked only (TODO(arl): remove from go) + "color": colorstring // plot/trace color + } + + Heatmap details object + { + "colorscale": array // array of weighted colors, + "buckets": array + "hover": { + "yname": string, // y axis units + "yunit": "bytes", // y axis name + "zname": "objects" // z axis name + } + } +*/ + + +class Plot { + /** + * Construct a new Plot object, wrapping a Plotly chart. See above + * documentation for plot configuration. + */ + + constructor(cfg) { + cfg.layout.paper_bgcolor = themeColors[theme.getThemeMode()].paper_bgcolor; + cfg.layout.plot_bgcolor = themeColors[theme.getThemeMode()].plot_bgcolor; + cfg.layout.font_color = themeColors[theme.getThemeMode()].font_color; + + this._cfg = cfg; + this._updateCount = 0; + this._dataTemplate = []; + this._lastData = [{ x: new Date() }]; + + if (this._cfg.type == 'heatmap') { + this._dataTemplate.push({ + type: 'heatmap', + x: null, + y: this._cfg.buckets, + z: null, + showlegend: false, + colorscale: this._cfg.colorscale, + custom_data: this._cfg.custom_data, + }); + } else { + this._cfg.subplots.forEach(subplot => { + this._dataTemplate.push({ + type: this._cfg.type, + x: null, + y: null, + name: subplot.name, + hovertemplate: `${subplot.unitfmt}`, + }) + }); + } + + this._plotlyLayout = newLayoutObject(cfg); + this._plotlyConfig = newConfigObject(cfg); + } + + name() { + return this._cfg.name; + } + + createElement(div, idx) { + this._htmlElt = div; + this._plotIdx = idx; + // Pass a single data with no data to create an empty plot, this removes + // the 'bad time formatting' warning at startup. + Plotly.newPlot(this._htmlElt, this._lastData, this._plotlyLayout, this._plotlyConfig); + if (this._cfg.type == 'heatmap') { + this._installHeatmapTooltip(); + } + + this._htmlElt.infoText = this._cfg.infoText.split('\n').map(line => `

${line}

`).join(''); + } + + _installHeatmapTooltip() { + const options = { + followCursor: true, + trigger: "manual", + allowHTML: true + }; + const instance = tippy(document.body, options); + const hover = this._cfg.hover; + const formatYUnit = formatFunction(hover.yunit); + + const onHover = (data) => { + const pt2txt = (d) => { + let bucket; + if (d.y == 0) { + const yhigh = formatYUnit(d.data.custom_data[d.y]); + bucket = `(-Inf, ${yhigh})`; + } else if (d.y == d.data.custom_data.length - 1) { + const ylow = formatYUnit(d.data.custom_data[d.y]); + bucket = `[${ylow}, +Inf)`; + } else { + const ylow = formatYUnit(d.data.custom_data[d.y - 1]); + const yhigh = formatYUnit(d.data.custom_data[d.y]); + bucket = `[${ylow}, ${yhigh})`; + } + + return ` +
+
+
${hover.yname}
+
${bucket}
+
+
+
${hover.zname}
+
${d.z}
+
+
`; + } + instance.setContent(data.points.map(pt2txt)[0]); + instance.show(); + }; + const onUnhover = (data) => { + instance.hide(); + }; + + this._htmlElt.on('plotly_hover', onHover) + .on('plotly_unhover', onUnhover); + } + + _extractData(data) { + const serie = data.series.get(this._cfg.name); + if (this._cfg.type == 'heatmap') { + this._dataTemplate[0].x = data.times; + this._dataTemplate[0].z = serie; + this._dataTemplate[0].hoverinfo = 'none'; + } else { + for (let i = 0; i < this._dataTemplate.length; i++) { + this._dataTemplate[i].x = data.times; + this._dataTemplate[i].y = serie[i]; + this._dataTemplate[i].stackgroup = this._cfg.subplots[i].stackgroup; + this._dataTemplate[i].hoveron = this._cfg.subplots[i].hoveron; + this._dataTemplate[i].type = this._cfg.subplots[i].type || this._cfg.type; + this._dataTemplate[i].marker = { + color: this._cfg.subplots[i].color, + }; + } + } + return this._dataTemplate; + } + + update(xrange, data, shapes) { + this._lastData = this._extractData(data); + this._updateCount++; + if (this._cfg.updateFreq == 0 || (this._updateCount % this._cfg.updateFreq == 0)) { + // Update layout with vertical shapes if necessary. + if (this._cfg.events != '') { + this._plotlyLayout.shapes = shapes.get(this._cfg.events); + } + + // Move the xaxis time range. + this._plotlyLayout.xaxis.range = xrange; + + Plotly.react(this._htmlElt, this._lastData, this._plotlyLayout, this._plotlyConfig); + } + } + + /** + * update theme color and immediately force plot redraw to apply the new theme + */ + updateTheme() { + const themeMode = theme.getThemeMode(); + this._cfg.layout.paper_bgcolor = themeColors[themeMode].paper_bgcolor; + this._cfg.layout.plot_bgcolor = themeColors[themeMode].plot_bgcolor; + this._cfg.layout.font_color = themeColors[themeMode].font_color; + + this._plotlyLayout = newLayoutObject(this._cfg); + this._plotlyConfig = newConfigObject(this._cfg); + + Plotly.react(this._htmlElt, this._lastData, this._plotlyLayout); + } +}; + +// Create 'vertical lines' shapes for each of the given timestamps. +const createVerticalLines = (tss) => { + const shapes = []; + for (let i = 0, n = tss.length; i < n; i++) { + const d = tss[i]; + shapes.push({ + type: 'line', + x0: d, + x1: d, + yref: 'paper', + y0: 0, + y1: 1, + line: { + color: 'rgb(55, 128, 191)', + width: 1, + dash: 'longdashdot', + } + }) + } + return shapes; +} + +export { createVerticalLines, Plot }; + +const durUnits = ['w', 'd', 'h', 'm', 's', 'ms', 'µs', 'ns']; +const durVals = [6048e11, 864e11, 36e11, 6e10, 1e9, 1e6, 1e3, 1]; + +// Formats a time duration provided in second. +const formatDuration = sec => { + let ns = sec * 1e9; + for (let i = 0; i < durUnits.length; i++) { + let inc = ns / durVals[i]; + + if (inc < 1) continue; + return Math.round(inc) + durUnits[i]; + } + return res.trim(); +}; + +const bytesUnits = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']; + +// Formats a size in bytes. +const formatBytes = bytes => { + let i = 0; + while (bytes > 1000) { + bytes /= 1000; + i++; + } + const res = Math.trunc(bytes); + return `${res}${bytesUnits[i]}`; +}; + +// Returns a format function based on the provided unit. +const formatFunction = unit => { + switch (unit) { + case 'duration': + return formatDuration; + case 'bytes': + return formatBytes; + } + // Default formatting + return (y) => { `${y} ${hover.yunit}` }; +}; diff --git a/vendor/github.com/arl/statsviz/internal/static/js/stats.js b/vendor/github.com/arl/statsviz/internal/static/js/stats.js new file mode 100644 index 00000000..5e8c8809 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/js/stats.js @@ -0,0 +1,95 @@ +// stats holds the data and function to modify it. +import Buffer from "./buffer.js"; + +var series = { + times: null, + eventsData: new Map(), + plotData: new Map(), +}; + +// initialize time series storage. +const init = (plotdefs, buflen) => { + const extraBufferCapacity = 20; // 20% of extra (preallocated) buffer datapoints + const bufcap = buflen + (buflen * extraBufferCapacity) / 100; // number of actual datapoints + + series.times = new Buffer(buflen, bufcap); + series.plotData.clear(); + plotdefs.series.forEach(plotdef => { + let ndim; + switch (plotdef.type) { + case 'bar': + case 'scatter': + ndim = plotdef.subplots.length; + break; + case 'heatmap': + ndim = plotdef.buckets.length; + break; + default: + console.error(`[statsviz]: unknown plot type "${plotdef.type}"`); + return; + }; + + let data = new Array(ndim); + for (let i = 0; i < ndim; i++) { + data[i] = new Buffer(buflen, bufcap); + } + series.plotData.set(plotdef.name, data); + }); + + plotdefs.events.forEach(event => { + series.eventsData.set(event, new Array()); + }); +} + +// push a new datapoint to all time series. +const pushData = (data) => { + series.times.push(data.timestamp); + + // Update time series. + for (const [name, plotData] of series.plotData) { + const curdata = data[name]; + for (let i = 0; i < curdata.length; i++) { + plotData[i].push(curdata[i]); + } + } + + // Update events series, deduplicating event timestamps and trimming the ones + // that are oldest with respect to the oldest timestamp we're keeping track of. + for (const [name, event] of series.eventsData) { + if (event.length == 0) { + if (data[name].length != 0) { + const eventTs = new Date(Math.floor(data[name][0])); + event.push(eventTs); + } + return; + } + const eventTs = new Date(Math.floor(data[name][0])); + if (eventTs.getTime() != event[event.length - 1].getTime()) { + event.push(eventTs); + let mints = series.times._buf[0]; + if (event[0] < mints) { + event.splice(0, 1); + } + } + } +} + +// slice returns the last n items from all time series. +const slice = (n) => { + let sliced = { + times: series.times.slice(n), + series: new Map(), + events: series.eventsData, + }; + + for (const [name, plotData] of series.plotData) { + const arr = new Array(plotData.length); + for (let i = 0; i < plotData.length; i++) { + arr[i] = plotData[i].slice(n); + } + sliced.series.set(name, arr); + } + return sliced; +} + +export { init, pushData, slice }; \ No newline at end of file diff --git a/vendor/github.com/arl/statsviz/internal/static/js/theme.js b/vendor/github.com/arl/statsviz/internal/static/js/theme.js new file mode 100644 index 00000000..18498f3d --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/js/theme.js @@ -0,0 +1,36 @@ +/** + * Get color theme based on previous user choice or browser theme + */ +export const getThemeMode = () => { + let themeMode = localStorage.getItem("theme-mode"); + + if (themeMode === null) { + const isDark = + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches; + themeMode = (isDark && "dark") || "light"; + + localStorage.setItem("theme-mode", themeMode); + } + + return themeMode; +}; + +/** + * Set light or dark theme + */ +export const updateThemeMode = () => { + if (getThemeMode() === "dark") { + document.body.classList.add("dark-theme"); + document + .getElementById("navbar") + .classList.replace("navbar-light", "navbar-dark"); + document.getElementById("navbar").classList.replace("bg-light", "bg-dark"); + } else { + document.body.classList.remove("dark-theme"); + document + .getElementById("navbar") + .classList.replace("navbar-dark", "navbar-light"); + document.getElementById("navbar").classList.replace("bg-dark", "bg-light"); + } +}; diff --git a/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap-toggle.min.css b/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap-toggle.min.css new file mode 100644 index 00000000..0d42ed09 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap-toggle.min.css @@ -0,0 +1,28 @@ +/*! ======================================================================== + * Bootstrap Toggle: bootstrap-toggle.css v2.2.0 + * http://www.bootstraptoggle.com + * ======================================================================== + * Copyright 2014 Min Hur, The New York Times Company + * Licensed under MIT + * ======================================================================== */ +.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px} +.toggle{position:relative;overflow:hidden} +.toggle input[type=checkbox]{display:none} +.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none} +.toggle.off .toggle-group{left:-100%} +.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0} +.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0} +.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px} +.toggle.btn{min-width:59px;min-height:34px} +.toggle-on.btn{padding-right:24px} +.toggle-off.btn{padding-left:24px} +.toggle.btn-lg{min-width:79px;min-height:45px} +.toggle-on.btn-lg{padding-right:31px} +.toggle-off.btn-lg{padding-left:31px} +.toggle-handle.btn-lg{width:40px} +.toggle.btn-sm{min-width:50px;min-height:30px} +.toggle-on.btn-sm{padding-right:20px} +.toggle-off.btn-sm{padding-left:20px} +.toggle.btn-xs{min-width:35px;min-height:22px} +.toggle-on.btn-xs{padding-right:12px} +.toggle-off.btn-xs{padding-left:12px} \ No newline at end of file diff --git a/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap.min.css b/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap.min.css new file mode 100644 index 00000000..a18042d1 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.0.2 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0))}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-font-sans-serif);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x) * -.5);margin-left:calc(var(--bs-gutter-x) * -.5)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + (.5rem + 2px));padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + (1rem + 2px));padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + (.75rem + 2px))}textarea.form-control-sm{min-height:calc(1.5em + (.5rem + 2px))}textarea.form-control-lg{min-height:calc(1.5em + (1rem + 2px))}.form-control-color{max-width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast:not(.showing):not(.show){opacity:0}.toast.hide{display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1060;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1050;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{color:#0d6efd!important}.text-secondary{color:#6c757d!important}.text-success{color:#198754!important}.text-info{color:#0dcaf0!important}.text-warning{color:#ffc107!important}.text-danger{color:#dc3545!important}.text-light{color:#f8f9fa!important}.text-dark{color:#212529!important}.text-white{color:#fff!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-reset{color:inherit!important}.bg-primary{background-color:#0d6efd!important}.bg-secondary{background-color:#6c757d!important}.bg-success{background-color:#198754!important}.bg-info{background-color:#0dcaf0!important}.bg-warning{background-color:#ffc107!important}.bg-danger{background-color:#dc3545!important}.bg-light{background-color:#f8f9fa!important}.bg-dark{background-color:#212529!important}.bg-body{background-color:#fff!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} diff --git a/vendor/github.com/arl/statsviz/internal/static/libs/css/font.awesome-6.1.2-all.min.css b/vendor/github.com/arl/statsviz/internal/static/libs/css/font.awesome-6.1.2-all.min.css new file mode 100644 index 00000000..b9a01fbb --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/libs/css/font.awesome-6.1.2-all.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2022 Fonticons, Inc. + */ +.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-duotone,.fa-light,.fa-regular,.fa-solid,.fa-thin,.fab,.fad,.fal,.far,.fas,.fat{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,none));transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)}.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-a:before{content:"\41"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-anchor:before{content:"\f13d"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-anchor-lock:before{content:"\e4ad"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-ankh:before{content:"\f644"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-archway:before{content:"\f557"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-arrow-trend-down:before{content:"\e097"}.fa-arrow-trend-up:before{content:"\e098"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-arrows-spin:before{content:"\e4bb"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-asterisk:before{content:"\2a"}.fa-at:before{content:"\40"}.fa-atom:before{content:"\f5d2"}.fa-audio-description:before{content:"\f29e"}.fa-austral-sign:before{content:"\e0a9"}.fa-award:before{content:"\f559"}.fa-b:before{content:"\42"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-backward:before{content:"\f04a"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-baht-sign:before{content:"\e0ac"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-barcode:before{content:"\f02a"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-bed:before{content:"\f236"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-bell:before{content:"\f0f3"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bicycle:before{content:"\f206"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blog:before{content:"\f781"}.fa-bold:before{content:"\f032"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-book-bookmark:before{content:"\e0bb"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-bookmark:before{content:"\f02e"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-bore-hole:before{content:"\e4c3"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-bottle-water:before{content:"\e4c5"}.fa-bowl-food:before{content:"\e4c6"}.fa-bowl-rice:before{content:"\e2eb"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes-packing:before{content:"\e4c7"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-bread-slice:before{content:"\f7ec"}.fa-bridge:before{content:"\e4c8"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-bridge-water:before{content:"\e4ce"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broom:before{content:"\f51a"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-brush:before{content:"\f55d"}.fa-bucket:before{content:"\e4cf"}.fa-bug:before{content:"\f188"}.fa-bug-slash:before{content:"\e490"}.fa-bugs:before{content:"\e4d0"}.fa-building:before{content:"\f1ad"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-building-circle-check:before{content:"\e4d2"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-building-flag:before{content:"\e4d5"}.fa-building-lock:before{content:"\e4d6"}.fa-building-ngo:before{content:"\e4d7"}.fa-building-shield:before{content:"\e4d8"}.fa-building-un:before{content:"\e4d9"}.fa-building-user:before{content:"\e4da"}.fa-building-wheat:before{content:"\e4db"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-burst:before{content:"\e4dc"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-c:before{content:"\43"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-week:before{content:"\f784"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-camera-rotate:before{content:"\e0d8"}.fa-campground:before{content:"\f6bb"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-car-on:before{content:"\e4dd"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-car-side:before{content:"\f5e4"}.fa-car-tunnel:before{content:"\e4de"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-cart-plus:before{content:"\f217"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cedi-sign:before{content:"\e0df"}.fa-cent-sign:before{content:"\e3f5"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-charging-station:before{content:"\f5e7"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-chart-column:before{content:"\e0e3"}.fa-chart-gantt:before{content:"\e0e4"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-chart-simple:before{content:"\e473"}.fa-check:before{content:"\f00c"}.fa-check-double:before{content:"\f560"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-child-dress:before{content:"\e59c"}.fa-child-reaching:before{content:"\e59d"}.fa-child-rifle:before{content:"\e4e0"}.fa-children:before{content:"\e4e1"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-circle-nodes:before{content:"\e4e2"}.fa-circle-notch:before{content:"\f1ce"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-city:before{content:"\f64f"}.fa-clapperboard:before{content:"\e131"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clipboard-question:before{content:"\e4e3"}.fa-clipboard-user:before{content:"\f7f3"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-clover:before{content:"\e139"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-code-commit:before{content:"\f386"}.fa-code-compare:before{content:"\e13a"}.fa-code-fork:before{content:"\e13b"}.fa-code-merge:before{content:"\f387"}.fa-code-pull-request:before{content:"\e13c"}.fa-coins:before{content:"\f51e"}.fa-colon-sign:before{content:"\e140"}.fa-comment:before{content:"\f075"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-compress:before{content:"\f066"}.fa-computer:before{content:"\e4e5"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cow:before{content:"\f6c8"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-d:before{content:"\44"}.fa-database:before{content:"\f1c0"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-democrat:before{content:"\f747"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-dharmachakra:before{content:"\f655"}.fa-diagram-next:before{content:"\e476"}.fa-diagram-predecessor:before{content:"\e477"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-diagram-successor:before{content:"\e47a"}.fa-diamond:before{content:"\f219"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-disease:before{content:"\f7fa"}.fa-display:before{content:"\e163"}.fa-divide:before{content:"\f529"}.fa-dna:before{content:"\f471"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-dong-sign:before{content:"\e169"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dove:before{content:"\f4ba"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-download:before{content:"\f019"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-e:before{content:"\45"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elevator:before{content:"\e16d"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-equals:before{content:"\3d"}.fa-eraser:before{content:"\f12d"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-exclamation:before{content:"\21"}.fa-expand:before{content:"\f065"}.fa-explosion:before{content:"\e4e9"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-eye-slash:before{content:"\f070"}.fa-f:before{content:"\46"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-fan:before{content:"\f863"}.fa-faucet:before{content:"\e005"}.fa-faucet-drip:before{content:"\e006"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-ferry:before{content:"\e4ea"}.fa-file:before{content:"\f15b"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-file-audio:before{content:"\f1c7"}.fa-file-circle-check:before{content:"\e5a0"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-file-circle-plus:before{content:"\e494"}.fa-file-circle-question:before{content:"\e4ef"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-excel:before{content:"\f1c3"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-file-medical:before{content:"\f477"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-shield:before{content:"\e4f0"}.fa-file-signature:before{content:"\f573"}.fa-file-video:before{content:"\f1c8"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-file-word:before{content:"\f1c2"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-burner:before{content:"\e4f1"}.fa-fire-extinguisher:before{content:"\f134"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-fish:before{content:"\f578"}.fa-fish-fins:before{content:"\e4f2"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flask-vial:before{content:"\e4f3"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-florin-sign:before{content:"\e184"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-folder-closed:before{content:"\e185"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-folder-tree:before{content:"\f802"}.fa-font:before{content:"\f031"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-franc-sign:before{content:"\e18f"}.fa-frog:before{content:"\f52e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-g:before{content:"\47"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-glass-water:before{content:"\e4f4"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-glasses:before{content:"\f530"}.fa-globe:before{content:"\f0ac"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-greater-than:before{content:"\3e"}.fa-greater-than-equal:before{content:"\f532"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-guarani-sign:before{content:"\e19a"}.fa-guitar:before{content:"\f7a6"}.fa-gun:before{content:"\e19b"}.fa-h:before{content:"\48"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-handcuffs:before{content:"\e4f8"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-hands-bound:before{content:"\e4f9"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-hands-clapping:before{content:"\e1a8"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-handshake:before{content:"\f2b5"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-hashtag:before{content:"\23"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-heart-circle-plus:before{content:"\e500"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-helicopter-symbol:before{content:"\e502"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-helmet-un:before{content:"\e503"}.fa-highlighter:before{content:"\f591"}.fa-hill-avalanche:before{content:"\e507"}.fa-hill-rockslide:before{content:"\e508"}.fa-hippo:before{content:"\f6ed"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-house-chimney-user:before{content:"\e065"}.fa-house-chimney-window:before{content:"\e00d"}.fa-house-circle-check:before{content:"\e509"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-house-crack:before{content:"\e3b1"}.fa-house-fire:before{content:"\e50c"}.fa-house-flag:before{content:"\e50d"}.fa-house-flood-water:before{content:"\e50e"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-house-lock:before{content:"\e510"}.fa-house-medical:before{content:"\e3b2"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-house-medical-flag:before{content:"\e514"}.fa-house-signal:before{content:"\e012"}.fa-house-tsunami:before{content:"\e515"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-hurricane:before{content:"\f751"}.fa-i:before{content:"\49"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-images:before{content:"\f302"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-italic:before{content:"\f033"}.fa-j:before{content:"\4a"}.fa-jar:before{content:"\e516"}.fa-jar-wheat:before{content:"\e517"}.fa-jedi:before{content:"\f669"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-jet-fighter-up:before{content:"\e518"}.fa-joint:before{content:"\f595"}.fa-jug-detergent:before{content:"\e519"}.fa-k:before{content:"\4b"}.fa-kaaba:before{content:"\f66b"}.fa-key:before{content:"\f084"}.fa-keyboard:before{content:"\f11c"}.fa-khanda:before{content:"\f66d"}.fa-kip-sign:before{content:"\e1c4"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-kitchen-set:before{content:"\e51a"}.fa-kiwi-bird:before{content:"\f535"}.fa-l:before{content:"\4c"}.fa-land-mine-on:before{content:"\e51b"}.fa-landmark:before{content:"\f66f"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-landmark-flag:before{content:"\e51c"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-file:before{content:"\e51d"}.fa-laptop-medical:before{content:"\f812"}.fa-lari-sign:before{content:"\e1c8"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-lemon:before{content:"\f094"}.fa-less-than:before{content:"\3c"}.fa-less-than-equal:before{content:"\f537"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-lines-leaning:before{content:"\e51e"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-lira-sign:before{content:"\f195"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-location-arrow:before{content:"\f124"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-location-pin-lock:before{content:"\e51f"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-locust:before{content:"\e520"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-m:before{content:"\4d"}.fa-magnet:before{content:"\f076"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-manat-sign:before{content:"\e1d5"}.fa-map:before{content:"\f279"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-pin:before{content:"\f276"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-and-venus:before{content:"\f224"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-mask:before{content:"\f6fa"}.fa-mask-face:before{content:"\e1d7"}.fa-mask-ventilator:before{content:"\e524"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-mattress-pillow:before{content:"\e525"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-medal:before{content:"\f5a2"}.fa-memory:before{content:"\f538"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-mill-sign:before{content:"\e1ed"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-mitten:before{content:"\f7b5"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-mobile-button:before{content:"\f10b"}.fa-mobile-retro:before{content:"\e527"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-money-bills:before{content:"\e1f3"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-mosquito-net:before{content:"\e52c"}.fa-motorcycle:before{content:"\f21c"}.fa-mound:before{content:"\e52d"}.fa-mountain:before{content:"\f6fc"}.fa-mountain-city:before{content:"\e52e"}.fa-mountain-sun:before{content:"\e52f"}.fa-mug-hot:before{content:"\f7b6"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-music:before{content:"\f001"}.fa-n:before{content:"\4e"}.fa-naira-sign:before{content:"\e1f6"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-not-equal:before{content:"\f53e"}.fa-notdef:before{content:"\e1fe"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-notes-medical:before{content:"\f481"}.fa-o:before{content:"\4f"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-oil-can:before{content:"\f613"}.fa-oil-well:before{content:"\e532"}.fa-om:before{content:"\f679"}.fa-otter:before{content:"\f700"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-p:before{content:"\50"}.fa-pager:before{content:"\f815"}.fa-paint-roller:before{content:"\f5aa"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-palette:before{content:"\f53f"}.fa-pallet:before{content:"\f482"}.fa-panorama:before{content:"\e209"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-passport:before{content:"\f5ab"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-pause:before{content:"\f04c"}.fa-paw:before{content:"\f1b0"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-people-group:before{content:"\e533"}.fa-people-line:before{content:"\e534"}.fa-people-pulling:before{content:"\e535"}.fa-people-robbery:before{content:"\e536"}.fa-people-roof:before{content:"\e537"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-person-booth:before{content:"\f756"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-person-burst:before{content:"\e53b"}.fa-person-cane:before{content:"\e53c"}.fa-person-chalkboard:before{content:"\e53d"}.fa-person-circle-check:before{content:"\e53e"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-person-circle-minus:before{content:"\e540"}.fa-person-circle-plus:before{content:"\e541"}.fa-person-circle-question:before{content:"\e542"}.fa-person-circle-xmark:before{content:"\e543"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-person-dress-burst:before{content:"\e544"}.fa-person-drowning:before{content:"\e545"}.fa-person-falling:before{content:"\e546"}.fa-person-falling-burst:before{content:"\e547"}.fa-person-half-dress:before{content:"\e548"}.fa-person-harassing:before{content:"\e549"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-person-military-pointing:before{content:"\e54a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-person-military-to-person:before{content:"\e54c"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-person-pregnant:before{content:"\e31e"}.fa-person-rays:before{content:"\e54d"}.fa-person-rifle:before{content:"\e54e"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-person-shelter:before{content:"\e54f"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-person-through-window:before{content:"\e5a9"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-person-walking-luggage:before{content:"\e554"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-peseta-sign:before{content:"\e221"}.fa-peso-sign:before{content:"\e222"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-circle-check:before{content:"\e555"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-lock:before{content:"\e558"}.fa-plane-slash:before{content:"\e069"}.fa-plane-up:before{content:"\e22d"}.fa-plant-wilt:before{content:"\e5aa"}.fa-plate-wheat:before{content:"\e55a"}.fa-play:before{content:"\f04b"}.fa-plug:before{content:"\f1e6"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-plug-circle-check:before{content:"\e55c"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-plus-minus:before{content:"\e43c"}.fa-podcast:before{content:"\f2ce"}.fa-poo:before{content:"\f2fe"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-power-off:before{content:"\f011"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-puzzle-piece:before{content:"\f12e"}.fa-q:before{content:"\51"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\3f"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-r:before{content:"\52"}.fa-radiation:before{content:"\f7b9"}.fa-radio:before{content:"\f8d7"}.fa-rainbow:before{content:"\f75b"}.fa-ranking-star:before{content:"\e561"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-recycle:before{content:"\f1b8"}.fa-registered:before{content:"\f25d"}.fa-repeat:before{content:"\f363"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-republican:before{content:"\f75e"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-ribbon:before{content:"\f4d6"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-road-barrier:before{content:"\e562"}.fa-road-bridge:before{content:"\e563"}.fa-road-circle-check:before{content:"\e564"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-road-circle-xmark:before{content:"\e566"}.fa-road-lock:before{content:"\e567"}.fa-road-spikes:before{content:"\e568"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-route:before{content:"\f4d7"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-rug:before{content:"\e569"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-rupiah-sign:before{content:"\e23d"}.fa-s:before{content:"\53"}.fa-sack-dollar:before{content:"\f81d"}.fa-sack-xmark:before{content:"\e56a"}.fa-sailboat:before{content:"\e445"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-school:before{content:"\f549"}.fa-school-circle-check:before{content:"\e56b"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-school-flag:before{content:"\e56e"}.fa-school-lock:before{content:"\e56f"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-screwdriver:before{content:"\f54a"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-scroll:before{content:"\f70e"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-sd-card:before{content:"\f7c2"}.fa-section:before{content:"\e447"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-server:before{content:"\f233"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-arrow-turn-right:before,.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-sheet-plastic:before{content:"\e571"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-shield-cat:before{content:"\e572"}.fa-shield-dog:before{content:"\e573"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-shield-heart:before{content:"\e574"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-shoe-prints:before{content:"\f54b"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-shop-lock:before{content:"\e4a5"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-shower:before{content:"\f2cc"}.fa-shrimp:before{content:"\e448"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-sim-card:before{content:"\f7c4"}.fa-sink:before{content:"\e06d"}.fa-sitemap:before{content:"\f0e8"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-spa:before{content:"\f5bb"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-spray-can:before{content:"\f5bd"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-square:before{content:"\f0c8"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-square-full:before{content:"\f45c"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-square-nfi:before{content:"\e576"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-square-person-confined:before{content:"\e577"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-square-virus:before{content:"\e578"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-stairs:before{content:"\e289"}.fa-stamp:before{content:"\f5bf"}.fa-stapler:before{content:"\e5af"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-stethoscope:before{content:"\f0f1"}.fa-stop:before{content:"\f04d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-slash:before{content:"\e071"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stroopwafel:before{content:"\f551"}.fa-subscript:before{content:"\f12c"}.fa-suitcase:before{content:"\f0f2"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-superscript:before{content:"\f12b"}.fa-swatchbook:before{content:"\f5c3"}.fa-synagogue:before{content:"\f69b"}.fa-syringe:before{content:"\f48e"}.fa-t:before{content:"\54"}.fa-table:before{content:"\f0ce"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-tablet-button:before{content:"\f10a"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tarp:before{content:"\e57b"}.fa-tarp-droplet:before{content:"\e57c"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-tent:before{content:"\e57d"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tent-arrows-down:before{content:"\e581"}.fa-tents:before{content:"\e582"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-text-width:before{content:"\f035"}.fa-thermometer:before{content:"\f491"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-ticket:before{content:"\f145"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-timeline:before{content:"\e29c"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toilet-portable:before{content:"\e583"}.fa-toilets-portable:before{content:"\e584"}.fa-toolbox:before{content:"\f552"}.fa-tooth:before{content:"\f5c9"}.fa-torii-gate:before{content:"\f6a1"}.fa-tornado:before{content:"\f76f"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-tower-cell:before{content:"\e585"}.fa-tower-observation:before{content:"\e586"}.fa-tractor:before{content:"\f722"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-train-tram:before{content:"\e5b4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-tree-city:before{content:"\e587"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-trophy:before{content:"\f091"}.fa-trowel:before{content:"\e589"}.fa-trowel-bricks:before{content:"\e58a"}.fa-truck:before{content:"\f0d1"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-truck-droplet:before{content:"\e58c"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-truck-field:before{content:"\e58d"}.fa-truck-field-un:before{content:"\e58e"}.fa-truck-front:before{content:"\e2b7"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-truck-plane:before{content:"\e58f"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-u:before{content:"\55"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-universal-access:before{content:"\f29a"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-upload:before{content:"\f093"}.fa-user:before{content:"\f007"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-clock:before{content:"\f4fd"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-user-graduate:before{content:"\f501"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-user-injured:before{content:"\f728"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-user-lock:before{content:"\f502"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-between-lines:before{content:"\e591"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-users-line:before{content:"\e592"}.fa-users-rays:before{content:"\e593"}.fa-users-rectangle:before{content:"\e594"}.fa-users-slash:before{content:"\e073"}.fa-users-viewfinder:before{content:"\e595"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-v:before{content:"\56"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-vault:before{content:"\e2c5"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-vial:before{content:"\f492"}.fa-vial-circle-check:before{content:"\e596"}.fa-vial-virus:before{content:"\e597"}.fa-vials:before{content:"\f493"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-virus:before{content:"\e074"}.fa-virus-covid:before{content:"\e4a8"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-voicemail:before{content:"\f897"}.fa-volcano:before{content:"\f770"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-volume-off:before{content:"\f026"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-vr-cardboard:before{content:"\f729"}.fa-w:before{content:"\57"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-wallet:before{content:"\f555"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-wand-sparkles:before{content:"\f72b"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-wave-square:before{content:"\f83e"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-wheelchair:before{content:"\f193"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-wind:before{content:"\f72e"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-worm:before{content:"\e599"}.fa-wrench:before{content:"\f0ad"}.fa-x:before{content:"\58"}.fa-x-ray:before{content:"\f497"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-xmarks-lines:before{content:"\e59a"}.fa-y:before{content:"\59"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-z:before{content:"\5a"}.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-family:"Font Awesome 6 Brands";font-weight:400}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-alipay:before{content:"\f642"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-amilia:before{content:"\f36d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-pay:before{content:"\f415"}.fa-artstation:before{content:"\f77a"}.fa-asymmetrik:before{content:"\f372"}.fa-atlassian:before{content:"\f77b"}.fa-audible:before{content:"\f373"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-aws:before{content:"\f375"}.fa-bandcamp:before{content:"\f2d5"}.fa-battle-net:before{content:"\f835"}.fa-behance:before{content:"\f1b4"}.fa-bilibili:before{content:"\e3d9"}.fa-bimobject:before{content:"\f378"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bootstrap:before{content:"\f836"}.fa-bots:before{content:"\e340"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-buromobelexperte:before{content:"\f37f"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cmplid:before{content:"\e360"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cotton-bureau:before{content:"\f89e"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-critical-role:before{content:"\f6c9"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dhl:before{content:"\f790"}.fa-diaspora:before{content:"\f791"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-draft2digital:before{content:"\f396"}.fa-dribbble:before{content:"\f17d"}.fa-dropbox:before{content:"\f16b"}.fa-drupal:before{content:"\f1a9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-elementor:before{content:"\f430"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-etsy:before{content:"\f2d7"}.fa-evernote:before{content:"\f839"}.fa-expeditedssl:before{content:"\f23e"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-figma:before{content:"\f799"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-fly:before{content:"\f417"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-fulcrum:before{content:"\f50b"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-gofore:before{content:"\f3a7"}.fa-golang:before{content:"\e40f"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-wallet:before{content:"\f1ee"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-gulp:before{content:"\f3ae"}.fa-hacker-news:before{content:"\f1d4"}.fa-hackerrank:before{content:"\f5f7"}.fa-hashnode:before{content:"\e499"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-hive:before{content:"\e07f"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-hotjar:before{content:"\f3b1"}.fa-houzz:before{content:"\f27c"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-ideal:before{content:"\e013"}.fa-imdb:before{content:"\f2d8"}.fa-instagram:before{content:"\f16d"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joomla:before{content:"\f1aa"}.fa-js:before{content:"\f3b8"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaggle:before{content:"\f5fa"}.fa-keybase:before{content:"\f4f5"}.fa-keycdn:before{content:"\f3ba"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-korvue:before{content:"\f42f"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-leanpub:before{content:"\f212"}.fa-less:before{content:"\f41d"}.fa-line:before{content:"\f3c0"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-mailchimp:before{content:"\f59e"}.fa-mandalorian:before{content:"\f50f"}.fa-markdown:before{content:"\f60f"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medapps:before{content:"\f3c6"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-mendeley:before{content:"\f7b3"}.fa-meta:before{content:"\e49b"}.fa-microblog:before{content:"\e01a"}.fa-microsoft:before{content:"\f3ca"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-nfc-directional:before{content:"\e530"}.fa-nfc-symbol:before{content:"\e531"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-old-republic:before{content:"\f510"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-padlet:before{content:"\e4a0"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-palfed:before{content:"\f3d8"}.fa-patreon:before{content:"\f3d9"}.fa-paypal:before{content:"\f1ed"}.fa-perbyte:before{content:"\e083"}.fa-periscope:before{content:"\f3da"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pix:before{content:"\e43a"}.fa-playstation:before{content:"\f3df"}.fa-product-hunt:before{content:"\f288"}.fa-pushed:before{content:"\f3e1"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-r-project:before{content:"\f4f7"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-redhat:before{content:"\f7bc"}.fa-renren:before{content:"\f18b"}.fa-replyd:before{content:"\f3e6"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-rev:before{content:"\f5b2"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-rust:before{content:"\e07a"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-schlix:before{content:"\f3ea"}.fa-screenpal:before{content:"\e570"}.fa-scribd:before{content:"\f28a"}.fa-searchengin:before{content:"\f3eb"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-servicestack:before{content:"\f3ec"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shopify:before{content:"\e057"}.fa-shopware:before{content:"\f5b5"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sith:before{content:"\f512"}.fa-sitrox:before{content:"\e44a"}.fa-sketch:before{content:"\f7c6"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-slideshare:before{content:"\f1e7"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-space-awesome:before{content:"\e5ac"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spotify:before{content:"\f1bc"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-symbol:before{content:"\f3f6"}.fa-sticker-mule:before{content:"\f3f7"}.fa-strava:before{content:"\f428"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-superpowers:before{content:"\f2dd"}.fa-supple:before{content:"\f3f9"}.fa-suse:before{content:"\f7d6"}.fa-swift:before{content:"\f8e1"}.fa-symfony:before{content:"\f83d"}.fa-teamspeak:before{content:"\f4f9"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-the-red-yeti:before{content:"\f69d"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-think-peaks:before{content:"\f731"}.fa-tiktok:before{content:"\e07b"}.fa-trade-federation:before{content:"\f513"}.fa-trello:before{content:"\f181"}.fa-tumblr:before{content:"\f173"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-uncharted:before{content:"\e084"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-vaadin:before{content:"\f408"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viber:before{content:"\f409"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-vuejs:before{content:"\f41f"}.fa-watchman-monitoring:before{content:"\e087"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whmcs:before{content:"\f40d"}.fa-wikipedia-w:before{content:"\f266"}.fa-windows:before{content:"\f17a"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-zhihu:before{content:"\f63f"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-family:"Font Awesome 6 Free";font-weight:400}:host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-family:"Font Awesome 6 Free";font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} \ No newline at end of file diff --git a/vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap-toggle.min.js b/vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap-toggle.min.js new file mode 100644 index 00000000..5bed0ff7 --- /dev/null +++ b/vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap-toggle.min.js @@ -0,0 +1,8 @@ +/*! ======================================================================== + * Bootstrap Toggle: bootstrap-toggle.js v2.2.0 + * http://www.bootstraptoggle.com + * ======================================================================== + * Copyright 2014 Min Hur, The New York Times Company + * Licensed under MIT + * ======================================================================== */ ++function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('