From 63b5b8d3f9c3b07c025376946cd2eddd10fc4ad6 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 1 Feb 2023 10:56:14 +0000 Subject: [PATCH] Start adding web UI, for now integrated with shuckle --- cpp/cdc/eggscdc.cpp | 16 + cpp/core/Exception.hpp | 4 +- cpp/core/Shuckle.cpp | 131 +++--- cpp/shard/eggsshard.cpp | 18 + deploy.py | 2 +- go/eggs/blockservicereq.go | 16 +- go/eggs/client.go | 94 ++-- go/eggs/managedprocess.go | 28 +- go/eggs/migrate.go | 2 +- go/eggs/mockableblockservice.go | 26 +- go/eggsfuse/eggsfuse.go | 30 +- go/eggsrun/eggsrun.go | 85 +++- go/eggsshuckle/base.html | 33 ++ go/eggsshuckle/bootstrap.min.css | 7 + go/eggsshuckle/directory.html | 99 ++++ go/eggsshuckle/eggsshuckle.go | 764 ++++++++++++++++++++++++------- go/eggsshuckle/error.html | 3 + go/eggsshuckle/file.html | 95 ++++ go/eggsshuckle/index.html | 65 +++ go/eggsshuckle/shuckle.png | Bin 0 -> 40121 bytes go/eggsshuckle/shuckleface.png | Bin 0 -> 18581 bytes go/integrationtest/req.go | 2 +- go/msgs/msgs.go | 4 +- 23 files changed, 1195 insertions(+), 329 deletions(-) create mode 100644 go/eggsshuckle/base.html create mode 100644 go/eggsshuckle/bootstrap.min.css create mode 100644 go/eggsshuckle/directory.html create mode 100644 go/eggsshuckle/error.html create mode 100644 go/eggsshuckle/file.html create mode 100644 go/eggsshuckle/index.html create mode 100644 go/eggsshuckle/shuckle.png create mode 100644 go/eggsshuckle/shuckleface.png diff --git a/cpp/cdc/eggscdc.cpp b/cpp/cdc/eggscdc.cpp index 29e77bb1..b4eedcb6 100644 --- a/cpp/cdc/eggscdc.cpp +++ b/cpp/cdc/eggscdc.cpp @@ -21,6 +21,8 @@ void usage(const char* binary) { fprintf(stderr, " How to advertise ourselves to shuckle.\n"); fprintf(stderr, " -log-file string\n"); fprintf(stderr, " If not provided, stdout.\n"); + fprintf(stderr, " -port port\n"); + fprintf(stderr, " Port to listen on.\n"); } static std::array parseIpv4(const char* binary, const std::string& arg) { @@ -37,6 +39,18 @@ static std::array parseIpv4(const char* binary, const std::string& a return out; } +static uint16_t parsePort(const std::string& arg) { + size_t idx; + unsigned long port = std::stoul(arg, &idx); + if (idx != arg.size()) { + die("Runoff character in number %s", arg.c_str()); + } + if (port > 0xFFFFul) { + die("Bad port %s", arg.c_str()); + } + return port; +} + int main(int argc, char** argv) { namespace fs = std::filesystem; @@ -80,6 +94,8 @@ int main(int argc, char** argv) { shuckleAddress = getNextArg(); } else if (arg == "-own-ip") { options.ownIp = parseIpv4(argv[0], getNextArg()); + } else if (arg == "-port") { + options.port = parsePort(getNextArg()); } else { args.emplace_back(std::move(arg)); } diff --git a/cpp/core/Exception.hpp b/cpp/core/Exception.hpp index a42fd215..ebb3087e 100644 --- a/cpp/core/Exception.hpp +++ b/cpp/core/Exception.hpp @@ -86,7 +86,7 @@ SyscallException::SyscallException(int line, const char *file, const char *funct const char* errmsg = safe_strerror(_errno); std::stringstream ss; - ss << "SyscallException(" << file << "@" << line << ", " << _errno << "/" << translateErrno(_errno) << "=" << errmsg << " in " << function << "):\n"; + ss << "SyscallException(" << file << "@" << line << ", " << _errno << "/" << translateErrno(_errno) << "=" << errmsg << " in " << function << "): "; format_pack(ss, fmt, args...); _msg = ss.str(); @@ -96,7 +96,7 @@ template FatalException::FatalException(int line, const char *file, const char *function, const char *fmt, Args ... args) { std::stringstream ss; - ss << "FatalException(" << file << "@" << line << " in " << function << "):\n"; + ss << "FatalException(" << file << "@" << line << " in " << function << "): "; format_pack(ss, fmt, args...); _msg = ss.str(); diff --git a/cpp/core/Shuckle.cpp b/cpp/core/Shuckle.cpp index 47e4999a..0afa08a0 100644 --- a/cpp/core/Shuckle.cpp +++ b/cpp/core/Shuckle.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -14,59 +15,85 @@ #include #include "Bincode.hpp" +#include "Env.hpp" #include "Msgs.hpp" #include "MsgsGen.hpp" #include "Shuckle.hpp" #include "Exception.hpp" -static std::string generateErrString(const std::string& what, int err) { - +static std::string explicitGenerateErrString(const std::string& what, int err, const char* str) { const char *errmsg = safe_strerror(err); std::stringstream ss; - ss << "could not " << what << ": " << err << "/" << translateErrno(err) << "=" << errmsg; + ss << "could not " << what << ": " << err << "/" << str << "=" << errmsg; return ss.str(); } -static std::mutex gethostbynameMutex; +static std::string generateErrString(const std::string& what, int err) { + return explicitGenerateErrString(what, err, (std::string(translateErrno(err)) + "=" + safe_strerror(err)).c_str()); +} -static int shuckleSock(const std::string& host, uint16_t port, Duration timeout, std::string& errString) { - struct hostent* he; +struct ShuckleSock { + int fd; + ~ShuckleSock() { close(fd); } +}; + +static ShuckleSock shuckleSock(const std::string& host, uint16_t port, Duration timeout, std::string& errString) { + ShuckleSock sock; + sock.fd = -1; + + std::unique_ptr infos(nullptr, &freeaddrinfo); { - // too lazy to learn getaddrinfo - std::lock_guard lock(gethostbynameMutex); - he = gethostbyname(host.c_str()); - if (he == nullptr) { - throw EGGS_EXCEPTION("could not get address for host %s", host); + char portStr[10]; + snprintf(portStr, sizeof(portStr), "%d", port); + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET; + struct addrinfo* infosRaw; + int res = getaddrinfo(host.c_str(), portStr, &hint, &infosRaw); + if (res != 0) { + if (res == EAI_SYSTEM) { // errno is filled in in this case + throw SYSCALL_EXCEPTION("getaddrinfo"); + } + std::string prefix = "resolve host " + host + ":" + std::to_string(port); + if (res == EAI_ADDRFAMILY || res == EAI_AGAIN || res == EAI_NONAME) { // things that might be worth retrying + errString = explicitGenerateErrString(prefix, res, gai_strerror(res)); + return sock; + } + throw EGGS_EXCEPTION("%s: %s/%s", prefix, res, gai_strerror(res)); // we're probably hosed + } + infos.reset(infosRaw); + } + + for (struct addrinfo* info = infos.get(); info != nullptr; info = info->ai_next) { + int infoSock = socket(AF_INET, SOCK_STREAM, 0); + if (infoSock < 0) { + throw SYSCALL_EXCEPTION("socket"); + } + + // We retry upstream anyway, and we want prompt termination of `connect`. + // If we don't do this, mistakenly trying to connect to an iceland shuckle (currently + // the default) will hang for a long time. + int synRetries = 0; + setsockopt(infoSock, IPPROTO_TCP, TCP_SYNCNT, &synRetries, sizeof(synRetries)); + + if (connect(infoSock, info->ai_addr, info->ai_addrlen) < 0) { + close(infoSock); + } else { + sock.fd = infoSock; + break; } } - // nonblock at the beginning for connect timeout - int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - throw SYSCALL_EXCEPTION("socket"); - } - - // We retry upstream anyway, and we want prompt termination of `connect` - int synRetries = 0; - setsockopt(sock, IPPROTO_TCP, TCP_SYNCNT, &synRetries, sizeof(synRetries)); - - struct sockaddr_in shuckleAddr; - shuckleAddr.sin_family = AF_INET; - shuckleAddr.sin_addr = *(struct in_addr*)he->h_addr_list[0]; - shuckleAddr.sin_port = htons(port); - - if (connect(sock, (struct sockaddr*)&shuckleAddr, sizeof(shuckleAddr)) < 0) { + if (sock.fd == -1) { errString = generateErrString("connect to " + host, errno); - close(sock); - return -1; - } + } else { + struct timeval tv; + tv.tv_sec = timeout.ns/1'000'000'000ull; + tv.tv_usec = (timeout.ns%1'000'000'000ull)/1'000; - struct timeval tv; - tv.tv_sec = timeout.ns/1'000'000'000ull; - tv.tv_usec = (timeout.ns%1'000'000'000ull)/1'000; - - if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { - throw SYSCALL_EXCEPTION("setsockopt"); + if (setsockopt(sock.fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + throw SYSCALL_EXCEPTION("setsockopt"); + } } return sock; @@ -134,21 +161,21 @@ static std::string readShuckleResponse(int fd, ShuckleRespContainer& resp) { std::string fetchBlockServices(const std::string& addr, uint16_t port, Duration timeout, ShardId shid, UpdateBlockServicesEntry& blocks) { std::string errString; - int sock = shuckleSock(addr, port, timeout, errString); - if (sock < 0) { + auto sock = shuckleSock(addr, port, timeout, errString); + if (sock.fd < 0) { return errString; } ShuckleReqContainer reqContainer; auto& req = reqContainer.setBlockServicesForShard(); req.shard = shid; - errString = writeShuckleRequest(sock, reqContainer); + errString = writeShuckleRequest(sock.fd, reqContainer); if (!errString.empty()) { return errString; } ShuckleRespContainer respContainer; - errString = readShuckleResponse(sock, respContainer); + errString = readShuckleResponse(sock.fd, respContainer); if (!errString.empty()) { return errString; } @@ -162,8 +189,8 @@ std::string registerShard( const std::string& addr, uint16_t port, Duration timeout, ShardId shid, const std::array& shardAddr, uint16_t shardPort ) { std::string errString; - int sock = shuckleSock(addr, port, timeout, errString); - if (sock < 0) { + auto sock = shuckleSock(addr, port, timeout, errString); + if (sock.fd < 0) { return errString; } @@ -172,13 +199,13 @@ std::string registerShard( req.id = shid; req.info.ip.data = shardAddr; req.info.port = shardPort; - errString = writeShuckleRequest(sock, reqContainer); + errString = writeShuckleRequest(sock.fd, reqContainer); if (!errString.empty()) { return errString; } ShuckleRespContainer respContainer; - errString = readShuckleResponse(sock, respContainer); + errString = readShuckleResponse(sock.fd, respContainer); if (!errString.empty()) { return errString; } @@ -189,8 +216,8 @@ std::string registerShard( std::string registerCDC(const std::string& host, uint16_t port, Duration timeout, const std::array& cdcAddr, uint16_t cdcPort) { std::string errString; - int sock = shuckleSock(host, port, timeout, errString); - if (sock < 0) { + auto sock = shuckleSock(host, port, timeout, errString); + if (sock.fd < 0) { return errString; } @@ -198,13 +225,13 @@ std::string registerCDC(const std::string& host, uint16_t port, Duration timeout auto& req = reqContainer.setRegisterCdc(); req.ip.data = cdcAddr; req.port = cdcPort; - errString = writeShuckleRequest(sock, reqContainer); + errString = writeShuckleRequest(sock.fd, reqContainer); if (!errString.empty()) { return errString; } ShuckleRespContainer respContainer; - errString = readShuckleResponse(sock, respContainer); + errString = readShuckleResponse(sock.fd, respContainer); if (!errString.empty()) { return errString; } @@ -215,20 +242,20 @@ std::string registerCDC(const std::string& host, uint16_t port, Duration timeout std::string fetchShards(const std::string& host, uint16_t port, Duration timeout, std::array& shards) { std::string errString; - int sock = shuckleSock(host, port, timeout, errString); - if (sock < 0) { + auto sock = shuckleSock(host, port, timeout, errString); + if (sock.fd < 0) { return errString; } ShuckleReqContainer reqContainer; reqContainer.setShards(); - errString = writeShuckleRequest(sock, reqContainer); + errString = writeShuckleRequest(sock.fd, reqContainer); if (!errString.empty()) { return errString; } ShuckleRespContainer respContainer; - errString = readShuckleResponse(sock, respContainer); + errString = readShuckleResponse(sock.fd, respContainer); if (!errString.empty()) { return errString; } diff --git a/cpp/shard/eggsshard.cpp b/cpp/shard/eggsshard.cpp index 6fa2870a..e9ae8685 100644 --- a/cpp/shard/eggsshard.cpp +++ b/cpp/shard/eggsshard.cpp @@ -1,7 +1,9 @@ #include +#include #include #include #include +#include #include "Exception.hpp" #include "Shard.hpp" @@ -26,6 +28,8 @@ static void usage(const char* binary) { fprintf(stderr, " Drop given ratio of packets on arrival.\n"); fprintf(stderr, " -outgoing-packet-drop [0, 1)\n"); fprintf(stderr, " Drop given ratio of packets after processing them.\n"); + fprintf(stderr, " -port port\n"); + fprintf(stderr, " Port on which to listen on.\n"); } static double parseDouble(const std::string& arg) { @@ -59,6 +63,18 @@ static std::array parseIpv4(const char* binary, const std::string& a return out; } +static uint16_t parsePort(const std::string& arg) { + size_t idx; + unsigned long port = std::stoul(arg, &idx); + if (idx != arg.size()) { + die("Runoff character in number %s", arg.c_str()); + } + if (port > 0xFFFFul) { + die("Bad port %s", arg.c_str()); + } + return port; +} + int main(int argc, char** argv) { namespace fs = std::filesystem; @@ -106,6 +122,8 @@ int main(int argc, char** argv) { shuckleAddress = getNextArg(); } else if (arg == "-own-ip") { options.ownIp = parseIpv4(argv[0], getNextArg()); + } else if (arg == "-port") { + options.port = parsePort(getNextArg()); } else { args.emplace_back(std::move(arg)); } diff --git a/deploy.py b/deploy.py index d86ece3d..d2d07400 100755 --- a/deploy.py +++ b/deploy.py @@ -7,7 +7,7 @@ this_dir = os.path.dirname(__file__) metadata_hosts = ['REDACTED'] metadata_binaries = [ - 'cpp/build/alpine/shard/eggsshard', 'cpp/build/alpine/cdc/eggscdc', 'go/eggsshuckle/eggsshuckle' + 'cpp/build/alpine/shard/eggsshard', 'cpp/build/alpine/cdc/eggscdc', 'go/eggsshuckle/eggsshuckle', 'go/eggsrun/eggsrun' ] storage_hosts = [f'REDACTED{i}' for i in range(1,17)] storage_binaries = [ diff --git a/go/eggs/blockservicereq.go b/go/eggs/blockservicereq.go index b5e6a3b4..d3e798c8 100644 --- a/go/eggs/blockservicereq.go +++ b/go/eggs/blockservicereq.go @@ -100,7 +100,7 @@ func WriteBlock( if _, err := conn.ReadFrom(&lr); err != nil { return proof, fmt.Errorf("could not write block data to: %w", err) } - // write response: (block_service_id, 'W', block_id, proof) + // write response: (block_service_id, 'W', proof) if err := bsRespInit(conn, block.BlockServiceId, 'W'); err != nil { return proof, err } @@ -119,14 +119,15 @@ func FetchBlock( io.Writer }, blockService *msgs.BlockService, - block *msgs.FetchedBlock, + blockId msgs.BlockId, + crc32 [4]byte, offset uint32, count uint32, ) error { - logger.Debug("fetching block %+v from block service %+v, offset=%v, count=%v", block, blockService, offset, count) + logger.Debug("fetching block %v from block service %+v, offset=%v, count=%v", blockId, blockService, offset, count) // start reading the block: message (block_service_id, 'f', block_id, offset) readReq := bsReqInit(blockService.Id, 'f') - bsWrite(readReq, uint64(block.BlockId)) + bsWrite(readReq, uint64(blockId)) bsWrite(readReq, offset) bsWrite(readReq, count) err := bsSend(conn, readReq) @@ -176,8 +177,9 @@ func CopyBlock( io.Writer }, sourceBlockService *msgs.BlockService, + sourceBlockId msgs.BlockId, + sourceBlockCrc [4]byte, sourceBlockSize uint64, - sourceBlock *msgs.FetchedBlock, dstConn interface { io.ReaderFrom io.Reader @@ -187,10 +189,10 @@ func CopyBlock( ) ([8]byte, error) { var proof [8]byte var err error - if err := FetchBlock(logger, sourceConn, sourceBlockService, sourceBlock, 0, uint32(sourceBlockSize)); err != nil { + if err := FetchBlock(logger, sourceConn, sourceBlockService, sourceBlockId, sourceBlockCrc, 0, uint32(sourceBlockSize)); err != nil { return proof, err } - proof, err = WriteBlock(logger, dstConn, dstBlock, sourceConn, uint32(sourceBlockSize), sourceBlock.Crc32) + proof, err = WriteBlock(logger, dstConn, dstBlock, sourceConn, uint32(sourceBlockSize), sourceBlockCrc) if err != nil { return proof, err } diff --git a/go/eggs/client.go b/go/eggs/client.go index eef1fa5f..46618dfb 100644 --- a/go/eggs/client.go +++ b/go/eggs/client.go @@ -62,7 +62,10 @@ func NewClient( counters *ClientCounters, cdcKey cipher.Block, ) (*Client, error) { - c := Client{} + var shardIps [256][4]byte + var shardPorts [256]uint16 + var cdcIp [4]byte + var cdcPort uint16 { resp, err := ShuckleRequest(log, shuckleAddress, &msgs.ShardsReq{}) if err != nil { @@ -73,8 +76,8 @@ func NewClient( if shard.Port == 0 { return nil, fmt.Errorf("shard %v not present in shuckle", i) } - c.shardIps[i] = shard.Ip - c.shardPorts[i] = shard.Port + shardIps[i] = shard.Ip + shardPorts[i] = shard.Port } resp, err = ShuckleRequest(log, shuckleAddress, &msgs.CdcReq{}) if err != nil { @@ -84,30 +87,40 @@ func NewClient( if cdc.Port == 0 { return nil, fmt.Errorf("CDC not present in shuckle") } - c.cdcIp = cdc.Ip - c.cdcPort = cdc.Port + cdcIp = cdc.Ip + cdcPort = cdc.Port + } + return NewClientDirect(log, shid, counters, cdcKey, cdcIp, cdcPort, &shardIps, &shardPorts) +} + +func NewClientDirect( + log LogLevels, + shid *msgs.ShardId, + counters *ClientCounters, + cdcKey cipher.Block, + cdcIp [4]byte, + cdcPort uint16, + shardIps *[256][4]byte, + shardPorts *[256]uint16, +) (c *Client, err error) { + c = &Client{ + shardIps: *shardIps, + shardPorts: *shardPorts, + cdcIp: cdcIp, + cdcPort: cdcPort, } - var err error c.cdcSocket, err = CreateCDCSocket(c.cdcIp, c.cdcPort) if err != nil { return nil, err } if shid != nil { - c.shardSocketFactory, err = newShardSpecificFactory(*shid, c.shardIps[int(*shid)], c.shardPorts[int(*shid)]) - if err != nil { - c.cdcSocket.Close() - return nil, err - } + c.shardSocketFactory = &shardSpecificFactory{shid: *shid} } else { - c.shardSocketFactory, err = newAllShardsFactory(c.shardIps[:], c.shardPorts[:]) - if err != nil { - c.cdcSocket.Close() - return nil, err - } + c.shardSocketFactory = &allShardsFactory{} } c.counters = counters c.cdcKey = cdcKey - return &c, nil + return c, nil } func (c *Client) GetShardSocket(shid msgs.ShardId) (*net.UDPConn, error) { @@ -160,20 +173,11 @@ type allShardsFactory struct { shardLocks [256]sync.Mutex } -func newAllShardsFactory(shardIps [][4]byte, shardPorts []uint16) (*allShardsFactory, error) { - var err error - c := allShardsFactory{} - for i := 0; i < 256; i++ { - c.shardSocks[msgs.ShardId(i)], err = CreateShardSocket(msgs.ShardId(i), shardIps[i], shardPorts[i]) - if err != nil { - return nil, err - } - } - return &c, nil -} - func (c *allShardsFactory) close() { for _, sock := range c.shardSocks { + if sock == nil { + continue + } if err := sock.Close(); err != nil { panic(err) } @@ -181,7 +185,15 @@ func (c *allShardsFactory) close() { } func (c *allShardsFactory) getShardSocket(shid msgs.ShardId, ip [4]byte, port uint16) (*net.UDPConn, error) { - if c.shardLocks[int(shid)].TryLock() { + ix := int(shid) + if c.shardLocks[ix].TryLock() { + if c.shardSocks[ix] == nil { + var err error + c.shardSocks[ix], err = CreateShardSocket(shid, ip, port) + if err != nil { + return nil, err + } + } return c.shardSocks[int(shid)], nil } else { conn, err := CreateShardSocket(shid, ip, port) @@ -212,26 +224,24 @@ type shardSpecificFactory struct { // TODO probably convert these errors to stderr, we can't do much with them usually // but they'd be worth knowing about func (c *shardSpecificFactory) close() error { + if c.shardSock == nil { + return nil + } if err := c.shardSock.Close(); err != nil { return err } return nil } -func newShardSpecificFactory(shid msgs.ShardId, ip [4]byte, port uint16) (*shardSpecificFactory, error) { - c := shardSpecificFactory{ - shid: shid, - } - var err error - c.shardSock, err = CreateShardSocket(shid, ip, port) - if err != nil { - return nil, err - } - return &c, nil -} - func (c *shardSpecificFactory) getShardSocket(shid msgs.ShardId, ip [4]byte, port uint16) (*net.UDPConn, error) { if shid == c.shid && c.shardLock.TryLock() { + if c.shardSock == nil { + var err error + c.shardSock, err = CreateShardSocket(shid, ip, port) + if err != nil { + return nil, err + } + } return c.shardSock, nil } else { shardSock, err := CreateShardSocket(shid, ip, port) diff --git a/go/eggs/managedprocess.go b/go/eggs/managedprocess.go index 5cf803e6..f8ea6b6d 100644 --- a/go/eggs/managedprocess.go +++ b/go/eggs/managedprocess.go @@ -263,6 +263,7 @@ type BlockServiceOpts struct { Verbose bool ShuckleAddress string OwnIp string + Profile bool } func createDataDir(dir string) { @@ -290,6 +291,9 @@ func (procs *ManagedProcesses) StartBlockService(ll LogLevels, opts *BlockServic if opts.ShuckleAddress != "" { args = append(args, "-shuckle", opts.ShuckleAddress) } + if opts.Profile { + args = append(args, "-profile-file") + } args = append(args, opts.Path, opts.StorageClass.String()) procs.Start(ll, &ManagedProcessArgs{ Name: fmt.Sprintf("block service (port %d)", opts.Port), @@ -307,6 +311,7 @@ type EggsFuseOpts struct { Verbose bool Wait bool ShuckleAddress string + Profile bool } func (procs *ManagedProcesses) StartEggsFuse(ll LogLevels, opts *EggsFuseOpts) string { @@ -326,6 +331,9 @@ func (procs *ManagedProcesses) StartEggsFuse(ll LogLevels, opts *EggsFuseOpts) s if opts.Verbose { args = append(args, "-verbose") } + if opts.Profile { + args = append(args, "-profile-file", path.Join(opts.Path, "prof")) + } args = append(args, mountPoint) procs.Start(ll, &ManagedProcessArgs{ Name: "eggsfuse", @@ -418,6 +426,7 @@ type ShardOpts struct { OutgoingPacketDrop float64 ShuckleAddress string OwnIp string + Port uint16 } func (procs *ManagedProcesses) StartShard(ll LogLevels, opts *ShardOpts) { @@ -427,16 +436,19 @@ func (procs *ManagedProcesses) StartShard(ll LogLevels, opts *ShardOpts) { createDataDir(opts.Dir) args := []string{ "-log-file", path.Join(opts.Dir, "log"), - opts.Dir, - fmt.Sprintf("%d", int(opts.Shid)), "-incoming-packet-drop", fmt.Sprintf("%g", opts.IncomingPacketDrop), "-outgoing-packet-drop", fmt.Sprintf("%g", opts.OutgoingPacketDrop), "-shuckle", opts.ShuckleAddress, "-own-ip", opts.OwnIp, + "-port", fmt.Sprintf("%v", opts.Port), } if opts.Verbose { args = append(args, "-verbose") } + args = append(args, + opts.Dir, + fmt.Sprintf("%d", int(opts.Shid)), + ) cppDir := cppDir() mpArgs := ManagedProcessArgs{ Name: fmt.Sprintf("shard %v", opts.Shid), @@ -485,6 +497,7 @@ type CDCOpts struct { Perf bool ShuckleAddress string OwnIp string + Port uint16 } func (procs *ManagedProcesses) StartCDC(ll LogLevels, opts *CDCOpts) { @@ -496,11 +509,12 @@ func (procs *ManagedProcesses) StartCDC(ll LogLevels, opts *CDCOpts) { "-log-file", path.Join(opts.Dir, "log"), "-shuckle", opts.ShuckleAddress, "-own-ip", opts.OwnIp, - opts.Dir, + "-port", fmt.Sprintf("%v", opts.Port), } if opts.Verbose { args = append(args, "-verbose") } + args = append(args, opts.Dir) cppDir := cppDir() mpArgs := ManagedProcessArgs{ Name: "cdc", @@ -524,8 +538,8 @@ func (procs *ManagedProcesses) StartCDC(ll LogLevels, opts *CDCOpts) { }, mpArgs.Args..., ) - procs.Start(ll, &mpArgs) - } else if opts.Perf { + } + if opts.Perf { mpArgs.Name = fmt.Sprintf("%s (perf)", mpArgs.Name) mpArgs.Exe = "perf" mpArgs.Args = append( @@ -536,10 +550,8 @@ func (procs *ManagedProcesses) StartCDC(ll LogLevels, opts *CDCOpts) { }, mpArgs.Args..., ) - procs.Start(ll, &mpArgs) - } else { - procs.Start(ll, &mpArgs) } + procs.Start(ll, &mpArgs) } func WaitForShard(log LogLevels, shuckleAddress string, shid msgs.ShardId, timeout time.Duration) { diff --git a/go/eggs/migrate.go b/go/eggs/migrate.go index 2a910282..56351835 100644 --- a/go/eggs/migrate.go +++ b/go/eggs/migrate.go @@ -76,7 +76,7 @@ func copyBlock( if err != nil { return 0, err } - proof, err = mbs.CopyBlock(log, srcConn, blockService, blockSize, block, dstConn, dstBlock) + proof, err = mbs.CopyBlock(log, srcConn, blockService, block.BlockId, block.Crc32, blockSize, dstConn, dstBlock) dstConn.Close() if err != nil { return 0, fmt.Errorf("could not copy block: %w", err) diff --git a/go/eggs/mockableblockservice.go b/go/eggs/mockableblockservice.go index 6f605aab..9273c1a4 100644 --- a/go/eggs/mockableblockservice.go +++ b/go/eggs/mockableblockservice.go @@ -29,7 +29,8 @@ type MockableBlockServices interface { logger LogLevels, conn MockableBlockServiceConn, blockService *msgs.BlockService, - block *msgs.FetchedBlock, + blockId msgs.BlockId, + crc32 [4]byte, offset uint32, count uint32, ) error @@ -42,8 +43,9 @@ type MockableBlockServices interface { logger LogLevels, sourceConn MockableBlockServiceConn, sourceBlockService *msgs.BlockService, + sourceBlockId msgs.BlockId, + sourceBlockCrc [4]byte, sourceBlockSize uint64, - sourceBlock *msgs.FetchedBlock, dstConn MockableBlockServiceConn, dstBlock *msgs.BlockInfo, ) ([8]byte, error) @@ -70,11 +72,12 @@ func (RealBlockServices) FetchBlock( logger LogLevels, conn MockableBlockServiceConn, blockService *msgs.BlockService, - block *msgs.FetchedBlock, + blockId msgs.BlockId, + blockCrc [4]byte, offset uint32, count uint32, ) error { - return FetchBlock(logger, conn, blockService, block, offset, count) + return FetchBlock(logger, conn, blockService, blockId, blockCrc, offset, count) } func (RealBlockServices) EraseBlock( @@ -89,14 +92,17 @@ func (RealBlockServices) CopyBlock( logger LogLevels, sourceConn MockableBlockServiceConn, sourceBlockService *msgs.BlockService, + sourceBlockId msgs.BlockId, + sourceBlockCrc [4]byte, sourceBlockSize uint64, - sourceBlock *msgs.FetchedBlock, dstConn MockableBlockServiceConn, dstBlock *msgs.BlockInfo, ) ([8]byte, error) { - return CopyBlock(logger, sourceConn, sourceBlockService, sourceBlockSize, sourceBlock, dstConn, dstBlock) + return CopyBlock(logger, sourceConn, sourceBlockService, sourceBlockId, sourceBlockCrc, sourceBlockSize, dstConn, dstBlock) } +var _ = (MockableBlockServices)(RealBlockServices{}) + type MockedBlockServices struct { Keys map[msgs.BlockServiceId]cipher.Block } @@ -155,7 +161,8 @@ func (mbs *MockedBlockServices) FetchBlock( logger LogLevels, conn MockableBlockServiceConn, blockService *msgs.BlockService, - block *msgs.FetchedBlock, + blockId msgs.BlockId, + blockCrc [4]byte, offset uint32, count uint32, ) error { @@ -178,8 +185,9 @@ func (mbs *MockedBlockServices) CopyBlock( logger LogLevels, sourceConn MockableBlockServiceConn, sourceBlockService *msgs.BlockService, + sourceBlockId msgs.BlockId, + sourceBlockCrc [4]byte, sourceBlockSize uint64, - sourceBlock *msgs.FetchedBlock, dstConn MockableBlockServiceConn, dstBlock *msgs.BlockInfo, ) ([8]byte, error) { @@ -189,3 +197,5 @@ func (mbs *MockedBlockServices) CopyBlock( } return BlockWriteProof(dstBlock.BlockServiceId, dstBlock.BlockId, key), nil } + +var _ = (MockableBlockServices)((*MockedBlockServices)(nil)) diff --git a/go/eggsfuse/eggsfuse.go b/go/eggsfuse/eggsfuse.go index 38ae4344..3a0967b9 100644 --- a/go/eggsfuse/eggsfuse.go +++ b/go/eggsfuse/eggsfuse.go @@ -9,6 +9,7 @@ import ( "net" "os" "os/signal" + "runtime/pprof" "sync" "syscall" "xtx/eggsfs/eggs" @@ -625,8 +626,6 @@ func (of *openFile) readInternal(dest []byte, off int64) (fuse.ReadResult, sysca panic(fmt.Errorf("unexpected out of bounds spanOffset/remainingInSpan")) } - // log.Debug("reading file %v at %v, count %v, spanOffset=%v, remainingInSpan=%v", ) - if currentSpan.StorageClass == msgs.INLINE_STORAGE { if err := of.resetBlockService(); err != 0 { return nil, err @@ -644,7 +643,7 @@ func (of *openFile) readInternal(dest []byte, off int64) (fuse.ReadResult, sysca if len(dest) < int(toRead) { toRead = uint32(len(dest)) } - if err := eggs.FetchBlock(log, of.currentBlockServiceConn, of.currentBlockService, block, uint32(spanOffset), toRead); err != nil { + if err := eggs.FetchBlock(log, of.currentBlockServiceConn, of.currentBlockService, block.BlockId, block.Crc32, uint32(spanOffset), toRead); err != nil { panic(err) } dest = dest[:toRead] @@ -728,24 +727,28 @@ var _ = (fs.FileFlusher)((*transientFile)(nil)) var _ = (fs.FileReader)((*openFile)(nil)) var _ = (fs.FileFlusher)((*openFile)(nil)) -func unmount(server *fuse.Server, unmounted *bool) { - if *unmounted { +func terminate(server *fuse.Server, terminated *bool) { + if *terminated { return } + *terminated = true if err := server.Unmount(); err != nil { - fmt.Fprintf(os.Stderr, "could not unmount: %v\n", err) + log.Info("could not unmount: %v\n", err) } + pprof.StopCPUProfile() } func usage() { fmt.Fprintf(os.Stderr, "Usage: %s [options] \n", os.Args[0]) flag.PrintDefaults() } + func main() { verbose := flag.Bool("verbose", false, "Enables debug logging.") logFile := flag.String("log-file", "", "Redirect logging output to given file.") signalParent := flag.Bool("signal-parent", false, "If passed, will send USR1 to parent when ready -- useful for tests.") shuckleAddress := flag.String("shuckle", eggs.DEFAULT_SHUCKLE_ADDRESS, "Shuckle address (host:port).") + profileFile := flag.String("profile-file", "", "If set, will write CPU profile here.") flag.Usage = usage flag.Parse() @@ -776,6 +779,15 @@ func main() { Logger: logger, } + if *profileFile != "" { + f, err := os.Create(*profileFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not open profile file %v", *profileFile) + os.Exit(1) + } + pprof.StartCPUProfile(f) // we stop in terminate() + } + var err error client, err = eggs.NewClient(log, *shuckleAddress, nil, nil, nil) if err != nil { @@ -802,8 +814,8 @@ func main() { } } - unmounted := false - defer unmount(server, &unmounted) + terminated := false + defer terminate(server, &terminated) // Cleanup if we get killed with a signal. Obviously we can't do much // in the case of SIGKILL or SIGQUIT. signalChan := make(chan os.Signal, 1) @@ -811,7 +823,7 @@ func main() { go func() { sig := <-signalChan signal.Stop(signalChan) - unmount(server, &unmounted) + terminate(server, &terminated) syscall.Kill(syscall.Getpid(), sig.(syscall.Signal)) }() diff --git a/go/eggsrun/eggsrun.go b/go/eggsrun/eggsrun.go index cbe08700..546fb73d 100644 --- a/go/eggsrun/eggsrun.go +++ b/go/eggsrun/eggsrun.go @@ -20,14 +20,33 @@ func noRunawayArgs() { } func main() { - buildType := flag.String("build-type", "alpine", "C++ build type, one of alpine/release/debug/sanitized/valgrind") + buildType := flag.String("build-type", "", "C++ build type, one of alpine/release/debug/sanitized/valgrind. Either this or -exe-dir must be set.") verbose := flag.Bool("verbose", false, "Note that verbose won't do much for the shard unless you build with debug.") dataDir := flag.String("data-dir", "", "Directory where to store the EggsFS data. If not present a temporary directory will be used.") - hddBlockServices := flag.Uint("hdd-block-services", 10, "Number of HDD block services (default 10).") - flashBlockServices := flag.Uint("flash-block-services", 5, "Number of HDD block services (default 5).") + hddBlockServices := flag.Uint("hdd-block-services", 10, "Number of HDD block services (default 0).") + flashBlockServices := flag.Uint("flash-block-services", 5, "Number of HDD block services (default 0).") + profile := flag.Bool("profile", false, "Whether to run code (both Go and C++) with profiling.") + ownIp := flag.String("own-ip", "127.0.0.1", "What IP to advertise to shuckle for these services.") + shuckleBincodePort := flag.Uint("shuckle-bincode-port", 10000, "") + shuckleHttpPort := flag.Uint("shuckle-http-port", 10001, "") + startingPort := flag.Uint("start-port", 10002, "The services will be assigned port in this order, CDC, shard_000, ..., shard_255, bs_0, ..., bs_n. Otherwise ports will be chosen randomly.") flag.Parse() noRunawayArgs() + validPort := func(port uint) { + if port > uint(^uint16(0)) { + fmt.Fprintf(os.Stderr, "Invalid port %v.\n", port) + os.Exit(2) + } + } + if *shuckleBincodePort == 0 { + fmt.Fprintf(os.Stderr, "-shuckle-bincode-port can't be automatically picked.\n") + os.Exit(2) + } + validPort(*shuckleBincodePort) + validPort(*shuckleHttpPort) + validPort(*startingPort) + if *verbose && *buildType != "debug" { fmt.Printf("We're building with build type %v, which is not \"debug\", and you also passed in -verbose.\nBe aware that you won't get debug messages for C++ binaries.\n\n", *buildType) } @@ -39,6 +58,10 @@ func main() { } *dataDir = dir fmt.Printf("running with temp data dir %v\n", *dataDir) + } else { + if err := os.Mkdir(*dataDir, 0777); err != nil && !os.IsExist(err) { + panic(fmt.Errorf("could not create data dir: %w", err)) + } } logFile := path.Join(*dataDir, "go-log") @@ -71,12 +94,11 @@ func main() { fmt.Printf("starting components\n") // Start shuckle - shucklePort := uint16(10000) - shuckleAddress := fmt.Sprintf("localhost:%v", shucklePort) + shuckleAddress := fmt.Sprintf("127.0.0.1:%v", *shuckleBincodePort) procs.StartShuckle(log, &eggs.ShuckleOpts{ Exe: shuckleExe, - BincodePort: shucklePort, - HttpPort: shucklePort + 1, + BincodePort: uint16(*shuckleBincodePort), + HttpPort: uint16(*shuckleHttpPort), Verbose: *verbose, Dir: path.Join(*dataDir, "shuckle"), }) @@ -87,39 +109,56 @@ func main() { if i >= *hddBlockServices { storageClass = msgs.FLASH_STORAGE } - procs.StartBlockService(log, &eggs.BlockServiceOpts{ + opts := eggs.BlockServiceOpts{ Exe: blockServiceExe, Path: path.Join(*dataDir, fmt.Sprintf("bs_%d", i)), StorageClass: storageClass, FailureDomain: fmt.Sprintf("%d", i), Verbose: *verbose, - ShuckleAddress: fmt.Sprintf("localhost:%d", shucklePort), - OwnIp: "127.0.0.1", - }) + ShuckleAddress: shuckleAddress, + OwnIp: *ownIp, + Profile: *profile, + } + if *startingPort != 0 { + opts.Port = uint16(*startingPort) + 257 + uint16(i) + } + procs.StartBlockService(log, &opts) } // Start CDC - procs.StartCDC(log, &eggs.CDCOpts{ - Exe: cppExes.CDCExe, - Dir: path.Join(*dataDir, "cdc"), - Verbose: *verbose && *buildType == "debug", - Valgrind: *buildType == "valgrind", - ShuckleAddress: shuckleAddress, - OwnIp: "127.0.0.1", - }) + { + opts := eggs.CDCOpts{ + Exe: cppExes.CDCExe, + Dir: path.Join(*dataDir, "cdc"), + Verbose: *verbose && *buildType == "debug", + Valgrind: *buildType == "valgrind", + ShuckleAddress: shuckleAddress, + OwnIp: *ownIp, + Perf: *profile, + } + if *startingPort != 0 { + opts.Port = uint16(*startingPort) + } + procs.StartCDC(log, &opts) + } // Start shards for i := 0; i < 256; i++ { shid := msgs.ShardId(i) - procs.StartShard(log, &eggs.ShardOpts{ + opts := eggs.ShardOpts{ Exe: cppExes.ShardExe, Dir: path.Join(*dataDir, fmt.Sprintf("shard_%03d", i)), Verbose: *verbose && *buildType == "debug", Shid: shid, Valgrind: *buildType == "valgrind", ShuckleAddress: shuckleAddress, - OwnIp: "127.0.0.1", - }) + OwnIp: *ownIp, + Perf: *profile, + } + if *startingPort != 0 { + opts.Port = uint16(*startingPort) + 1 + uint16(i) + } + procs.StartShard(log, &opts) } waitShuckleFor := 5 * time.Second @@ -127,7 +166,7 @@ func main() { waitShuckleFor = 30 * time.Second } fmt.Printf("waiting for shuckle for %v...\n", waitShuckleFor) - eggs.WaitForShuckle(log, fmt.Sprintf("localhost:%v", shucklePort), int(*hddBlockServices+*flashBlockServices), waitShuckleFor) + eggs.WaitForShuckle(log, shuckleAddress, int(*hddBlockServices+*flashBlockServices), waitShuckleFor) fmt.Printf("operational 🤖\n") diff --git a/go/eggsshuckle/base.html b/go/eggsshuckle/base.html new file mode 100644 index 00000000..3c8282d6 --- /dev/null +++ b/go/eggsshuckle/base.html @@ -0,0 +1,33 @@ + + + + + + + + + + + EggsFS — {{.Title}} + + +
+

{{.Title}}

+ {{template "body" .Body}} +
+ + + + diff --git a/go/eggsshuckle/bootstrap.min.css b/go/eggsshuckle/bootstrap.min.css new file mode 100644 index 00000000..edfbbb03 --- /dev/null +++ b/go/eggsshuckle/bootstrap.min.css @@ -0,0 +1,7 @@ +@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}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/go/eggsshuckle/directory.html b/go/eggsshuckle/directory.html new file mode 100644 index 00000000..2f30949e --- /dev/null +++ b/go/eggsshuckle/directory.html @@ -0,0 +1,99 @@ +{{define "body"}} + + + {{if .Path}} + + + + + {{end}} + + + + + + + + + + + + + +
Path{{.Path}}
Id{{.Id}}
Owner + {{if eq .Owner "0x0"}} + NONE + {{else}} + {{.Owner}} + {{end}} +
Mtime{{.Mtime}}
+

Edges

+
+
+ + +
+
+ + + + + + + + + + + + + + {{range $edge := .Edges}} + {{if $edge.Current}} + + {{else}} + + {{end}} + + + + + + + + + {{end}} + +
NameHashNameTypeTargetCreationTimeOwnedLocked
{{$edge.NameHash}} + {{if eq $edge.TargetId "0x0"}} + {{$edge.Name}} + {{else}} + {{if and $edge.Current $.Path}} + {{$edge.Name}} + {{else}} + {{$edge.Name}} + {{end}} + {{end}} + + {{$edge.Type}} + + {{if not (eq $edge.TargetId "0x0")}} + {{$edge.TargetId}} + {{else}} + DELETE + {{end}} + {{$edge.CreationTime}} + {{if $edge.Owned}} + yes + {{else}} + no + {{end}} + + {{if $edge.Current}} + {{if $edge.Locked}} + yes + {{else}} + no + {{end}} + {{end}} +
+{{end}} \ No newline at end of file diff --git a/go/eggsshuckle/eggsshuckle.go b/go/eggsshuckle/eggsshuckle.go index 4e5a0acc..570d5fcf 100644 --- a/go/eggsshuckle/eggsshuckle.go +++ b/go/eggsshuckle/eggsshuckle.go @@ -3,7 +3,7 @@ package main import ( "bytes" _ "embed" - "encoding/base64" + "encoding/hex" "flag" "fmt" "html/template" @@ -11,13 +11,39 @@ import ( "log" "net" "net/http" + "net/url" "os" + "runtime/debug" "sort" + "strconv" + "strings" "sync" "xtx/eggsfs/eggs" "xtx/eggsfs/msgs" ) +type namedTemplate struct { + name string + body string +} + +func parseTemplates(ts ...namedTemplate) (tmpl *template.Template) { + for _, t := range ts { + if tmpl == nil { + tmpl = template.New(t.name) + if _, err := tmpl.Parse(t.body); err != nil { + panic(err) + } + } else { + other := tmpl.New(t.name) + if _, err := other.Parse(t.body); err != nil { + panic(err) + } + } + } + return tmpl +} + type state struct { mutex sync.RWMutex blockServices map[msgs.BlockServiceId]*msgs.BlockServiceInfo @@ -26,7 +52,7 @@ type state struct { cdcPort uint16 } -func newBlockServices() *state { +func newState() *state { return &state{ mutex: sync.RWMutex{}, blockServices: make(map[msgs.BlockServiceId]*msgs.BlockServiceInfo), @@ -46,6 +72,7 @@ func handleBlockServicesForShard(ll eggs.LogLevels, s *state, w io.Writer, req * i++ } + template.ParseFiles() return &resp } @@ -159,6 +186,110 @@ func noRunawayArgs() { } } +//go:embed shuckle.png +var shucklePngStr []byte + +//go:embed shuckleface.png +var shuckleFacePngStr []byte + +//go:embed bootstrap.min.css +var bootstrapCssStr []byte + +type pageData struct { + Title string + Body any +} + +//go:embed base.html +var baseTemplateStr string + +func renderPage( + tmpl *template.Template, + data *pageData, +) []byte { + content := bytes.NewBuffer([]byte{}) + if err := tmpl.Execute(content, &data); err != nil { + panic(err) + } + return content.Bytes() +} + +func sendPage( + tmpl *template.Template, + data *pageData, + status int, +) (io.ReadCloser, int64, int) { + content := renderPage(tmpl, data) + return io.NopCloser(bytes.NewReader(content)), int64(len(content)), status +} + +func handleWithRecover( + log eggs.LogLevels, + w http.ResponseWriter, + r *http.Request, + handle func(log eggs.LogLevels, query url.Values) (io.ReadCloser, int64, int), +) { + statusPtr := new(int) + var content io.ReadCloser + defer func() { + if content != nil { + content.Close() + } + }() + sizePtr := new(int64) + func() { + var status int + var size int64 + defer func() { + if r := recover(); r != nil { + content, size, status = sendPage(errorPage(http.StatusInternalServerError, fmt.Sprintf("PANIC: %v\n%v", r, string(debug.Stack())))) + *statusPtr = status + *sizePtr = size + } + }() + if r.Method != http.MethodGet { + content, size, status = sendPage(errorPage(http.StatusMethodNotAllowed, "Only GET allowed")) + } else { + query, err := url.ParseQuery(r.URL.RawQuery) + if err != nil { + content, size, status = sendPage(errorPage(http.StatusBadRequest, "could not parse query")) + } else { + content, size, status = handle(log, query) + } + } + *statusPtr = status + *sizePtr = size + }() + w.WriteHeader(*statusPtr) + if written, err := io.CopyN(w, content, *sizePtr); err != nil { + log.RaiseAlert(fmt.Errorf("Could not send full response of size %v, %v written: %w", *sizePtr, written, err)) + } +} + +func handlePage( + log eggs.LogLevels, + w http.ResponseWriter, + r *http.Request, + // third result is status code + page func(query url.Values) (*template.Template, *pageData, int), +) { + handleWithRecover( + log, w, r, + func(log eggs.LogLevels, query url.Values) (io.ReadCloser, int64, int) { + return sendPage(page(query)) + }, + ) +} + +//go:embed error.html +var errorTemplateStr string + +var errorTemplate *template.Template + +func errorPage(status int, body string) (*template.Template, *pageData, int) { + return errorTemplate, &pageData{Title: "Error!", Body: body}, status +} + type indexBlockService struct { Id msgs.BlockServiceId Addr string @@ -171,189 +302,487 @@ type indexBlockService struct { } type indexData struct { - ShuckleFacePngBase64 string - ShucklePngBase64 string - NumBlockServices int - NumFailureDomains int - TotalCapacity string - TotalUsed string - TotalUsedPercentage string - CDCAddr string - BlockServices []indexBlockService - ShardsAddrs []string - Blocks uint64 + NumBlockServices int + NumFailureDomains int + TotalCapacity string + TotalUsed string + TotalUsedPercentage string + CDCAddr string + BlockServices []indexBlockService + ShardsAddrs []string + Blocks uint64 } -//go:embed shuckle.png -var shucklePng []byte -var shucklePngBase64 string - -//go:embed shuckleface.png -var shuckleFacePng []byte -var shuckleFacePngBase64 string - -const indexTemplateStr string = ` - - - - - Shuckle - - - -

Shuckle

- Mr. Shuckle -

Overview

-
    -
  • {{.NumBlockServices}} block services on {{.NumFailureDomains}} failure domains
  • -
  • Total capacity: {{.TotalCapacity}}
  • -
  • {{.TotalUsed}} used over {{.Blocks}} blocks ({{.TotalUsedPercentage}})
  • -
-

CDC

- - - - - - - - - - -
Address
{{.CDCAddr}}
-

Shards

- - - - - - - - {{ range $id, $addr := .ShardsAddrs }} - - - - - {{end}} - -
IdAddress
{{printf "%03v" $id}}{{$addr}}
-

Block services

- - - - - - - - - - - - - - {{ range .BlockServices }} - - - - - - - - - - - {{end}} - -
FailureDomainAddressPathIdStorageClassBlocksCapacityAvailable
{{.FailureDomain}}{{.Addr}}{{.Path}}{{.Id}}{{.StorageClass}}{{.Blocks}}{{.CapacityBytes}}{{.AvailableBytes}}
- - -` +//go:embed index.html +var indexTemplateStr string func formatSize(bytes uint64) string { bytesf := float64(bytes) if bytes == 0 { return "0" } - if bytesf < 1e6 { - return fmt.Sprintf("%.2fKB", bytesf/1e3) + if bytes < (uint64(1) << 20) { + return fmt.Sprintf("%.2fKiB", bytesf/float64(uint64(1)<<10)) } - if bytesf < 1e9 { - return fmt.Sprintf("%.2fMB", bytesf/1e6) + if bytes < (uint64(1) << 30) { + return fmt.Sprintf("%.2fMiB", bytesf/float64(uint64(1)<<20)) } - if bytesf < 1e12 { - return fmt.Sprintf("%.2fGB", bytesf/1e9) + if bytes < (uint64(1) << 40) { + return fmt.Sprintf("%.2fGiB", bytesf/float64(uint64(1)<<30)) } - if bytesf < 1e15 { - return fmt.Sprintf("%.2fTB", bytesf/1e12) + if bytes < (uint64(1) << 50) { + return fmt.Sprintf("%.2fTiB", bytesf/float64(uint64(1)<<40)) } - return fmt.Sprintf("%.2fPB", bytesf/1e15) + return fmt.Sprintf("%.2fPiB", bytesf/float64(uint64(1)<<50)) } -func handleIndex(ll eggs.LogLevels, template *template.Template, state *state, w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) - return +func formatPreciseSize(bytes uint64) string { + if bytes == 0 { + return "0" } + if bytes%(1<<30) == 0 { + return fmt.Sprintf("%vGiB", bytes>>30) + } + if bytes%(1<<20) == 0 { + return fmt.Sprintf("%vMiB", bytes>>20) + } + if bytes%(1<<10) == 0 { + return fmt.Sprintf("%vKiB", bytes>>10) + } + return fmt.Sprintf("%vB", bytes) +} + +var shuckleTemplate *template.Template + +func handleIndex(ll eggs.LogLevels, state *state, w http.ResponseWriter, r *http.Request) { + handlePage( + ll, w, r, + func(_ url.Values) (*template.Template, *pageData, int) { + state.mutex.RLock() + defer state.mutex.RUnlock() + + data := indexData{ + NumBlockServices: len(state.blockServices), + } + data.CDCAddr = fmt.Sprintf("%v:%v", net.IP(state.cdcIp[:]), state.cdcPort) + totalCapacityBytes := uint64(0) + totalAvailableBytes := uint64(0) + failureDomainsBytes := make(map[string]struct{}) + for _, bs := range state.blockServices { + data.BlockServices = append(data.BlockServices, indexBlockService{ + Id: bs.Id, + Addr: fmt.Sprintf("%v:%v", net.IP(bs.Ip[:]), bs.Port), + StorageClass: bs.StorageClass, + FailureDomain: string(bs.FailureDomain[:bytes.Index(bs.FailureDomain[:], []byte{0})]), + CapacityBytes: formatSize(bs.CapacityBytes), + AvailableBytes: formatSize(bs.AvailableBytes), + Path: bs.Path, + Blocks: bs.Blocks, + }) + failureDomainsBytes[string(bs.FailureDomain[:])] = struct{}{} + totalAvailableBytes += bs.AvailableBytes + totalCapacityBytes += bs.CapacityBytes + data.Blocks += bs.Blocks + } + sort.Slice( + data.BlockServices, + func(i, j int) bool { + a := data.BlockServices[i] + b := data.BlockServices[j] + if len(a.FailureDomain) != len(b.FailureDomain) { + return len(a.FailureDomain) < len(b.FailureDomain) + } + if a.FailureDomain != b.FailureDomain { + return a.FailureDomain < b.FailureDomain + } + if a.Path != b.Path { + return a.Path < b.Path + } + return a.Id < b.Id + }, + ) + for _, shard := range state.shards { + data.ShardsAddrs = append(data.ShardsAddrs, fmt.Sprintf("%v:%v", net.IP(shard.Ip[:]), shard.Port)) + } + data.TotalUsed = formatSize(totalCapacityBytes - totalAvailableBytes) + data.TotalCapacity = formatSize(totalAvailableBytes) + data.NumFailureDomains = len(failureDomainsBytes) + if totalAvailableBytes == 0 { + data.TotalUsedPercentage = "0%" + } else { + data.TotalUsedPercentage = fmt.Sprintf("%0.2f%%", 100.0*float64(totalAvailableBytes)/float64(totalCapacityBytes)) + } + return shuckleTemplate, &pageData{Title: "Shuckle", Body: &data}, http.StatusOK + }, + ) +} + +//go:embed file.html +var fileTemplateStr string + +type fileBlock struct { + Id string + Crc32 string + BlockService string + Link string +} + +type fileSpan struct { + Offset string + Size string + Crc32 string + StorageClass string + BlockSize string + BodyBlocks []fileBlock + BodyBytes string + ParityBlocks int + DataBlocks int +} + +type fileData struct { + Id string + Path string // might be empty + Size string + Mtime string + Spans []fileSpan +} + +//go:embed directory.html +var directoryTemplateStr string + +type directoryEdge struct { + Current bool + TargetId string + Owned bool + NameHash string + Name string + CreationTime string + Type string + Locked bool +} + +type directoryData struct { + Id string + Path string // might be empty + Mtime string + Owner string + Edges []directoryEdge +} + +func newClient(log eggs.LogLevels, state *state) *eggs.Client { state.mutex.RLock() defer state.mutex.RUnlock() - data := indexData{ - ShucklePngBase64: shucklePngBase64, - ShuckleFacePngBase64: shuckleFacePngBase64, - NumBlockServices: len(state.blockServices), + var shardIps [256][4]byte + var shardPorts [256]uint16 + for i, si := range state.shards { + shardIps[i] = si.Ip + shardPorts[i] = si.Port } - data.CDCAddr = fmt.Sprintf("%v:%v", net.IP(state.cdcIp[:]), state.cdcPort) - totalCapacityBytes := uint64(0) - totalAvailableBytes := uint64(0) - failureDomainsBytes := make(map[string]struct{}) - for _, bs := range state.blockServices { - data.BlockServices = append(data.BlockServices, indexBlockService{ - Id: bs.Id, - Addr: fmt.Sprintf("%v:%v", net.IP(bs.Ip[:]), bs.Port), - StorageClass: bs.StorageClass, - FailureDomain: string(bs.FailureDomain[:bytes.Index(bs.FailureDomain[:], []byte{0})]), - CapacityBytes: formatSize(bs.CapacityBytes), - AvailableBytes: formatSize(bs.AvailableBytes), - Path: bs.Path, - Blocks: bs.Blocks, - }) - failureDomainsBytes[string(bs.FailureDomain[:])] = struct{}{} - totalAvailableBytes += bs.AvailableBytes - totalCapacityBytes += bs.CapacityBytes - data.Blocks += bs.Blocks + client, err := eggs.NewClientDirect(log, nil, nil, nil, state.cdcIp, state.cdcPort, &shardIps, &shardPorts) + if err != nil { + panic(err) } - sort.Slice( - data.BlockServices, - func(i, j int) bool { - a := data.BlockServices[i] - b := data.BlockServices[j] - if len(a.FailureDomain) != len(b.FailureDomain) { - return len(a.FailureDomain) < len(b.FailureDomain) + return client +} + +func lookup(log eggs.LogLevels, client *eggs.Client, path string) *msgs.InodeId { + segments := strings.Split(path, "/")[1:] // starts with / + id := msgs.ROOT_DIR_INODE_ID + for _, segment := range segments { + if segment == "" { + continue + } + req := msgs.LookupReq{ + DirId: id, + Name: segment, + } + resp := msgs.LookupResp{} + if err := client.ShardRequest(log, id.Shard(), &req, &resp); err != nil { + eggsErr, ok := err.(msgs.ErrCode) + if ok { + if eggsErr == msgs.NAME_NOT_FOUND { + return nil + } } - if a.FailureDomain != b.FailureDomain { - return a.FailureDomain < b.FailureDomain + panic(err) + } + id = resp.TargetId + } + return &id +} + +var fileTemplate *template.Template +var directoryTemplate *template.Template + +func handleInode( + log eggs.LogLevels, + state *state, + w http.ResponseWriter, + r *http.Request, +) { + handlePage( + log, w, r, + func(query url.Values) (*template.Template, *pageData, int) { + path := query.Get("path") + if path != "" { + if len(path) == 0 || path[0] != '/' { + return errorPage(http.StatusBadRequest, "Path must start with /") + } } - if a.Path != b.Path { - return a.Path < b.Path + var id msgs.InodeId + idStr := query.Get("id") + if idStr != "" { + i, err := strconv.ParseUint(idStr, 0, 63) + if err != nil { + return errorPage(http.StatusBadRequest, fmt.Sprintf("cannot parse id %v: %v", idStr, err)) + } + id = msgs.InodeId(i) + } + if id != msgs.NULL_INODE_ID && path != "" { + return errorPage(http.StatusBadRequest, "cannot specify both id and path") + } + if id == msgs.NULL_INODE_ID && path == "" { + path = "/" + } + client := newClient(log, state) + if id == msgs.NULL_INODE_ID { + mbId := lookup(log, client, path) + if mbId == nil { + return errorPage(http.StatusNotFound, fmt.Sprintf("path '%v' not found", path)) + } + id = *mbId + } + if id.Type() == msgs.DIRECTORY { + data := directoryData{ + Id: fmt.Sprintf("%v", id), + Path: path, + } + title := fmt.Sprintf("Directory %v", data.Id) + { + resp := msgs.StatDirectoryResp{} + if err := client.ShardRequest(log, id.Shard(), &msgs.StatDirectoryReq{Id: id}, &resp); err != nil { + panic(err) + } + data.Owner = resp.Owner.String() + data.Mtime = resp.Mtime.String() + } + { + _, full := query["full"] + if full { + + } else { + req := msgs.FullReadDirReq{DirId: id} + resp := msgs.FullReadDirResp{} + for { + if err := client.ShardRequest(log, id.Shard(), &req, &resp); err != nil { + panic(err) + } + for _, edge := range resp.Results { + dataEdge := directoryEdge{ + Current: edge.Current, + TargetId: fmt.Sprintf("%v", edge.TargetId.Id()), + Owned: edge.Current || edge.TargetId.Extra(), + NameHash: fmt.Sprintf("%08x", edge.NameHash), + Name: edge.Name, + CreationTime: edge.CreationTime.String(), + Locked: edge.Current && edge.TargetId.Extra(), + } + if edge.TargetId.Id() != msgs.NULL_INODE_ID { + dataEdge.Type = edge.TargetId.Id().Type().String() + } + data.Edges = append(data.Edges, dataEdge) + } + req.Cursor = resp.Next + if req.Cursor == (msgs.FullReadDirCursor{}) { + break + } + } + } + } + sort.Slice( + data.Edges, + func(i, j int) bool { + a := data.Edges[i] + b := data.Edges[j] + if a.Name != b.Name { + return a.Name < b.Name + } + if a.NameHash != b.NameHash { + return a.NameHash < b.NameHash + } + return a.CreationTime < b.CreationTime + }, + ) + return directoryTemplate, &pageData{Title: title, Body: &data}, http.StatusOK + } else { + data := fileData{ + Id: fmt.Sprintf("%v", id), + Path: path, + } + title := fmt.Sprintf("File %v", data.Id) + { + resp := msgs.StatFileResp{} + if err := client.ShardRequest(log, id.Shard(), &msgs.StatFileReq{Id: id}, &resp); err != nil { + panic(err) + } + data.Mtime = resp.Mtime.String() + data.Size = fmt.Sprintf("%v (%v bytes)", formatSize(resp.Size), resp.Size) + } + { + req := msgs.FileSpansReq{FileId: id} + resp := msgs.FileSpansResp{} + for { + if err := client.ShardRequest(log, id.Shard(), &req, &resp); err != nil { + panic(err) + } + for _, span := range resp.Spans { + fs := fileSpan{ + Offset: formatPreciseSize(span.ByteOffset), + Size: formatPreciseSize(span.Size), + Crc32: hex.EncodeToString(span.Crc32[:]), + StorageClass: span.StorageClass.String(), + BlockSize: formatPreciseSize(span.BlockSize), + BodyBytes: hex.EncodeToString(span.BodyBytes), + DataBlocks: span.Parity.DataBlocks(), + ParityBlocks: span.Parity.ParityBlocks(), + } + for _, block := range span.BodyBlocks { + blockService := resp.BlockServices[block.BlockServiceIx] + crcStr := hex.EncodeToString(block.Crc32[:]) + fb := fileBlock{ + Id: block.BlockId.String(), + BlockService: blockService.Id.String(), + Crc32: crcStr, + Link: fmt.Sprintf("/blocks/%v/%v?size=%v&crc=%v", blockService.Id, block.BlockId, span.BlockSize, crcStr), + } + fs.BodyBlocks = append(fs.BodyBlocks, fb) + } + data.Spans = append(data.Spans, fs) + } + req.ByteOffset = resp.NextOffset + if req.ByteOffset == 0 { + break + } + } + } + return fileTemplate, &pageData{Title: title, Body: &data}, http.StatusOK } - return a.Id < b.Id }, ) - for _, shard := range state.shards { - data.ShardsAddrs = append(data.ShardsAddrs, fmt.Sprintf("%v:%v", net.IP(shard.Ip[:]), shard.Port)) - } - data.TotalUsed = formatSize(totalCapacityBytes - totalAvailableBytes) - data.TotalCapacity = formatSize(totalAvailableBytes) - data.NumFailureDomains = len(failureDomainsBytes) - if totalAvailableBytes == 0 { - data.TotalUsedPercentage = "0%" - } else { - data.TotalUsedPercentage = fmt.Sprintf("%0.2f%%", 100.0*float64(totalAvailableBytes)/float64(totalCapacityBytes)) - } - if err := template.Execute(w, &data); err != nil { - ll.RaiseAlert(fmt.Errorf("could not execute template: %w", err)) - http.Error(w, "internal error", http.StatusInternalServerError) +} + +func handleBlock(log eggs.LogLevels, st *state, w http.ResponseWriter, r *http.Request) { + handleWithRecover( + log, w, r, + func(log eggs.LogLevels, query url.Values) (io.ReadCloser, int64, int) { + segments := strings.Split(r.URL.Path, "/")[1:] + if segments[0] != "blocks" { + panic(fmt.Errorf("bad path %v", r.URL.Path)) + } + if len(segments) != 3 { + return sendPage(errorPage(http.StatusBadRequest, fmt.Sprintf("Expected /blocks//, got %v", r.URL.Path))) + } + blockServiceIdU, err := strconv.ParseUint(segments[1], 0, 64) + if err != nil { + return sendPage(errorPage(http.StatusBadRequest, fmt.Sprintf("Expected /blocks//, got %v", r.URL.Path))) + } + blockServiceId := msgs.BlockServiceId(blockServiceIdU) + blockIdU, err := strconv.ParseUint(segments[2], 0, 64) + if err != nil { + return sendPage(errorPage(http.StatusBadRequest, fmt.Sprintf("Expected /blocks//, got %v", r.URL.Path))) + } + blockId := msgs.BlockId(blockIdU) + size, err := strconv.ParseUint(query.Get("size"), 0, 32) + if err != nil { + return sendPage(errorPage(http.StatusBadRequest, fmt.Sprintf("Bad block size '%v'", query.Get("size")))) + } + crcBytes, err := hex.DecodeString(query.Get("crc")) + if err != nil || len(crcBytes) != 4 { + return sendPage(errorPage(http.StatusBadRequest, fmt.Sprintf("Bad crc '%v'", query.Get("crc")))) + } + var crc [4]byte + copy(crc[:], crcBytes) + var blockService msgs.BlockService + var conn *net.TCPConn + { + st.mutex.RLock() + blockServiceInfo, found := st.blockServices[blockServiceId] + st.mutex.RUnlock() + if !found { + return sendPage(errorPage(http.StatusNotFound, fmt.Sprintf("Unknown block service id %v", blockServiceId))) + } + blockService.Id = blockServiceInfo.Id + blockService.Ip = blockServiceInfo.Ip + blockService.Port = blockServiceInfo.Port + var err error + conn, err = eggs.BlockServiceConnection(blockServiceId, blockService.Ip[:], blockService.Port) + if err != nil { + panic(err) + } + } + if err := eggs.FetchBlock(log, conn, &blockService, blockId, crc, 0, uint32(size)); err != nil { + panic(err) + } + w.Header().Set("Content-Type", "application/x-binary") + log.Info("serving block of size %v", size) + return conn, int64(size), http.StatusOK + }, + ) +} + +func setupRouting(log eggs.LogLevels, st *state) { + errorTemplate = parseTemplates( + namedTemplate{name: "base", body: baseTemplateStr}, + namedTemplate{name: "error", body: errorTemplateStr}, + ) + + setupPage := func(path string, handle func(ll eggs.LogLevels, state *state, w http.ResponseWriter, r *http.Request)) { + http.HandleFunc( + path, + func(w http.ResponseWriter, r *http.Request) { handle(log, st, w, r) }, + ) } + + // Static assets + http.HandleFunc( + "/bootstrap.min.css", + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/css; charset=utf-8") + w.Write(bootstrapCssStr) + }, + ) + http.HandleFunc( + "/shuckle-face.png", + func(w http.ResponseWriter, r *http.Request) { w.Write(shuckleFacePngStr) }, + ) + http.HandleFunc( + "/shuckle.png", + func(w http.ResponseWriter, r *http.Request) { w.Write(shucklePngStr) }, + ) + + // blocks serving + http.HandleFunc( + "/blocks/", + func(w http.ResponseWriter, r *http.Request) { handleBlock(log, st, w, r) }, + ) + + // pages + shuckleTemplate = parseTemplates( + namedTemplate{name: "base", body: baseTemplateStr}, + namedTemplate{name: "shuckle", body: indexTemplateStr}, + ) + setupPage("/shuckle", handleIndex) + + fileTemplate = parseTemplates( + namedTemplate{name: "base", body: baseTemplateStr}, + namedTemplate{name: "file", body: fileTemplateStr}, + ) + directoryTemplate = parseTemplates( + namedTemplate{name: "base", body: baseTemplateStr}, + namedTemplate{name: "directory", body: directoryTemplateStr}, + ) + setupPage("/inode", handleInode) } func main() { @@ -364,11 +793,6 @@ func main() { flag.Parse() noRunawayArgs() - indexTemplate, err := template.New("index").Option("missingkey=error").Parse(indexTemplateStr) - if err != nil { - panic(err) - } - logOut := os.Stdout if *logFile != "" { var err error @@ -383,9 +807,6 @@ func main() { Logger: eggs.NewLogger(logOut), } - shucklePngBase64 = base64.StdEncoding.EncodeToString(shucklePng) - shuckleFacePngBase64 = base64.StdEncoding.EncodeToString(shuckleFacePng) - bincodeListener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%v", *bincodePort)) if err != nil { panic(err) @@ -400,12 +821,9 @@ func main() { ll.Info("running on %v (HTTP) and %v (bincode)", httpListener.Addr(), bincodeListener.Addr()) - blockServices := newBlockServices() + state := newState() - http.HandleFunc( - "/", - func(w http.ResponseWriter, r *http.Request) { handleIndex(ll, indexTemplate, blockServices, w, r) }, - ) + setupRouting(ll, state) terminateChan := make(chan error) @@ -416,7 +834,7 @@ func main() { terminateChan <- err return } - go func() { handleRequest(ll, blockServices, conn.(*net.TCPConn)) }() + go func() { handleRequest(ll, state, conn.(*net.TCPConn)) }() } }() diff --git a/go/eggsshuckle/error.html b/go/eggsshuckle/error.html new file mode 100644 index 00000000..54eef858 --- /dev/null +++ b/go/eggsshuckle/error.html @@ -0,0 +1,3 @@ +{{define "body"}} +
{{.}}
+{{end}} \ No newline at end of file diff --git a/go/eggsshuckle/file.html b/go/eggsshuckle/file.html new file mode 100644 index 00000000..9a76fc44 --- /dev/null +++ b/go/eggsshuckle/file.html @@ -0,0 +1,95 @@ +{{define "body"}} + + + {{if .Path}} + + + + + {{end}} + + + + + + + + + + + + + +
Path{{.Path}}
Id{{.Id}}
Size{{.Size}}
Mtime{{.Mtime}}
+

Spans

+ + + + + + + + + + + + + {{range $span := .Spans}} + + + + + + + + + {{end}} + +
OffsetSizeCrc32StorageBlockSizeBody
{{$span.Offset}}{{$span.Size}}{{$span.Crc32}}{{$span.StorageClass}} + {{if $span.BlockSize}} + {{$span.BlockSize}} + {{end}} + + + {{if $span.BodyBytes}} + {{$span.BodyBytes}} + {{else}} + + + + + + + + + + + + {{range $block := $span.BodyBlocks}} + + {{end}} + + + + {{range $block := $span.BodyBlocks}} + + {{end}} + + + + {{range $block := $span.BodyBlocks}} + + {{end}} + + +
+ data + + parity +
Id + {{$block.Id}} +
Crc32{{$block.Crc32}}
BlockService{{$block.BlockService}}
+ {{end}} +
+
+{{end}} \ No newline at end of file diff --git a/go/eggsshuckle/index.html b/go/eggsshuckle/index.html new file mode 100644 index 00000000..bc48ac10 --- /dev/null +++ b/go/eggsshuckle/index.html @@ -0,0 +1,65 @@ +{{define "body"}} + Mr. Shuckle +

Overview

+
    +
  • {{.NumBlockServices}} block services on {{.NumFailureDomains}} failure domains
  • +
  • Total capacity: {{.TotalCapacity}}
  • +
  • {{.TotalUsed}} used over {{.Blocks}} blocks ({{.TotalUsedPercentage}})
  • +
+

CDC

+ + + + + + + + + + +
Address
{{.CDCAddr}}
+

Shards

+ + + + + + + + {{ range $id, $addr := .ShardsAddrs }} + + + + + {{end}} + +
IdAddress
{{printf "%03v" $id}}{{$addr}}
+

Block services

+ + + + + + + + + + + + + + {{ range .BlockServices }} + + + + + + + + + + + {{end}} + +
FailureDomainAddressPathIdStorageClassBlocksCapacityAvailable
{{.FailureDomain}}{{.Addr}}{{.Path}}{{.Id}}{{.StorageClass}}{{.Blocks}}{{.CapacityBytes}}{{.AvailableBytes}}
+{{end}} diff --git a/go/eggsshuckle/shuckle.png b/go/eggsshuckle/shuckle.png new file mode 100644 index 0000000000000000000000000000000000000000..86b3d86001a04ea24e497bdf0138c5f47c0b723e GIT binary patch literal 40121 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 Bx(xsT literal 0 HcmV?d00001 diff --git a/go/eggsshuckle/shuckleface.png b/go/eggsshuckle/shuckleface.png new file mode 100644 index 0000000000000000000000000000000000000000..1d340858a19331855fbd428f832d654554faf1f5 GIT binary patch literal 18581 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 Ksagrs(EkOF6Ye?y literal 0 HcmV?d00001 diff --git a/go/integrationtest/req.go b/go/integrationtest/req.go index f303dc32..d94d7f2e 100644 --- a/go/integrationtest/req.go +++ b/go/integrationtest/req.go @@ -193,7 +193,7 @@ func readFile(log eggs.LogLevels, client *eggs.Client, mbs eggs.MockableBlockSer if err != nil { panic(err) } - if err := mbs.FetchBlock(log, conn, blockService, block, 0, uint32(span.BlockSize)); err != nil { + if err := mbs.FetchBlock(log, conn, blockService, block.BlockId, block.Crc32, 0, uint32(span.BlockSize)); err != nil { conn.Close() panic(err) } diff --git a/go/msgs/msgs.go b/go/msgs/msgs.go index 0993161b..101a343b 100644 --- a/go/msgs/msgs.go +++ b/go/msgs/msgs.go @@ -95,11 +95,11 @@ func (id InodeId) String() string { } func (id BlockServiceId) String() string { - return fmt.Sprintf("0x%016X", uint64(id)) + return fmt.Sprintf("0x%016x", uint64(id)) } func (id BlockId) String() string { - return fmt.Sprintf("0x%016X", uint64(id)) + return fmt.Sprintf("0x%016x", uint64(id)) } func MakeInodeId(typ InodeType, shard ShardId, id uint64) InodeId {