bp/bp.txt

7469 lines
482 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// app/Language/en/Validation.php
<?php
return [];
// app/Language/.gitkeep
// app/Libraries/Twig/TwigJsonDecodeExtension.php
<?php
namespace App\Libraries\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class TwigJsonDecodeExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('json_decode', [$this, 'jsonDecode']),
];
}
public function jsonDecode(string $json, bool $assoc = true, int $depth = 512, int $flags = 0)
{
return json_decode($json, $assoc, $depth, $flags);
}
}
// app/Libraries/Twig/TwigGlobalsExtension.php
<?php
namespace App\Libraries\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Twig\TwigGlobal;
use App\Models\OrganizationModel;
use Config\Services;
class TwigGlobalsExtension extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction('get_session', [$this, 'getSession'], ['is_safe' => ['html']]),
new TwigFunction('get_current_org', [$this, 'getCurrentOrg'], ['is_safe' => ['html']]),
new TwigFunction('get_alerts', [$this, 'getAlerts'], ['is_safe' => ['html']]),
new TwigFunction('render_pager', [$this, 'renderPager'], ['is_safe' => ['html']]),
];
}
public function getSession()
{
return session();
}
public function getCurrentOrg()
{
$session = session();
$activeOrgId = $session->get('active_org_id');
if ($activeOrgId) {
$orgModel = new OrganizationModel();
return $orgModel->find($activeOrgId);
}
return null;
}
public function getAlerts(): array
{
$session = session();
$alerts = [];
$types = ['success', 'error', 'warning', 'info'];
foreach ($types as $type) {
if ($msg = $session->getFlashdata($type)) {
$alerts[] = ['type' => $type, 'message' => $msg];
}
}
if ($validationErrors = $session->getFlashdata('errors')) {
foreach ($validationErrors as $error) {
$alerts[] = ['type' => 'error', 'message' => $error];
}
}
return $alerts;
}
public function renderPager($pager)
{
if (!$pager) {
return '';
}
return $pager->links();
}
}
// app/Libraries/EmailLibrary.php
<?php
namespace App\Libraries;
use CodeIgniter\Email\Email;
use Config\Services;
class EmailLibrary
{
/**
public function sendVerificationEmail(string $email, string $name, string $token): bool
{
$emailConfig = config('Email');
$verificationUrl = base_url('/auth/verify/' . $token);
$twig = Services::twig();
$htmlBody = $twig->render('emails/verification', [
'name' => $name,
'verification_url' => $verificationUrl,
'app_name' => $emailConfig->fromName ?? 'Бизнес.Точка',
]);
$emailer = Services::email($emailConfig);
$emailer->setTo($email);
$emailer->setFrom($emailConfig->fromEmail, $emailConfig->fromName);
$emailer->setSubject('Подтверждение регистрации');
$emailer->setMessage($htmlBody);
try {
return $emailer->send();
} catch (\Exception $e) {
log_message('error', 'Ошибка отправки email: ' . $e->getMessage());
return false;
}
}
/**
public function sendWelcomeEmail(string $email, string $name): bool
{
$emailConfig = config('Email');
$twig = Services::twig();
$htmlBody = $twig->render('emails/welcome', [
'name' => $name,
'app_name' => $emailConfig->fromName ?? 'Бизнес.Точка',
]);
$emailer = Services::email($emailConfig);
$emailer->setTo($email);
$emailer->setFrom($emailConfig->fromEmail, $emailConfig->fromName);
$emailer->setSubject('Добро пожаловать!');
$emailer->setMessage($htmlBody);
try {
return $emailer->send();
} catch (\Exception $e) {
log_message('error', 'Ошибка отправки email: ' . $e->getMessage());
return false;
}
}
}
// app/Libraries/.gitkeep
// app/Helpers/.gitkeep
// app/index.html
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
// app/Common.php
<?php
/**
// app/Views/layouts/base.twig
{# app/Views/layouts/base.html.twig #}
{% set session_data = get_session() %}
{% set current_org = get_current_org() %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Бизнес.Точка{% endblock %}</title>
<!-- Bootstrap CSS -->
<link href="{{ base_url('assets/css/bootstrap.min.css') }}" rel="stylesheet">
<!-- FontAwesome -->
<link href="{{ base_url('assets/css/all.min.css') }}" rel="stylesheet">
{% block styles %}{% endblock %}
</head>
<body class="bg-light">
<div class="d-flex" id="wrapper">
<!-- SIDEBAR -->
<div class="bg-white border-end" id="sidebar-wrapper" style="width: 250px;">
<div class="sidebar-heading bg-primary text-white p-3 d-flex justify-content-between align-items-center">
<span><i class="fa-solid fa-circle-nodes me-2"></i>Бизнес.Точка</span>
</div>
<div class="list-group list-group-flush mt-2">
<a href="{{ base_url('/') }}" class="list-group-item list-group-item-action list-group-item-light py-3">
<i class="fa-solid fa-gauge-high me-2"></i> Главная
</a>
<!-- Ссылки на модули (пока неактивны) -->
<!-- Модуль Клиенты (базовый, доступен всем) -->
<a href="{{ base_url('/clients') }}" class="list-group-item list-group-item-action list-group-item-light py-3">
<i class="fa-solid fa-users me-2"></i> Клиенты
</a>
<a href="#" class="list-group-item list-group-item-action list-group-item-light py-3 disabled">
<i class="fa-solid fa-users me-2"></i> CRM
</a>
<a href="#" class="list-group-item list-group-item-action list-group-item-light py-3 disabled">
<i class="fa-solid fa-calendar me-2"></i> Booking
</a>
<a href="#" class="list-group-item list-group-item-action list-group-item-light py-3 disabled">
<i class="fa-solid fa-file-contract me-2"></i> Proof
</a>
<a href="#" class="list-group-item list-group-item-action list-group-item-light py-3 disabled">
<i class="fa-solid fa-check-square me-2"></i> Tasks
</a>
</div>
</div>
<!-- /SIDEBAR -->
<!-- Page Content -->
<div id="page-content-wrapper" class="w-100">
<!-- TOPBAR -->
<nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom px-4 shadow-sm">
<button class="btn btn-light border-0" id="sidebarToggle"><i class="fa-solid fa-bars"></i></button>
<div class="ms-auto d-flex align-items-center">
<!-- ДРОПДАУН ОРГАНИЗАЦИИ -->
<div class="dropdown me-4">
{% if current_org %}
<button class="btn btn-light dropdown-toggle border d-flex align-items-center" type="button" data-bs-toggle="dropdown">
<i class="fa-solid fa-building text-primary me-2"></i>
<span class="fw-bold text-dark">{{ current_org.name }}</span>
<span class="badge bg-light text-secondary ms-2 text-uppercase fs-6">{{ current_org.type == 'personal' ? 'Личное' : 'Бизнес' }}</span>
</button>
{% else %}
<a href="{{ base_url('/organizations') }}" class="btn btn-outline-danger">
<i class="fa-solid fa-exclamation-triangle me-1"></i> Выберите организацию
</a>
{% endif %}
{% if current_org %}
<ul class="dropdown-menu dropdown-menu-end shadow-sm">
{# <li>
<span class="dropdown-header text-muted small">
<i class="fa-solid fa-hashtag me-1"></i> {{ current_org.id }}
</span>
</li>
<li><hr class="dropdown-divider"></li> #}
<!-- Ссылка: Список всех организаций -->
<li>
<a class="dropdown-item" href="{{ base_url('/organizations') }}">
<i class="fa-solid fa-layer-group text-primary me-2"></i> Список организаций
</a>
</li>
<!-- Ссылка: Создать новую организацию -->
<li>
<a class="dropdown-item" href="{{ base_url('/organizations/create') }}">
<i class="fa-solid fa-plus-circle text-success me-2"></i> Создать организацию
</a>
</li>
<li><hr class="dropdown-divider"></li>
<!-- Ссылка: Настройки этой организации (будущий функционал) -->
<li>
<a class="dropdown-item text-muted" href="#">
<i class="fa-solid fa-gear me-2"></i> Настройки текущей
</a>
</li>
</ul>
{% endif %}
</div>
<!-- /ДРОПДАУН ОРГАНИЗАЦИИ -->
<!-- ПРОФИЛЬ ПОЛЬЗОВАТЕЛЯ -->
<div class="dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
<div class="bg-primary text-white rounded-circle d-flex justify-content-center align-items-center me-2" style="width:32px; height:32px;">
{{ session_data.name|first|upper }}
</div>
<span class="d-none d-md-inline text-dark">{{ session_data.name }}</span>
</a>
<ul class="dropdown-menu dropdown-menu-end shadow-sm">
<li><h6 class="dropdown-header">{{ session_data.email }}</h6></li>
<li><a class="dropdown-item" href="#"><i class="fa-regular fa-user me-2"></i> Профиль</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="{{ base_url('/logout') }}"><i class="fa-solid fa-arrow-right-from-bracket me-2"></i> Выйти</a></li>
</ul>
</div>
<!-- /ПРОФИЛЬ ПОЛЬЗОВАТЕЛЯ -->
</div>
</nav>
<!-- /TOPBAR -->
<!-- CONTENT -->
<div class="container-fluid p-4">
<!-- Подключаем компонент алертов (Toasts) -->
{% include 'components/alerts.twig' %}
{% block content %}{% endblock %}
</div>
<!-- /CONTENT -->
</div>
</div>
<!-- Scripts -->
<script src="{{ base_url('assets/js/bootstrap.bundle.min.js') }}"></script>
<script>
// Скрипт тоггла сайдбара
document.addEventListener("DOMContentLoaded", function () {
const sidebarToggle = document.body.querySelector('#sidebarToggle');
if (sidebarToggle) {
sidebarToggle.addEventListener('click', event => {
event.preventDefault();
document.body.classList.toggle('sb-sidenav-toggled');
});
}
});
</script>
{% block scripts %}{% endblock %}
</body>
</html>
// app/Views/layouts/public.twig
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Вход - Бизнес.Точка</title>
<link href="{{ base_url('assets/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ base_url('assets/css/all.min.css') }}" rel="stylesheet">
</head>
<body class="bg-light d-flex align-items-center justify-content-center" style="height: 100vh;">
{% include 'components/alerts.twig' %}
{% block content %}{% endblock %}
<script src="{{ base_url('assets/js/bootstrap.bundle.min.js') }}"></script>
{% block scripts %}{% endblock %}
</body>
</html>
// app/Views/pager/bootstrap_full.php
<?php
use CodeIgniter\Pager\PagerRenderer;
/**
$pager->setSurroundCount(2);
?>
<nav aria-label="<?= lang('Pager.pageNavigation') ?>">
<ul class="pagination justify-content-center">
<?php if ($pager->hasPrevious()) : ?>
<li class="page-item">
<a href="<?= $pager->getPrevious() ?>" class="page-link" aria-label="<?= lang('Pager.previous') ?>">
<span aria-hidden="true"><?= lang('Pager.previous') ?></span>
</a>
</li>
<?php endif ?>
<?php foreach ($pager->links() as $link) : ?>
<li class="page-item <?= $link['active'] ? 'active' : '' ?>">
<a href="<?= $link['uri'] ?>" class="page-link"><?= $link['title'] ?></a>
</li>
<?php endforeach ?>
<?php if ($pager->hasNext()) : ?>
<li class="page-item">
<a href="<?= $pager->getNext() ?>" class="page-link" aria-label="<?= lang('Pager.next') ?>">
<span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a>
</li>
<?php endif ?>
</ul>
</nav>
// app/Views/errors/cli/production.php
<?php
include __DIR__ . '/error_exception.php';
// app/Views/errors/cli/error_404.php
<?php
use CodeIgniter\CLI\CLI;
CLI::error('ERROR: ' . $code);
CLI::write($message);
CLI::newLine();
// app/Views/errors/cli/error_exception.php
<?php
use CodeIgniter\CLI\CLI;
CLI::write('[' . $exception::class . ']', 'light_gray', 'red');
CLI::write($message);
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green'));
CLI::newLine();
$last = $exception;
while ($prevException = $last->getPrevious()) {
$last = $prevException;
CLI::write(' Caused by:');
CLI::write(' [' . $prevException::class . ']', 'red');
CLI::write(' ' . $prevException->getMessage());
CLI::write(' at ' . CLI::color(clean_path($prevException->getFile()) . ':' . $prevException->getLine(), 'green'));
CLI::newLine();
}
if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
$backtraces = $last->getTrace();
if ($backtraces) {
CLI::write('Backtrace:', 'green');
}
foreach ($backtraces as $i => $error) {
$padFile = ' ';
$padClass = ' ';
$c = str_pad($i + 1, 3, ' ', STR_PAD_LEFT);
if (isset($error['file'])) {
$filepath = clean_path($error['file']) . ':' . $error['line'];
CLI::write($c . $padFile . CLI::color($filepath, 'yellow'));
} else {
CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow'));
}
$function = '';
if (isset($error['class'])) {
$type = ($error['type'] === '->') ? '()' . $error['type'] : $error['type'];
$function .= $padClass . $error['class'] . $type . $error['function'];
} elseif (! isset($error['class']) && isset($error['function'])) {
$function .= $padClass . $error['function'];
}
$args = implode(', ', array_map(static fn ($value): string => match (true) {
is_object($value) => 'Object(' . $value::class . ')',
is_array($value) => $value !== [] ? '[...]' : '[]',
$value === null => 'null',
default => var_export($value, true),
}, array_values($error['args'] ?? [])));
$function .= '(' . $args . ')';
CLI::write($function);
CLI::newLine();
}
}
// app/Views/errors/html/debug.css
:root {
--main-bg-color: #fff;
--main-text-color: #555;
--dark-text-color: #222;
--light-text-color: #c7c7c7;
--brand-primary-color: #DC4814;
--light-bg-color: #ededee;
--dark-bg-color: #404040;
}
body {
height: 100%;
background: var(--main-bg-color);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
color: var(--main-text-color);
font-weight: 300;
margin: 0;
padding: 0;
}
h1 {
font-weight: lighter;
font-size: 3rem;
color: var(--dark-text-color);
margin: 0;
}
h1.headline {
margin-top: 20%;
font-size: 5rem;
}
.text-center {
text-align: center;
}
p.lead {
font-size: 1.6rem;
}
.container {
max-width: 75rem;
margin: 0 auto;
padding: 1rem;
}
.header {
background: var(--light-bg-color);
color: var(--dark-text-color);
margin-top: 2.17rem;
}
.header .container {
padding: 1rem;
}
.header h1 {
font-size: 2.5rem;
font-weight: 500;
}
.header p {
font-size: 1.2rem;
margin: 0;
line-height: 2.5;
}
.header a {
color: var(--brand-primary-color);
margin-left: 2rem;
display: none;
text-decoration: none;
}
.header:hover a {
display: inline;
}
.environment {
background: var(--brand-primary-color);
color: var(--main-bg-color);
text-align: center;
padding: calc(4px + 0.2083vw);
width: 100%;
top: 0;
position: fixed;
}
.source {
background: #343434;
color: var(--light-text-color);
padding: 0.5em 1em;
border-radius: 5px;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 0.85rem;
margin: 0;
overflow-x: scroll;
}
.source span.line {
line-height: 1.4;
}
.source span.line .number {
color: #666;
}
.source .line .highlight {
display: block;
background: var(--dark-text-color);
color: var(--light-text-color);
}
.source span.highlight .number {
color: #fff;
}
.tabs {
list-style: none;
list-style-position: inside;
margin: 0;
padding: 0;
margin-bottom: -1px;
}
.tabs li {
display: inline;
}
.tabs a:link,
.tabs a:visited {
padding: 0 1rem;
line-height: 2.7;
text-decoration: none;
color: var(--dark-text-color);
background: var(--light-bg-color);
border: 1px solid rgba(0,0,0,0.15);
border-bottom: 0;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
display: inline-block;
}
.tabs a:hover {
background: var(--light-bg-color);
border-color: rgba(0,0,0,0.15);
}
.tabs a.active {
background: var(--main-bg-color);
color: var(--main-text-color);
}
.tab-content {
background: var(--main-bg-color);
border: 1px solid rgba(0,0,0,0.15);
}
.content {
padding: 1rem;
}
.hide {
display: none;
}
.alert {
margin-top: 2rem;
display: block;
text-align: center;
line-height: 3.0;
background: #d9edf7;
border: 1px solid #bcdff1;
border-radius: 5px;
color: #31708f;
}
table {
width: 100%;
overflow: hidden;
}
th {
text-align: left;
border-bottom: 1px solid #e7e7e7;
padding-bottom: 0.5rem;
}
td {
padding: 0.2rem 0.5rem 0.2rem 0;
}
tr:hover td {
background: #f1f1f1;
}
td pre {
white-space: pre-wrap;
}
.trace a {
color: inherit;
}
.trace table {
width: auto;
}
.trace tr td:first-child {
min-width: 5em;
font-weight: bold;
}
.trace td {
background: var(--light-bg-color);
padding: 0 1rem;
}
.trace td pre {
margin: 0;
}
.args {
display: none;
}
// app/Views/errors/html/production.php
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex">
<title><?= lang('Errors.whoops') ?></title>
<style>
<?= preg_replace('#[\r\n\t ]+#', ' ', file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css')) ?>
</style>
</head>
<body>
<div class="container text-center">
<h1 class="headline"><?= lang('Errors.whoops') ?></h1>
<p class="lead"><?= lang('Errors.weHitASnag') ?></p>
</div>
</body>
</html>
// app/Views/errors/html/error_400.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?= lang('Errors.badRequest') ?></title>
<style>
div.logo {
height: 200px;
width: 155px;
display: inline-block;
opacity: 0.08;
position: absolute;
top: 2rem;
left: 50%;
margin-left: -73px;
}
body {
height: 100%;
background: #fafafa;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #777;
font-weight: 300;
}
h1 {
font-weight: lighter;
letter-spacing: normal;
font-size: 3rem;
margin-top: 0;
margin-bottom: 0;
color: #222;
}
.wrap {
max-width: 1024px;
margin: 5rem auto;
padding: 2rem;
background: #fff;
text-align: center;
border: 1px solid #efefef;
border-radius: 0.5rem;
position: relative;
}
pre {
white-space: normal;
margin-top: 1.5rem;
}
code {
background: #fafafa;
border: 1px solid #efefef;
padding: 0.5rem 1rem;
border-radius: 5px;
display: block;
}
p {
margin-top: 1.5rem;
}
.footer {
margin-top: 2rem;
border-top: 1px solid #efefef;
padding: 1em 2em 0 2em;
font-size: 85%;
color: #999;
}
a:active,
a:link,
a:visited {
color: #dd4814;
}
</style>
</head>
<body>
<div class="wrap">
<h1>400</h1>
<p>
<?php if (ENVIRONMENT !== 'production') : ?>
<?= nl2br(esc($message)) ?>
<?php else : ?>
<?= lang('Errors.sorryBadRequest') ?>
<?php endif; ?>
</p>
</div>
</body>
</html>
// app/Views/errors/html/debug.js
var tabLinks = new Array();
var contentDivs = new Array();
function init()
{
var tabListItems = document.getElementById('tabs').childNodes;
console.log(tabListItems);
for (var i = 0; i < tabListItems.length; i ++)
{
if (tabListItems[i].nodeName == "LI")
{
var tabLink = getFirstChildWithTagName(tabListItems[i], 'A');
var id = getHash(tabLink.getAttribute('href'));
tabLinks[id] = tabLink;
contentDivs[id] = document.getElementById(id);
}
}
var i = 0;
for (var id in tabLinks)
{
tabLinks[id].onclick = showTab;
tabLinks[id].onfocus = function () {
this.blur()
};
if (i == 0)
{
tabLinks[id].className = 'active';
}
i ++;
}
var i = 0;
for (var id in contentDivs)
{
if (i != 0)
{
console.log(contentDivs[id]);
contentDivs[id].className = 'content hide';
}
i ++;
}
}
function showTab()
{
var selectedId = getHash(this.getAttribute('href'));
for (var id in contentDivs)
{
if (id == selectedId)
{
tabLinks[id].className = 'active';
contentDivs[id].className = 'content';
}
else
{
tabLinks[id].className = '';
contentDivs[id].className = 'content hide';
}
}
return false;
}
function getFirstChildWithTagName(element, tagName)
{
for (var i = 0; i < element.childNodes.length; i ++)
{
if (element.childNodes[i].nodeName == tagName)
{
return element.childNodes[i];
}
}
}
function getHash(url)
{
var hashPos = url.lastIndexOf('#');
return url.substring(hashPos + 1);
}
function toggle(elem)
{
elem = document.getElementById(elem);
if (elem.style && elem.style['display'])
{
var disp = elem.style['display'];
}
else if (elem.currentStyle)
{
var disp = elem.currentStyle['display'];
}
else if (window.getComputedStyle)
{
var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display');
}
elem.style.display = disp == 'block' ? 'none' : 'block';
return false;
}
// app/Views/errors/html/error_404.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?= lang('Errors.pageNotFound') ?></title>
<style>
div.logo {
height: 200px;
width: 155px;
display: inline-block;
opacity: 0.08;
position: absolute;
top: 2rem;
left: 50%;
margin-left: -73px;
}
body {
height: 100%;
background: #fafafa;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #777;
font-weight: 300;
}
h1 {
font-weight: lighter;
letter-spacing: normal;
font-size: 3rem;
margin-top: 0;
margin-bottom: 0;
color: #222;
}
.wrap {
max-width: 1024px;
margin: 5rem auto;
padding: 2rem;
background: #fff;
text-align: center;
border: 1px solid #efefef;
border-radius: 0.5rem;
position: relative;
}
pre {
white-space: normal;
margin-top: 1.5rem;
}
code {
background: #fafafa;
border: 1px solid #efefef;
padding: 0.5rem 1rem;
border-radius: 5px;
display: block;
}
p {
margin-top: 1.5rem;
}
.footer {
margin-top: 2rem;
border-top: 1px solid #efefef;
padding: 1em 2em 0 2em;
font-size: 85%;
color: #999;
}
a:active,
a:link,
a:visited {
color: #dd4814;
}
</style>
</head>
<body>
<div class="wrap">
<h1>404</h1>
<p>
<?php if (ENVIRONMENT !== 'production') : ?>
<?= nl2br(esc($message)) ?>
<?php else : ?>
<?= lang('Errors.sorryCannotFind') ?>
<?php endif; ?>
</p>
</div>
</body>
</html>
// app/Views/errors/html/error_exception.php
<?php
use CodeIgniter\HTTP\Header;
use CodeIgniter\CodeIgniter;
$errorId = uniqid('error', true);
?>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex">
<title><?= esc($title) ?></title>
<style>
<?= preg_replace('#[\r\n\t ]+#', ' ', file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css')) ?>
</style>
<script>
<?= file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.js') ?>
</script>
</head>
<body onload="init()">
<!-- Header -->
<div class="header">
<div class="environment">
Displayed at <?= esc(date('H:i:s')) ?> &mdash;
PHP: <?= esc(PHP_VERSION) ?> &mdash;
CodeIgniter: <?= esc(CodeIgniter::CI_VERSION) ?> --
Environment: <?= ENVIRONMENT ?>
</div>
<div class="container">
<h1><?= esc($title), esc($exception->getCode() ? ' #' . $exception->getCode() : '') ?></h1>
<p>
<?= nl2br(esc($exception->getMessage())) ?>
<a href="https:
rel="noreferrer" target="_blank">search &rarr;</a>
</p>
</div>
</div>
<!-- Source -->
<div class="container">
<p><b><?= esc(clean_path($file)) ?></b> at line <b><?= esc($line) ?></b></p>
<?php if (is_file($file)) : ?>
<div class="source">
<?= static::highlightFile($file, $line, 15); ?>
</div>
<?php endif; ?>
</div>
<div class="container">
<?php
$last = $exception;
while ($prevException = $last->getPrevious()) {
$last = $prevException;
?>
<pre>
Caused by:
<?= esc($prevException::class), esc($prevException->getCode() ? ' #' . $prevException->getCode() : '') ?>
<?= nl2br(esc($prevException->getMessage())) ?>
<a href="https:
rel="noreferrer" target="_blank">search &rarr;</a>
<?= esc(clean_path($prevException->getFile()) . ':' . $prevException->getLine()) ?>
</pre>
<?php
}
?>
</div>
<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) : ?>
<div class="container">
<ul class="tabs" id="tabs">
<li><a href="#backtrace">Backtrace</a></li>
<li><a href="#server">Server</a></li>
<li><a href="#request">Request</a></li>
<li><a href="#response">Response</a></li>
<li><a href="#files">Files</a></li>
<li><a href="#memory">Memory</a></li>
</ul>
<div class="tab-content">
<!-- Backtrace -->
<div class="content" id="backtrace">
<ol class="trace">
<?php foreach ($trace as $index => $row) : ?>
<li>
<p>
<!-- Trace info -->
<?php if (isset($row['file']) && is_file($row['file'])) : ?>
<?php
if (isset($row['function']) && in_array($row['function'], ['include', 'include_once', 'require', 'require_once'], true)) {
echo esc($row['function'] . ' ' . clean_path($row['file']));
} else {
echo esc(clean_path($row['file']) . ' : ' . $row['line']);
}
?>
<?php else: ?>
{PHP internal code}
<?php endif; ?>
<!-- Class/Method -->
<?php if (isset($row['class'])) : ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;<?= esc($row['class'] . $row['type'] . $row['function']) ?>
<?php if (! empty($row['args'])) : ?>
<?php $argsId = $errorId . 'args' . $index ?>
( <a href="#" onclick="return toggle('<?= esc($argsId, 'attr') ?>');">arguments</a> )
<div class="args" id="<?= esc($argsId, 'attr') ?>">
<table cellspacing="0">
<?php
$params = null;
if (! str_ends_with($row['function'], '}')) {
$mirror = isset($row['class']) ? new ReflectionMethod($row['class'], $row['function']) : new ReflectionFunction($row['function']);
$params = $mirror->getParameters();
}
foreach ($row['args'] as $key => $value) : ?>
<tr>
<td><code><?= esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?></code></td>
<td><pre><?= esc(print_r($value, true)) ?></pre></td>
</tr>
<?php endforeach ?>
</table>
</div>
<?php else : ?>
()
<?php endif; ?>
<?php endif; ?>
<?php if (! isset($row['class']) && isset($row['function'])) : ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp; <?= esc($row['function']) ?>()
<?php endif; ?>
</p>
<!-- Source? -->
<?php if (isset($row['file']) && is_file($row['file']) && isset($row['class'])) : ?>
<div class="source">
<?= static::highlightFile($row['file'], $row['line']) ?>
</div>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ol>
</div>
<!-- Server -->
<div class="content" id="server">
<?php foreach (['_SERVER', '_SESSION'] as $var) : ?>
<?php
if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<h3>$<?= esc($var) ?></h3>
<table>
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($GLOBALS[$var] as $key => $value) : ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)) : ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endforeach ?>
<!-- Constants -->
<?php $constants = get_defined_constants(true); ?>
<?php if (! empty($constants['user'])) : ?>
<h3>Constants</h3>
<table>
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($constants['user'] as $key => $value) : ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)) : ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<!-- Request -->
<div class="content" id="request">
<?php $request = service('request'); ?>
<table>
<tbody>
<tr>
<td style="width: 10em">Path</td>
<td><?= esc($request->getUri()) ?></td>
</tr>
<tr>
<td>HTTP Method</td>
<td><?= esc($request->getMethod()) ?></td>
</tr>
<tr>
<td>IP Address</td>
<td><?= esc($request->getIPAddress()) ?></td>
</tr>
<tr>
<td style="width: 10em">Is AJAX Request?</td>
<td><?= $request->isAJAX() ? 'yes' : 'no' ?></td>
</tr>
<tr>
<td>Is CLI Request?</td>
<td><?= $request->isCLI() ? 'yes' : 'no' ?></td>
</tr>
<tr>
<td>Is Secure Request?</td>
<td><?= $request->isSecure() ? 'yes' : 'no' ?></td>
</tr>
<tr>
<td>User Agent</td>
<td><?= esc($request->getUserAgent()->getAgentString()) ?></td>
</tr>
</tbody>
</table>
<?php $empty = true; ?>
<?php foreach (['_GET', '_POST', '_COOKIE'] as $var) : ?>
<?php
if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<?php $empty = false; ?>
<h3>$<?= esc($var) ?></h3>
<table style="width: 100%">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($GLOBALS[$var] as $key => $value) : ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)) : ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endforeach ?>
<?php if ($empty) : ?>
<div class="alert">
No $_GET, $_POST, or $_COOKIE Information to show.
</div>
<?php endif; ?>
<?php $headers = $request->headers(); ?>
<?php if (! empty($headers)) : ?>
<h3>Headers</h3>
<table>
<thead>
<tr>
<th>Header</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($headers as $name => $value) : ?>
<tr>
<td><?= esc($name, 'html') ?></td>
<td>
<?php
if ($value instanceof Header) {
echo esc($value->getValueLine(), 'html');
} else {
foreach ($value as $i => $header) {
echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
}
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<!-- Response -->
<?php
$response = service('response');
$response->setStatusCode(http_response_code());
?>
<div class="content" id="response">
<table>
<tr>
<td style="width: 15em">Response Status</td>
<td><?= esc($response->getStatusCode() . ' - ' . $response->getReasonPhrase()) ?></td>
</tr>
</table>
<?php $headers = $response->headers(); ?>
<?php if (! empty($headers)) : ?>
<h3>Headers</h3>
<table>
<thead>
<tr>
<th>Header</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($headers as $name => $value) : ?>
<tr>
<td><?= esc($name, 'html') ?></td>
<td>
<?php
if ($value instanceof Header) {
echo esc($response->getHeaderLine($name), 'html');
} else {
foreach ($value as $i => $header) {
echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
}
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<!-- Files -->
<div class="content" id="files">
<?php $files = get_included_files(); ?>
<ol>
<?php foreach ($files as $file) :?>
<li><?= esc(clean_path($file)) ?></li>
<?php endforeach ?>
</ol>
</div>
<!-- Memory -->
<div class="content" id="memory">
<table>
<tbody>
<tr>
<td>Memory Usage</td>
<td><?= esc(static::describeMemory(memory_get_usage(true))) ?></td>
</tr>
<tr>
<td style="width: 12em">Peak Memory Usage:</td>
<td><?= esc(static::describeMemory(memory_get_peak_usage(true))) ?></td>
</tr>
<tr>
<td>Memory Limit:</td>
<td><?= esc(ini_get('memory_limit')) ?></td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /tab-content -->
</div> <!-- /container -->
<?php endif; ?>
</body>
</html>
// app/Views/emails/verification.twig
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Подтверждение регистрации</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 0;
background-color: #f5f5f5;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.logo {
text-align: center;
margin-bottom: 20px;
font-size: 24px;
font-weight: bold;
color: #0d6efd;
}
.btn {
display: inline-block;
padding: 12px 24px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: bold;
text-align: center;
}
.btn:hover {
background-color: #0b5ed7;
}
.footer {
text-align: center;
margin-top: 20px;
font-size: 12px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="logo">{{ app_name }}</div>
<h2>Добрый день, {{ name }}!</h2>
<p>Спасибо за регистрацию в {{ app_name }}.</p>
<p>Для завершения регистрации и подтверждения вашего email адреса, пожалуйста, нажмите на кнопку ниже:</p>
<p style="text-align: center; margin: 30px 0;">
<a href="{{ verification_url }}" class="btn">Подтвердить email</a>
</p>
<p>Если кнопка не работает, вы можете скопировать ссылку и вставить её в адресную строку браузера:</p>
<p style="word-break: break-all; font-size: 12px; color: #666; background: #f8f9fa; padding: 10px; border-radius: 4px;">
{{ verification_url }}
</p>
<p>Ссылка действительна в течение 24 часов.</p>
<p>Если вы не регистрировались на {{ app_name }}, просто проигнорируйте это письмо.</p>
</div>
<div class="footer">
<p>© {{ "now"|date("Y") }} {{ app_name }}. Все права защищены.</p>
</div>
</div>
</body>
</html>
// app/Views/emails/welcome.twig
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Добро пожаловать</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 0;
background-color: #f5f5f5;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.logo {
text-align: center;
margin-bottom: 20px;
font-size: 24px;
font-weight: bold;
color: #198754;
}
.btn {
display: inline-block;
padding: 12px 24px;
background-color: #198754;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: bold;
text-align: center;
}
.btn:hover {
background-color: #157347;
}
.footer {
text-align: center;
margin-top: 20px;
font-size: 12px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="logo">{{ app_name }}</div>
<h2>Добро пожаловать, {{ name }}!</h2>
<p>Поздравляем! Ваш email успешно подтверждён.</p>
<p>Теперь вы можете:</p>
<ul>
<li>Создавать и управлять организациями</li>
<li>Приглашать сотрудников</li>
<li>Использовать все функции платформы</li>
</ul>
<p style="text-align: center; margin: 30px 0;">
<a href="{{ base_url('/') }}" class="btn">Перейти в личный кабинет</a>
</p>
<p>Если у вас возникнут вопросы, наша служба поддержки всегда готова помочь.</p>
<p>С уважением,<br>Команда {{ app_name }}</p>
</div>
<div class="footer">
<p>© {{ "now"|date("Y") }} {{ app_name }}. Все права защищены.</p>
</div>
</div>
</body>
</html>
// app/Views/organizations/delete.twig
{% extends 'layouts/base.twig' %}
{% block content %}
<div class="container" style="max-width: 500px; margin-top: 100px;">
<div class="card shadow border-danger">
<div class="card-header bg-danger text-white">
<h5 class="mb-0"><i class="fa-solid fa-triangle-exclamation me-2"></i>Подтверждение удаления</h5>
</div>
<div class="card-body text-center p-4">
<p class="lead">Вы уверены, что хотите удалить организацию?</p>
<div class="alert alert-warning">
<h5 class="alert-heading">{{ organization.name }}</h5>
{% if organization.inn %}
<p class="mb-0">ИНН: {{ organization.inn }}</p>
{% endif %}
{% if organization.type == 'personal' %}
<span class="badge bg-info">Личное пространство</span>
{% else %}
<span class="badge bg-secondary">Организация</span>
{% endif %}
</div>
<div class="alert alert-danger">
<strong>Внимание!</strong> Это действие нельзя отменить.<br>
Будут удалены:
<ul class="text-start mb-0 mt-2">
<li>Все данные организации</li>
<li>Все связи с пользователями</li>
<li>Все записи, связанные с организацией</li>
</ul>
</div>
{% from 'macros/forms.twig' import form_open, form_close %}
{{ form_open(base_url('/organizations/delete/' ~ organization.id)) }}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-danger btn-lg">
<i class="fa-solid fa-trash me-2"></i>Да, удалить безвозвратно
</button>
<a href="{{ base_url('/organizations/edit/' ~ organization.id) }}" class="btn btn-outline-secondary">
Нет, отмена
</a>
</div>
{{ form_close() }}
</div>
</div>
</div>
{% endblock %}
// app/Views/organizations/edit.twig
{% extends 'layouts/base.twig' %}
{% block content %}
<div class="container" style="max-width: 800px;">
<div class="card shadow">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">Редактирование организации</h5>
<a href="{{ base_url('/organizations') }}" class="btn btn-light btn-sm">
<i class="fa-solid fa-arrow-left"></i> Назад
</a>
</div>
<div class="card-body p-4">
{% from 'macros/forms.twig' import form_open, form_close %}
{{ form_open(base_url('/organizations/edit/' ~ organization.id)) }}
{# Основная информация #}
<h6 class="text-muted mb-3">Основная информация</h6>
<div class="mb-3">
<label for="name" class="form-label">Название организации <span class="text-danger">*</span></label>
<input type="text" name="name" class="form-control {{ errors.name is defined ? 'is-invalid' : '' }}"
id="name" required value="{{ old.name|default(organization.name) }}"
placeholder="Например, ООО Ромашка">
{% if errors.name is defined %}
<div class="invalid-feedback">{{ errors.name }}</div>
{% endif %}
</div>
<hr class="my-4">
{# Регистрационные данные #}
<h6 class="text-muted mb-3">Регистрационные данные</h6>
<div class="row">
<div class="col-md-4 mb-3">
<label for="inn" class="form-label">ИНН</label>
<input type="text" name="inn" class="form-control" id="inn"
value="{{ old.inn|default(requisites.inn) }}"
placeholder="10 или 12 цифр" maxlength="12">
</div>
<div class="col-md-4 mb-3">
<label for="ogrn" class="form-label">ОГРН</label>
<input type="text" name="ogrn" class="form-control" id="ogrn"
value="{{ old.ogrn|default(requisites.ogrn) }}"
placeholder="13 цифр" maxlength="13">
</div>
<div class="col-md-4 mb-3">
<label for="kpp" class="form-label">КПП</label>
<input type="text" name="kpp" class="form-control" id="kpp"
value="{{ old.kpp|default(requisites.kpp) }}"
placeholder="9 цифр" maxlength="9">
</div>
</div>
<hr class="my-4">
{# Адреса #}
<h6 class="text-muted mb-3">Адреса</h6>
<div class="mb-3">
<label for="legal_address" class="form-label">Юридический адрес</label>
<input type="text" name="legal_address" class="form-control" id="legal_address"
value="{{ old.legal_address|default(requisites.legal_address) }}"
placeholder="г. Москва, ул. Примерная, д. 1">
</div>
<div class="mb-3">
<label for="actual_address" class="form-label">Фактический адрес</label>
<input type="text" name="actual_address" class="form-control" id="actual_address"
value="{{ old.actual_address|default(requisites.actual_address) }}"
placeholder="г. Москва, ул. Примерная, д. 1">
<div class="form-text">Если совпадает с юридическим — оставьте пустым</div>
</div>
<hr class="my-4">
{# Контакты #}
<h6 class="text-muted mb-3">Контакты</h6>
<div class="row">
<div class="col-md-4 mb-3">
<label for="phone" class="form-label">Телефон</label>
<input type="tel" name="phone" class="form-control" id="phone"
value="{{ old.phone|default(requisites.phone) }}"
placeholder="+7 (999) 123-45-67">
</div>
<div class="col-md-4 mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control" id="email"
value="{{ old.email|default(requisites.email) }}"
placeholder="info@company.ru">
</div>
<div class="col-md-4 mb-3">
<label for="website" class="form-label">Веб-сайт</label>
<input type="url" name="website" class="form-control" id="website"
value="{{ old.website|default(requisites.website) }}"
placeholder="https://company.ru">
</div>
</div>
<hr class="my-4">
{# Банковские реквизиты #}
<h6 class="text-muted mb-3">Банковские реквизиты</h6>
<div class="mb-3">
<label for="bank_name" class="form-label">Название банка</label>
<input type="text" name="bank_name" class="form-control" id="bank_name"
value="{{ old.bank_name|default(requisites.bank_name) }}"
placeholder="ПАО Сбербанк г. Москва">
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label for="bank_bik" class="form-label">БИК</label>
<input type="text" name="bank_bik" class="form-control" id="bank_bik"
value="{{ old.bank_bik|default(requisites.bank_bik) }}"
placeholder="9 цифр" maxlength="9">
</div>
<div class="col-md-4 mb-3">
<label for="checking_account" class="form-label">Расчётный счёт</label>
<input type="text" name="checking_account" class="form-control" id="checking_account"
value="{{ old.checking_account|default(requisites.checking_account) }}"
placeholder="20 цифр" maxlength="20">
</div>
<div class="col-md-4 mb-3">
<label for="correspondent_account" class="form-label">Корреспондентский счёт</label>
<input type="text" name="correspondent_account" class="form-control" id="correspondent_account"
value="{{ old.correspondent_account|default(requisites.correspondent_account) }}"
placeholder="20 цифр" maxlength="20">
</div>
</div>
<div class="d-grid gap-2 d-md-flex mt-4">
<button type="submit" class="btn btn-success btn-lg">
<i class="fa-solid fa-check me-2"></i>Сохранить изменения
</button>
<a href="{{ base_url('/organizations') }}" class="btn btn-outline-secondary btn-lg">
Отмена
</a>
<a href="{{ base_url('/organizations/delete/' ~ organization.id) }}"
class="btn btn-outline-danger btn-lg ms-md-auto">
<i class="fa-solid fa-trash me-2"></i>Удалить организацию
</a>
</div>
{{ form_close() }}
</div>
</div>
</div>
{% endblock %}
// app/Views/organizations/create.twig
{% extends 'layouts/base.twig' %}
{% block content %}
<div class="container" style="max-width: 800px;">
<div class="card shadow">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">Создание организации</h5>
<a href="{{ base_url('/organizations') }}" class="btn btn-light btn-sm">
<i class="fa-solid fa-arrow-left"></i> Назад
</a>
</div>
<div class="card-body p-4">
{% from 'macros/forms.twig' import form_open, form_close %}
{{ form_open(base_url('/organizations/create')) }}
{# Основная информация #}
<h6 class="text-muted mb-3">Основная информация</h6>
<div class="mb-3">
<label for="name" class="form-label">Название организации <span class="text-danger">*</span></label>
<input type="text" name="name" class="form-control {{ errors.name is defined ? 'is-invalid' : '' }}"
id="name" required value="{{ old.name|default('') }}"
placeholder="Например, ООО Ромашка">
{% if errors.name is defined %}
<div class="invalid-feedback">{{ errors.name }}</div>
{% endif %}
</div>
<hr class="my-4">
{# Регистрационные данные #}
<h6 class="text-muted mb-3">Регистрационные данные</h6>
<div class="row">
<div class="col-md-4 mb-3">
<label for="inn" class="form-label">ИНН</label>
<input type="text" name="inn" class="form-control" id="inn"
value="{{ old.inn|default('') }}"
placeholder="10 или 12 цифр" maxlength="12">
</div>
<div class="col-md-4 mb-3">
<label for="ogrn" class="form-label">ОГРН</label>
<input type="text" name="ogrn" class="form-control" id="ogrn"
value="{{ old.ogrn|default('') }}"
placeholder="13 цифр" maxlength="13">
</div>
<div class="col-md-4 mb-3">
<label for="kpp" class="form-label">КПП</label>
<input type="text" name="kpp" class="form-control" id="kpp"
value="{{ old.kpp|default('') }}"
placeholder="9 цифр" maxlength="9">
</div>
</div>
<hr class="my-4">
{# Адреса #}
<h6 class="text-muted mb-3">Адреса</h6>
<div class="mb-3">
<label for="legal_address" class="form-label">Юридический адрес</label>
<input type="text" name="legal_address" class="form-control" id="legal_address"
value="{{ old.legal_address|default('') }}"
placeholder="г. Москва, ул. Примерная, д. 1">
</div>
<div class="mb-3">
<label for="actual_address" class="form-label">Фактический адрес</label>
<input type="text" name="actual_address" class="form-control" id="actual_address"
value="{{ old.actual_address|default('') }}"
placeholder="г. Москва, ул. Примерная, д. 1">
<div class="form-text">Если совпадает с юридическим — оставьте пустым</div>
</div>
<hr class="my-4">
{# Контакты #}
<h6 class="text-muted mb-3">Контакты</h6>
<div class="row">
<div class="col-md-4 mb-3">
<label for="phone" class="form-label">Телефон</label>
<input type="tel" name="phone" class="form-control" id="phone"
value="{{ old.phone|default('') }}"
placeholder="+7 (999) 123-45-67">
</div>
<div class="col-md-4 mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control" id="email"
value="{{ old.email|default('') }}"
placeholder="info@company.ru">
</div>
<div class="col-md-4 mb-3">
<label for="website" class="form-label">Веб-сайт</label>
<input type="url" name="website" class="form-control" id="website"
value="{{ old.website|default('') }}"
placeholder="https://company.ru">
</div>
</div>
<hr class="my-4">
{# Банковские реквизиты #}
<h6 class="text-muted mb-3">Банковские реквизиты</h6>
<div class="mb-3">
<label for="bank_name" class="form-label">Название банка</label>
<input type="text" name="bank_name" class="form-control" id="bank_name"
value="{{ old.bank_name|default('') }}"
placeholder="ПАО Сбербанк г. Москва">
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label for="bank_bik" class="form-label">БИК</label>
<input type="text" name="bank_bik" class="form-control" id="bank_bik"
value="{{ old.bank_bik|default('') }}"
placeholder="9 цифр" maxlength="9">
</div>
<div class="col-md-4 mb-3">
<label for="checking_account" class="form-label">Расчётный счёт</label>
<input type="text" name="checking_account" class="form-control" id="checking_account"
value="{{ old.checking_account|default('') }}"
placeholder="20 цифр" maxlength="20">
</div>
<div class="col-md-4 mb-3">
<label for="correspondent_account" class="form-label">Корреспондентский счёт</label>
<input type="text" name="correspondent_account" class="form-control" id="correspondent_account"
value="{{ old.correspondent_account|default('') }}"
placeholder="20 цифр" maxlength="20">
</div>
</div>
<div class="d-grid gap-2 d-md-flex mt-4">
<button type="submit" class="btn btn-primary btn-lg">
<i class="fa-solid fa-check me-2"></i>Создать организацию
</button>
<a href="{{ base_url('/organizations') }}" class="btn btn-outline-secondary btn-lg">
Отмена
</a>
</div>
{{ form_close() }}
</div>
</div>
</div>
{% endblock %}
// app/Views/organizations/index.twig
{% extends 'layouts/base.twig' %}
{% block content %}
<div class="container" style="max-width: 900px;">
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="mb-0">Мои организации</h4>
<a href="{{ base_url('/organizations/create') }}" class="btn btn-primary">
<i class="fa-solid fa-plus me-2"></i>Создать организацию
</a>
</div>
{% if organizations is empty %}
<div class="card shadow-sm">
<div class="card-body text-center py-5">
<i class="fa-solid fa-building display-1 text-muted mb-3"></i>
<h5 class="text-muted">Организаций пока нет</h5>
<p class="text-muted">Создайте свою первую организацию для начала работы</p>
<a href="{{ base_url('/organizations/create') }}" class="btn btn-primary">
<i class="fa-solid fa-plus me-2"></i>Создать организацию
</a>
</div>
</div>
{% else %}
<div class="row">
{% for org in organizations %}
{# Декодируем JSON реквизиты #}
{% set req = org.requisites ? org.requisites|json_decode : {} %}
<div class="col-md-6 mb-3">
<div class="card shadow-sm h-100 {{ org.id == session.get('active_org_id') ? 'border-primary' : '' }}">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<h5 class="card-title mb-1">
{% if org.type == 'personal' %}
<i class="fa-solid fa-user text-info me-2"></i>
{% else %}
<i class="fa-solid fa-building text-primary me-2"></i>
{% endif %}
{{ org.name }}
</h5>
<small class="text-muted">
{% if org.type == 'personal' %}
Личное пространство
{% else %}
{% if req.inn %}ИНН: {{ req.inn }}{% endif %}
{% endif %}
</small>
</div>
{% if org.id == session.get('active_org_id') %}
<span class="badge bg-primary">Активна</span>
{% endif %}
</div>
{# Краткая информация #}
{% if req.phone or req.email %}
<div class="mb-3">
{% if req.phone %}
<small class="d-block text-muted">
<i class="fa-solid fa-phone me-1"></i>{{ req.phone }}
</small>
{% endif %}
{% if req.email %}
<small class="d-block text-muted">
<i class="fa-solid fa-envelope me-1"></i>{{ req.email }}
</small>
{% endif %}
</div>
{% endif %}
<div class="d-flex flex-wrap gap-2">
{# Кнопка выбора/переключения #}
{% if org.id != session.get('active_org_id') %}
<a href="{{ base_url('/organizations/switch/' ~ org.id) }}"
class="btn btn-outline-primary btn-sm">
<i class="fa-solid fa-check me-1"></i>Выбрать
</a>
{% endif %}
{# Кнопка редактирования #}
<a href="{{ base_url('/organizations/edit/' ~ org.id) }}"
class="btn btn-outline-secondary btn-sm">
<i class="fa-solid fa-pen me-1"></i>Ред.
</a>
{# Кнопка удаления (только для бизнес-организаций) #}
{% if org.type == 'business' %}
<a href="{{ base_url('/organizations/delete/' ~ org.id) }}"
class="btn btn-outline-danger btn-sm">
<i class="fa-solid fa-trash me-1"></i>Удалить
</a>
{% endif %}
</div>
</div>
<div class="card-footer bg-transparent text-muted small">
Создана: {{ org.created_at|date('d.m.Y') }}
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
{# Статистика #}
<div class="mt-4 text-center text-muted small">
Всего организаций: {{ count }}
</div>
</div>
{% endblock %}
// app/Views/dashboard/index.twig
{# app/Views/dashboard/index.twig #}
{% extends 'layouts/base.twig' %}
{% block content %}
<div class="container-fluid">
<div class="row mb-4">
<div class="col-12">
<h2>{% if current_org %}Добро пожаловать в {{ current_org.name }}!{% else %}Добро пожаловать!{% endif %}</h2>
<p class="text-muted">Ваш личный кабинет для управления бизнесом.</p>
</div>
</div>
<div class="row">
<!-- Карточки модулей (плейсхолдеры) -->
<div class="col-md-6 col-lg-3 mb-4">
<div class="card h-100 text-center p-3">
<div class="card-body">
<i class="fa-solid fa-users fa-3x text-primary mb-3"></i>
<h5 class="card-title">CRM</h5>
<p class="card-text text-muted">Управление клиентами</p>
<a href="#" class="btn btn-outline-primary btn-sm">Скоро</a>
</div>
</div>
</div>
<div class="col-md-6 col-lg-3 mb-4">
<div class="card h-100 text-center p-3">
<div class="card-body">
<i class="fa-solid fa-calendar-check fa-3x text-success mb-3"></i>
<h5 class="card-title">Booking</h5>
<p class="card-text text-muted">Запись на приём</p>
<a href="#" class="btn btn-outline-success btn-sm">Скоро</a>
</div>
</div>
</div>
<div class="col-md-6 col-lg-3 mb-4">
<div class="card h-100 text-center p-3">
<div class="card-body">
<i class="fa-solid fa-file-signature fa-3x text-warning mb-3"></i>
<h5 class="card-title">Proof</h5>
<p class="card-text text-muted">Согласование файлов</p>
<a href="#" class="btn btn-outline-warning btn-sm">Скоро</a>
</div>
</div>
</div>
<div class="col-md-6 col-lg-3 mb-4">
<div class="card h-100 text-center p-3">
<div class="card-body">
<i class="fa-solid fa-list-check fa-3x text-danger mb-3"></i>
<h5 class="card-title">Tasks</h5>
<p class="card-text text-muted">Задачи и проекты</p>
<a href="#" class="btn btn-outline-danger btn-sm">Скоро</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
// app/Views/welcome_message.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome to CodeIgniter 4!</title>
<meta name="description" content="The small framework with powerful features">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/png" href="/favicon.ico">
<!-- STYLES -->
<style {csp-style-nonce}>
transition: background-color 300ms ease, color 300ms ease;
}
background-color: rgba(221, 72, 20, .2);
outline: none;
}
html, body {
color: rgba(33, 37, 41, 1);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
font-size: 16px;
margin: 0;
padding: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
header {
background-color: rgba(247, 248, 249, 1);
padding: .4rem 0 0;
}
.menu {
padding: .4rem 2rem;
}
header ul {
border-bottom: 1px solid rgba(242, 242, 242, 1);
list-style-type: none;
margin: 0;
overflow: hidden;
padding: 0;
text-align: right;
}
header li {
display: inline-block;
}
header li a {
border-radius: 5px;
color: rgba(0, 0, 0, .5);
display: block;
height: 44px;
text-decoration: none;
}
header li.menu-item a {
border-radius: 5px;
margin: 5px 0;
height: 38px;
line-height: 36px;
padding: .4rem .65rem;
text-align: center;
}
header li.menu-item a:hover,
header li.menu-item a:focus {
background-color: rgba(221, 72, 20, .2);
color: rgba(221, 72, 20, 1);
}
header .logo {
float: left;
height: 44px;
padding: .4rem .5rem;
}
header .menu-toggle {
display: none;
float: right;
font-size: 2rem;
font-weight: bold;
}
header .menu-toggle button {
background-color: rgba(221, 72, 20, .6);
border: none;
border-radius: 3px;
color: rgba(255, 255, 255, 1);
cursor: pointer;
font: inherit;
font-size: 1.3rem;
height: 36px;
padding: 0;
margin: 11px 0;
overflow: visible;
width: 40px;
}
header .menu-toggle button:hover,
header .menu-toggle button:focus {
background-color: rgba(221, 72, 20, .8);
color: rgba(255, 255, 255, .8);
}
header .heroe {
margin: 0 auto;
max-width: 1100px;
padding: 1rem 1.75rem 1.75rem 1.75rem;
}
header .heroe h1 {
font-size: 2.5rem;
font-weight: 500;
}
header .heroe h2 {
font-size: 1.5rem;
font-weight: 300;
}
section {
margin: 0 auto;
max-width: 1100px;
padding: 2.5rem 1.75rem 3.5rem 1.75rem;
}
section h1 {
margin-bottom: 2.5rem;
}
section h2 {
font-size: 120%;
line-height: 2.5rem;
padding-top: 1.5rem;
}
section pre {
background-color: rgba(247, 248, 249, 1);
border: 1px solid rgba(242, 242, 242, 1);
display: block;
font-size: .9rem;
margin: 2rem 0;
padding: 1rem 1.5rem;
white-space: pre-wrap;
word-break: break-all;
}
section code {
display: block;
}
section a {
color: rgba(221, 72, 20, 1);
}
section svg {
margin-bottom: -5px;
margin-right: 5px;
width: 25px;
}
.further {
background-color: rgba(247, 248, 249, 1);
border-bottom: 1px solid rgba(242, 242, 242, 1);
border-top: 1px solid rgba(242, 242, 242, 1);
}
.further h2:first-of-type {
padding-top: 0;
}
.svg-stroke {
fill: none;
stroke: #000;
stroke-width: 32px;
}
footer {
background-color: rgba(221, 72, 20, .8);
text-align: center;
}
footer .environment {
color: rgba(255, 255, 255, 1);
padding: 2rem 1.75rem;
}
footer .copyrights {
background-color: rgba(62, 62, 62, 1);
color: rgba(200, 200, 200, 1);
padding: .25rem 1.75rem;
}
@media (max-width: 629px) {
header ul {
padding: 0;
}
header .menu-toggle {
padding: 0 1rem;
}
header .menu-item {
background-color: rgba(244, 245, 246, 1);
border-top: 1px solid rgba(242, 242, 242, 1);
margin: 0 15px;
width: calc(100% - 30px);
}
header .menu-toggle {
display: block;
}
header .hidden {
display: none;
}
header li.menu-item a {
background-color: rgba(221, 72, 20, .1);
}
header li.menu-item a:hover,
header li.menu-item a:focus {
background-color: rgba(221, 72, 20, .7);
color: rgba(255, 255, 255, .8);
}
}
</style>
</head>
<body>
<!-- HEADER: MENU + HEROE SECTION -->
<header>
<div class="menu">
<ul>
<li class="logo">
<a href="https:
<svg role="img" aria-label="Visit CodeIgniter.com official website!" xmlns="http:
</a>
</li>
<li class="menu-toggle">
<button id="menuToggle">&#9776;</button>
</li>
<li class="menu-item hidden"><a href="#">Home</a></li>
<li class="menu-item hidden"><a href="https:
</li>
<li class="menu-item hidden"><a href="https:
<li class="menu-item hidden"><a
href="https:
</li>
</ul>
</div>
<div class="heroe">
<h1>Welcome to CodeIgniter <?= CodeIgniter\CodeIgniter::CI_VERSION ?></h1>
<h2>The small framework with powerful features</h2>
</div>
</header>
<!-- CONTENT -->
<section>
<h1>About this page</h1>
<p>The page you are looking at is being generated dynamically by CodeIgniter.</p>
<p>If you would like to edit this page you will find it located at:</p>
<pre><code>app/Views/welcome_message.php</code></pre>
<p>The corresponding controller for this page can be found at:</p>
<pre><code>app/Controllers/Home.php</code></pre>
</section>
<div class="further">
<section>
<h1>Go further</h1>
<h2>
<svg xmlns='http:
Learn
</h2>
<p>The User Guide contains an introduction, tutorial, a number of "how to"
guides, and then reference documentation for the components that make up
the framework. Check the <a href="https:
target="_blank">User Guide</a> !</p>
<h2>
<svg xmlns='http:
Discuss
</h2>
<p>CodeIgniter is a community-developed open source project, with several
venues for the community members to gather and exchange ideas. View all
the threads on <a href="https:
target="_blank">CodeIgniter's forum</a>, or <a href="https:
target="_blank">chat on Slack</a> !</p>
<h2>
<svg xmlns='http:
Contribute
</h2>
<p>CodeIgniter is a community driven project and accepts contributions
of code and documentation from the community. Why not
<a href="https:
join us</a> ?</p>
</section>
</div>
<!-- FOOTER: DEBUG INFO + COPYRIGHTS -->
<footer>
<div class="environment">
<p>Page rendered in {elapsed_time} seconds using {memory_usage} MB of memory.</p>
<p>Environment: <?= ENVIRONMENT ?></p>
</div>
<div class="copyrights">
<p>&copy; <?= date('Y') ?> CodeIgniter Foundation. CodeIgniter is open source project released under the MIT
open source licence.</p>
</div>
</footer>
<!-- SCRIPTS -->
<script {csp-script-nonce}>
document.getElementById("menuToggle").addEventListener('click', toggleMenu);
function toggleMenu() {
var menuItems = document.getElementsByClassName('menu-item');
for (var i = 0; i < menuItems.length; i++) {
var menuItem = menuItems[i];
menuItem.classList.toggle("hidden");
}
}
</script>
<!-- -->
</body>
</html>
// app/Views/landing/index.twig
{# app/Views/Landing/index.twig #}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Бизнес.Точка - Автоматизация для бизнеса</title>
<link href="{{ base_url('assets/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ base_url('assets/css/all.min.css') }}" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container">
<nav class="d-flex justify-content-between align-items-center py-3">
<h3 class="text-primary fw-bold m-0"><i class="fa-solid fa-circle-nodes"></i> Бизнес.Точка</h3>
<div>
<a href="{{ base_url('/login') }}" class="btn btn-outline-primary me-2">Войти</a>
<a href="{{ base_url('/register') }}" class="btn btn-primary">Попробовать бесплатно</a>
</div>
</nav>
<div class="text-center py-5 my-5">
<h1 class="display-4 fw-bold mb-3">Всё для вашего бизнеса</h1>
<p class="lead text-muted mb-4">CRM, Запись клиентов, Задачи и Согласование файлов в одном месте.</p>
<div class="d-flex justify-content-center gap-3">
<a href="{{ base_url('/register') }}" class="btn btn-primary btn-lg px-5 shadow">Начать бесплатно</a>
</div>
</div>
</div>
</body>
</html>
// app/Views/auth/register_success.twig
{% extends 'layouts/public.twig' %}
{% block content %}
<div class="container d-flex justify-content-center align-items-center" style="min-height: 80vh;">
<div class="card shadow-lg border-0" style="max-width: 500px; width: 100%;">
<div class="card-body p-5 text-center">
<div class="mb-4">
<i class="fa-solid fa-envelope-circle-check display-1 text-primary"></i>
</div>
<h3 class="mb-3">Регистрация успешна!</h3>
<p class="text-muted mb-4">
Мы отправили письмо с ссылкой для подтверждения email на вашу почту.
</p>
<div class="alert alert-info text-start">
<i class="fa-solid fa-info-circle me-2"></i>
<strong>Что делать дальше:</strong>
<ol class="mb-0 mt-2">
<li>Откройте почтовый ящик</li>
<li>Найдите письмо от нас</li>
<li>Нажмите на ссылку для подтверждения</li>
</ol>
</div>
<div class="mb-4">
<a href="/auth/resend-verification" class="text-muted small">
<i class="fa-solid fa-rotate-right me-1"></i>Не получили письмо? Отправить снова
</a>
</div>
<hr class="my-4">
<a href="/" class="btn btn-outline-secondary">
<i class="fa-solid fa-home me-2"></i>На главную
</a>
</div>
</div>
</div>
{% endblock %}
// app/Views/auth/resend_verification.twig
{% extends 'layouts/public.twig' %}
{% block content %}
<div class="container d-flex justify-content-center align-items-center" style="min-height: 80vh;">
<div class="card shadow-lg border-0" style="max-width: 500px; width: 100%;">
<div class="card-body p-5">
<div class="text-center mb-4">
<i class="fa-solid fa-envelope-open-text display-4 text-primary"></i>
<h4 class="mt-3">Повторная отправка письма</h4>
<p class="text-muted">Введите ваш email для получения новой ссылки подтверждения</p>
</div>
{% from 'macros/forms.twig' import form_open, form_close %}
{{ form_open(base_url('/auth/resend-verification')) }}
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control {{ errors.email is defined ? 'is-invalid' : '' }}"
id="email" required value="{{ old.email|default('') }}"
placeholder="name@example.com">
{% if errors.email is defined %}
<div class="invalid-feedback">{{ errors.email }}</div>
{% endif %}
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">
<i class="fa-solid fa-paper-plane me-2"></i>Отправить письмо
</button>
<a href="/" class="btn btn-outline-secondary">
На главную
</a>
</div>
{{ form_close() }}
</div>
</div>
</div>
{% endblock %}
// app/Views/auth/register.twig
{% extends 'layouts/public.twig' %}
{% block content %}
<div class="card shadow-sm" style="width: 100%; max-width: 400px;">
<div class="card-body p-4">
<h3 class="text-center mb-4">Регистрация</h3>
{# ИСПОЛЬЗУЕМ МАКРОС form_open. CSRF добавится АВТОМАТИЧЕСКИ #}
{{ form_open(base_url('/register'), 'class="needs-validation"') }}
<div class="mb-3">
<label for="name" class="form-label">Ваше имя</label>
<input type="text" name="name" class="form-control" id="name" required value="{{ old.name|default('') }}">
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control" id="email" required value="{{ old.email|default('') }}">
</div>
<div class="mb-3">
<label for="password" class="form-label">Пароль</label>
<input type="password" name="password" class="form-control" id="password" required minlength="6">
</div>
<button type="submit" class="btn btn-primary w-100">Создать аккаунт</button>
{# ЗАКРЫВАЕМ ФОРМУ МАКРОСОМ #}
{{ form_close() }}
<div class="mt-3 text-center">
<small>Уже есть аккаунт? <a href="{{ base_url('/login') }}">Войти</a></small>
</div>
</div>
</div>
{% endblock %}
// app/Views/auth/verify_error.twig
{% extends 'layouts/public.twig' %}
{% block content %}
<div class="container d-flex justify-content-center align-items-center" style="min-height: 80vh;">
<div class="card shadow-lg border-0" style="max-width: 500px; width: 100%;">
<div class="card-body p-5 text-center">
<div class="mb-4">
<i class="fa-solid fa-triangle-exclamation display-1 text-warning"></i>
</div>
<h3 class="mb-3">Ошибка подтверждения</h3>
<div class="alert alert-warning text-start">
{{ message|default('Недействительная ссылка для подтверждения.') }}
</div>
<div class="mb-4">
<a href="/auth/resend-verification" class="btn btn-outline-primary">
<i class="fa-solid fa-envelope me-2"></i>Запросить ссылку повторно
</a>
</div>
<hr class="my-4">
<div class="d-flex justify-content-center gap-3">
<a href="/" class="btn btn-outline-secondary">
<i class="fa-solid fa-home me-2"></i>На главную
</a>
<a href="/login" class="btn btn-outline-secondary">
<i class="fa-solid fa-sign-in-alt me-2"></i>Войти
</a>
</div>
</div>
</div>
</div>
{% endblock %}
// app/Views/auth/verify_success.twig
{% extends 'layouts/public.twig' %}
{% block content %}
<div class="container d-flex justify-content-center align-items-center" style="min-height: 80vh;">
<div class="card shadow-lg border-0" style="max-width: 500px; width: 100%;">
<div class="card-body p-5 text-center">
<div class="mb-4">
<i class="fa-solid fa-circle-check display-1 text-success"></i>
</div>
<h3 class="mb-3">Email подтверждён!</h3>
<p class="text-muted mb-4">
{% if name %}
{{ name }}, спасибо за подтверждение.
{% else %}
Спасибо за подтверждение.
{% endif %}
</p>
<p class="text-muted mb-4">
Теперь вы можете войти в систему и начать работу.
</p>
<div class="d-grid gap-2">
<a href="/login" class="btn btn-primary btn-lg">
<i class="fa-solid fa-sign-in-alt me-2"></i>Войти
</a>
</div>
</div>
</div>
</div>
{% endblock %}
// app/Views/auth/login.twig
{% extends 'layouts/public.twig' %}
{% block content %}
<div class="card shadow-sm" style="width: 100%; max-width: 400px;">
<div class="card-body p-4">
<div class="text-center mb-4">
<i class="fa-solid fa-circle-nodes fa-3x text-primary mb-2"></i>
<h3>Бизнес.Точка</h3>
<p class="text-muted">Вход в систему</p>
</div>
{{ form_open(base_url('/login'), 'class="needs-validation"') }}
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" value="{{ old.email|default('') }}" class="form-control" id="email" required autofocus>
</div>
<div class="mb-3">
<label for="password" class="form-label">Пароль</label>
<input type="password" name="password" value="{{ old.password|default('') }}" class="form-control" id="password" required>
</div>
<button type="submit" class="btn btn-primary w-100">Войти</button>
{{ form_close() }}
<div class="mt-3 text-center">
<small>Нет аккаунта? <a href="{{ base_url('/register') }}">Зарегистрироваться</a></small>
</div>
</div>
</div>
{% endblock %}
// app/Views/components/alerts.twig
{# app/Views/components/alerts.twig #}
{% set alerts = get_alerts() %}
{% if alerts is not empty %}
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 1050">
{% for alert in alerts %}
{# Преобразуем наш тип 'error' в класс Bootstrap 'danger' #}
{% set bs_type = alert.type == 'error' ? 'danger' : alert.type %}
<div class="toast align-items-center text-white bg-{{ bs_type }} border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
{{ alert.message }}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
{% endfor %}
</div>
<script>
// Автоматически показываем все toasts при загрузке страницы
document.addEventListener("DOMContentLoaded", function() {
var toastElList = [].slice.call(document.querySelectorAll('.toast'));
toastElList.forEach(function(toastEl) {
var toast = new bootstrap.Toast(toastEl);
toast.show();
});
});
</script>
{% endif %}
// app/Views/components/table/pagination.twig
{#
pagination.twig - Универсальный компонент пагинации
Использует встроенный пейджер CodeIgniter 4
Параметры:
- pagination: Объект с данными пагинации (из pager->getDetails())
- currentPage: Текущая страница
- pageCount: Всего страниц
- total: Всего записей
- perPage: Записей на странице
- from: Начальная запись
- to: Конечная запись
- id: ID таблицы для уникальности элементов
#}
{% set currentPage = pagination.currentPage|default(1) %}
{% set totalPages = pagination.pageCount|default(1) %}
{% set totalRecords = pagination.total|default(0) %}
{% set perPage = pagination.perPage|default(10) %}
{% set from = pagination.from|default((currentPage - 1) * perPage + 1) %}
{% set to = pagination.to|default(min(currentPage * perPage, totalRecords)) %}
{# Информация о записях #}
{% set infoText = 'Показано ' ~ from ~ '' ~ to ~ ' из ' ~ totalRecords %}
<div class="pagination-wrapper">
{# Информация о количестве записей #}
<div class="pagination-info">
<span>{{ infoText }}</span>
</div>
{# Кнопки навигации - посередине #}
<nav aria-label="Пагинация">
<ul class="pagination pagination-sm mb-0">
{# Предыдущая страница #}
<li class="page-item {{ currentPage <= 1 ? 'disabled' }}">
<a class="page-link" href="#" data-nav-page="prev" aria-label="Предыдущая">
<i class="fa-solid fa-chevron-left"></i>
</a>
</li>
{# Номера страниц #}
{% set startPage = max(1, currentPage - 2) %}
{% set endPage = min(totalPages, currentPage + 2) %}
{# Всегда показываем первую страницу если есть #}
{% if startPage > 1 %}
<li class="page-item">
<a class="page-link" href="#" data-page="1">1</a>
</li>
{% if startPage > 2 %}
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
{% endif %}
{% endif %}
{# Страницы вокруг текущей #}
{% for page in startPage..endPage %}
<li class="page-item {{ page == currentPage ? 'active' }}">
<a class="page-link" href="#" data-page="{{ page }}">{{ page }}</a>
</li>
{% endfor %}
{# Всегда показываем последнюю страницу если есть #}
{% if endPage < totalPages %}
{% if endPage < totalPages - 1 %}
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
{% endif %}
<li class="page-item">
<a class="page-link" href="#" data-page="{{ totalPages }}">{{ totalPages }}</a>
</li>
{% endif %}
{# Следующая страница #}
<li class="page-item {{ currentPage >= totalPages ? 'disabled' }}">
<a class="page-link" href="#" data-nav-page="next" aria-label="Следующая">
<i class="fa-solid fa-chevron-right"></i>
</a>
</li>
</ul>
</nav>
{# Выбор количества записей - справа #}
<div class="per-page-selector">
<label for="per-page-select-{{ id|default('table') }}">Записей на странице:</label>
<select id="per-page-select-{{ id|default('table') }}" class="form-select-sm per-page-select">
<option value="10" {{ perPage == 10 ? 'selected' }}>10</option>
<option value="25" {{ perPage == 25 ? 'selected' }}>25</option>
<option value="50" {{ perPage == 50 ? 'selected' }}>50</option>
<option value="100" {{ perPage == 100 ? 'selected' }}>100</option>
</select>
</div>
</div>
// app/Views/components/table/table_header.twig
{#
table_header.twig - Переиспользуемый шаблон заголовка таблицы
Параметры:
- columns: Ассоциативный массив столбцов ['name' => ['label' => 'Name', 'width' => '40%']]
- sort: Текущий столбец сортировки
- order: Направление сортировки (asc/desc)
- filters: Текущие значения фильтров
#}
<thead class="table-light">
<tr>
{% for columnKey, column in columns %}
<th style="width: {{ column.width|default('auto') }};"
class="{{ column.align|default('') }}"
data-sort-column="{{ columnKey }}">
<div class="header-content">
{# Иконка поиска #}
<i class="fa-solid fa-search search-trigger text-muted"
data-search-trigger="{{ columnKey }}"
title="{{ column.searchTitle|default('Поиск по ' ~ column.label|lower) }}"></i>
{# Текст заголовка #}
<span class="header-text" data-header-text="{{ columnKey }}">
{{ column.label }}
</span>
{# Иконка сортировки #}
<i class="fa-solid fa-sort sort-icon {{ sort == columnKey ? 'active' : 'text-muted' }}"
data-sort="{{ columnKey }}"
title="{{ sort == columnKey ? (order == 'asc' ? 'Сортировка по возрастанию (нажмите для сортировки по убыванию)' : 'Сортировка по убыванию (нажмите для сортировки по возрастанию)') : 'Сортировка' }}"></i>
{# Поле поиска #}
<input type="text"
class="form-control form-control-sm header-search-input"
data-search-input="{{ columnKey }}"
value="{{ filters[columnKey]|default('') }}"
placeholder="{{ column.placeholder|default('Поиск...') }}"
style="display: none;">
</div>
</th>
{% endfor %}
{# Колонка действий (опционально) #}
{% if actions is defined and actions %}
<th class="text-end {{ actions.class|default('') }}" style="width: {{ actions.width|default('auto') }};">
{{ actions.label|default('Действия') }}
</th>
{% endif %}
</tr>
</thead>
// app/Views/components/table/README.md
# DataTable Components
Переиспользуемые компоненты для отображения интерактивных таблиц с AJAX-загрузкой, сортировкой и поиском.
## Структура компонентов
```
public/
├── js/
│ └── modules/
│ └── DataTable.js # JS-модуль для инициализации таблиц
└── css/
└── components/
└── data-table.css # Стили для интерактивных таблиц
app/Views/components/table/
├── table.twig # Основной компонент таблицы
├── table_header.twig # Переиспользуемый заголовок
└── pagination.twig # Компонент пагинации
```
## Быстрый старт
### 1. Подключение стилей и скриптов
В вашем шаблоне добавьте:
```twig
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="/css/components/data-table.css">
{% endblock %}
{% block scripts %}
{{ parent() }}
<script src="/js/modules/DataTable.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
new DataTable('your-table-id', {
url: '/your-module/table',
perPage: 10
});
});
</script>
{% endblock %}
```
### 2. Использование компонента таблицы
```twig
{{ include('@components/table/table.twig', {
id: 'products-table',
url: '/products/table',
perPage: 25,
sort: sort|default(''),
order: order|default('asc'),
filters: filters|default({}),
columns: [
{ name: 'name', label: 'Название', width: '35%' },
{ name: 'sku', label: 'Артикул', width: '15%' },
{ name: 'price', label: 'Цена', width: '15%' },
{ name: 'stock', label: 'Остаток', width: '15%' }
],
actions: { label: 'Действия', width: '20%' },
emptyMessage: 'Товары не найдены'
}) }}
```
## Конфигурация столбцов
Каждый столбец поддерживает следующие параметры:
| Параметр | Тип | Описание |
|----------|-----|----------|
| `name` | string | Идентификатор столбца (используется для сортировки и фильтрации) |
| `label` | string | Отображаемое название столбца |
| `width` | string | Ширина столбца (например, '35%', '200px') |
| `placeholder` | string | Текст-подсказка в поле поиска |
| `searchTitle` | string | Title для иконки поиска |
| `align` | string | CSS-класс выравнивания |
## Конфигурация пагинации
Компонент автоматически получает данные из объекта `pager`:
```php
// В контроллере
$pagination = [
'currentPage' => $pager->getCurrentPage(),
'totalPages' => $pager->getPageCount(),
'totalRecords' => $pager->getTotal(),
'perPage' => $perPage,
'from' => (($pager->getCurrentPage() - 1) * $perPage + 1),
'to' => min($pager->getCurrentPage() * $perPage, $pager->getTotal())
];
```
## Пример контроллера
```php
public function table()
{
$page = (int) ($this->request->getGet('page') ?? 1);
$perPage = (int) ($this->request->getGet('perPage') ?? 10);
$sort = $this->request->getGet('sort') ?? '';
$order = $this->request->getGet('order') ?? 'asc';
// Фильтры
$filters = [
'name' => $this->request->getGet('filters[name]') ?? '',
];
// Построение запроса
$builder = $this->model->builder();
// Применяем фильтры
if (!empty($filters['name'])) {
$builder->like('name', $filters['name']);
}
// Сортировка
if (!empty($sort)) {
$builder->orderBy($sort, $order);
}
// Пагинация
$items = $builder->paginate($perPage, 'default', $page);
$data = [
'items' => $items,
'pager' => $this->model->pager,
'perPage' => $perPage,
'sort' => $sort,
'order' => $order,
'filters' => $filters,
];
return $this->renderTwig('path/to/your/_table', $data);
}
```
## AJAX-ответ
Для AJAX-запросов контроллер должен возвращать только `tbody` и `tfoot`:
```twig
{# _table.twig для модуля #}
{% set isAjax = app.request.headers.get('X-Requested-With') == 'XMLHttpRequest' %}
{% if isAjax %}
{# AJAX: только tbody #}
<tbody>
{% for item in items %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.price }}</td>
<td class="text-end">
<a href="...">Редактировать</a>
</td>
</tr>
{% endfor %}
</tbody>
{% if items is not empty and pager %}
<tfoot>
<tr>
<td colspan="3">
{{ include('@components/table/pagination.twig', {
pagination: paginationData,
id: 'your-table-id'
}) }}
</td>
</tr>
</tfoot>
{% endif %}
{% else %}
{# Обычный запрос: полная таблица #}
<div class="table-responsive">
{{ include('@components/table/table.twig', {
id: 'your-table-id',
url: '/your-module/table',
perPage: perPage,
columns: columns,
pagination: paginationData,
actions: { label: 'Действия' },
emptyMessage: 'Нет данных'
}) }}
</div>
{% endif %}
```
## API DataTable
### Опции при инициализации
```javascript
new DataTable('container-id', {
url: '/api/endpoint', // URL для AJAX-загрузки
perPage: 10, // Записей на странице по умолчанию
debounceTime: 300, // Задержка поиска в мс
preserveSearchOnSort: true // Сохранять видимость поиска при сортировке
});
```
### Методы
```javascript
const table = new DataTable('my-table', options);
// Установка фильтра
table.setFilter('columnName', 'value');
// Установка количества записей
table.setPerPage(25);
// Переход на страницу
table.goToPage(3);
```
## Доступные CSS-классы
| Класс | Описание |
|-------|----------|
| `.data-table` | Основной контейнер таблицы |
| `.header-content` | Контейнер для элементов заголовка |
| `.header-text` | Текст заголовка столбца |
| `.search-trigger` | Иконка поиска |
| `.sort-icon` | Иконка сортировки |
| `.header-search-input` | Поле ввода поиска |
| `.sort-icon.active` | Активная сортировка |
| `.pagination-wrapper` | Обёртка пагинации |
## Расширение функциональности
### Добавление кастомных действий
Для добавления кнопок действий в строки:
```twig
{% for client in clients %}
<tr>
<td>{{ client.name }}</td>
<td>{{ client.email }}</td>
<td class="actions-cell text-end">
<a href="{{ editUrl }}" class="btn btn-sm btn-outline-primary">
<i class="fa-solid fa-pen"></i>
</a>
</td>
</tr>
{% endfor %}
```
### Кастомные строки
Компонент поддерживает произвольное содержимое ячеек через параметр `rows`:
```twig
{% set rows = [] %}
{% for product in products %}
{% set rows = rows|merge([{
cells: [
{ content: '<strong>' ~ product.name ~ '</strong>', class: '' },
{ content: product.price ~ ' ₽', class: 'text-end' }
],
actions: '<a href="...">Редактировать</a>'
}]) %}
{% endfor %}
{{ include('@components/table/table.twig', {
id: 'products-table',
rows: rows,
columns: columns,
...
}) }}
```
// app/Views/components/table/table.twig
{#
table.twig - Универсальный компонент таблицы с AJAX-загрузкой
Параметры:
- id: ID контейнера таблицы (обязательно)
- url: URL для AJAX-загрузки данных (обязательно)
- perPage: Количество записей на странице (по умолчанию 10)
- columns: Ассоциативный массив ['name' => ['label' => 'Name', 'width' => '40%']]
- sort: Текущий столбец сортировки
- order: Направление сортировки
- filters: Текущие значения фильтров
- items: Массив объектов модели (автоматический рендеринг)
- rows: Предварительно построенные строки (устаревший формат, для совместимости)
- pagination: Данные пагинации
- actions: Настройки колонки действий
- emptyMessage: Сообщение при отсутствии данных
- tableClass: Дополнительные классы для таблицы
- itemsKey: Ключ массива items в данных (по умолчанию 'items')
#}
<div id="{{ id }}" class="data-table" data-url="{{ url }}" data-per-page="{{ perPage|default(10) }}">
<table class="table table-hover mb-0 {{ tableClass|default('') }}">
{# Заголовок таблицы #}
{{ include('@components/table/table_header.twig', {
columns: columns,
sort: sort|default(''),
order: order|default('asc'),
filters: filters|default({}),
actions: actions|default(false)
}) }}
{# Тело таблицы #}
<tbody>
{% if rows is defined and rows|length > 0 %}
{# Старый формат: предварительно построенные строки #}
{% for row in rows %}
<tr>
{% for cell in row.cells %}
<td class="{{ cell.class|default('') }}">{{ cell.content|raw }}</td>
{% endfor %}
{% if row.actions is defined %}
<td class="actions-cell text-end">
{{ row.actions|raw }}
</td>
{% endif %}
</tr>
{% endfor %}
{% elseif items is defined and items|length > 0 %}
{# Новый формат: автоматический рендеринг из объектов модели #}
{% set columnKeys = columns|keys %}
{% for item in items %}
<tr>
{# Ячейки данных #}
{% for columnKey in columnKeys %}
<td>{{ attribute(item, columnKey)|default('—') }}</td>
{% endfor %}
{# Колонка действий #}
{% if actions is defined and actions %}
<td class="actions-cell text-end">
{#Placeholder для действий - переопределяется в дочерних шаблонах#}
{% if item.actions is defined %}{{ item.actions|raw }}{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
{% else %}
{# Пустое состояние #}
<tr>
<td colspan="{{ columns|length + (actions is defined and actions ? 1 : 0) }}"
class="text-center py-4 text-muted">
{{ emptyMessage|default('Нет данных для отображения') }}
</td>
</tr>
{% endif %}
</tbody>
{# Футер с пагинацией - показываем всегда #}
<tfoot>
<tr>
<td colspan="{{ columns|length + 1 }}">
{{ include('@components/table/pagination.twig', {
pagination: pagerDetails|default({
currentPage: 1,
pageCount: 1,
total: items|length|default(0),
perPage: perPage|default(10),
from: 1,
to: items|length|default(0)
}),
id: id
}) }}
</td>
</tr>
</tfoot>
</table>
</div>
// app/Views/macros/forms.twig
{# app/Views/macros/forms.twig #}
{% macro form_open(action, attributes = '') %}
<form action="{{ action }}" method="post" {{ attributes|raw }}>
{# Выводим глобальную переменную csrf_token #}
{{ csrf_field()|raw }}
{% endmacro %}
{% macro form_close() %}
</form>
{% endmacro %}
// app/.htaccess
<IfModule authz_core_module>
Require all denied
</IfModule>
<IfModule !authz_core_module>
Deny from all
</IfModule>
// app/Filters/OrganizationFilter.php
<?php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class OrganizationFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
$session = session();
$uri = $request->getUri();
$currentPath = $uri->getPath();
if (!$session->get('isLoggedIn')) {
$publicRoutes = [
'/',
'/login',
'/register',
'/register/success',
'/auth/verify',
'/auth/resend-verification',
];
if (!in_array($currentPath, $publicRoutes)) {
return redirect()->to('/');
}
return;
}
$publicAuthRoutes = [
'/login',
'/register',
'/logout',
'/register/success',
'/auth/verify',
'/auth/resend-verification',
'/organizations',
'/organizations/create',
'/organizations/switch'
];
if (in_array($currentPath, $publicAuthRoutes) || strpos($currentPath, '/organizations/switch') === 0) {
return;
}
if ($currentPath === '/') {
return;
}
if (empty(session()->get('active_org_id'))) {
return redirect()->to('/organizations');
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
}
}
// app/Filters/.gitkeep
// app/Models/UserModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
'email',
'password',
'name',
'phone',
'avatar',
'verification_token',
'email_verified',
'verified_at'
];
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $beforeInsert = ['hashPassword'];
protected $beforeUpdate = ['hashPassword'];
protected function hashPassword(array $data)
{
if (isset($data['data']['password'])) {
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
}
return $data;
}
}
// app/Models/OrganizationModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class OrganizationModel extends Model
{
protected $table = 'organizations';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['owner_id', 'name', 'type', 'logo', 'requisites', 'trial_ends_at', 'settings'];
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
public function getUserOrganizations(int $userId)
{
return $this->where('owner_id', $userId)->findAll();
}
}
// app/Models/.gitkeep
// app/Models/OrganizationUserModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class OrganizationUserModel extends Model
{
protected $table = 'organization_users';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $allowedFields = ['organization_id', 'user_id', 'role', 'status', 'joined_at'];
protected $useTimestamps = false;
}
// app/Database/Seeds/.gitkeep
// app/Database/Migrations/2026-01-07-053357_CreateUsersTable.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateUsersTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 255,
'unique' => true,
],
'password' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'phone' => [
'type' => 'VARCHAR',
'constraint' => 20,
'null' => true,
],
'avatar' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('users');
}
public function down()
{
$this->forge->dropTable('users');
}
}
// app/Database/Migrations/2026-01-08-000001_AddEmailVerificationToUsers.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddEmailVerificationToUsers extends Migration
{
public function up()
{
$fields = [
'verification_token' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
'comment' => 'Токен для подтверждения email',
],
'email_verified' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
'comment' => 'Статус подтверждения email (0 - не подтвержден, 1 - подтвержден)',
],
'verified_at' => [
'type' => 'DATETIME',
'null' => true,
'comment' => 'Дата и время подтверждения email',
],
];
$this->forge->addColumn('users', $fields);
}
public function down()
{
$this->forge->dropColumn('users', ['verification_token', 'email_verified', 'verified_at']);
}
}
// app/Database/Migrations/2026-01-08-200001_CreateOrganizationsClientsTable.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateOrganizationsClientsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'organization_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'phone' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
],
'notes' => [
'type' => 'TEXT',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
'deleted_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('organization_id');
$this->forge->addForeignKey('organization_id', 'organizations', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('organizations_clients');
}
public function down()
{
$this->forge->dropTable('organizations_clients');
}
}
// app/Database/Migrations/2026-01-07-053413_CreateOrganizationSubscriptionsTable.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateOrganizationSubscriptionsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'organization_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'module_code' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'status' => [
'type' => 'ENUM',
'constraint' => ['trial', 'active', 'expired', 'cancelled'],
'default' => 'trial',
],
'expires_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['organization_id', 'module_code']);
$this->forge->addForeignKey('organization_id', 'organizations', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('organization_subscriptions');
}
public function down()
{
$this->forge->dropTable('organization_subscriptions');
}
}
// app/Database/Migrations/2026-01-07-053401_CreateOrganizationsTable.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateOrganizationsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'owner_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'type' => [
'type' => 'ENUM',
'constraint' => ['business', 'personal'],
'default' => 'business',
],
'logo' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'requisites' => [
'type' => 'JSON',
'null' => true,
],
'trial_ends_at' => [
'type' => 'DATETIME',
'null' => true,
],
'settings' => [
'type' => 'JSON',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
'deleted_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('owner_id', 'users', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('organizations');
}
public function down()
{
$this->forge->dropTable('organizations');
}
}
// app/Database/Migrations/.gitkeep
// app/Database/Migrations/2026-01-07-053407_CreateOrganizationUsersTable.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateOrganizationUsersTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'organization_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'role' => [
'type' => 'ENUM',
'constraint' => ['owner', 'admin', 'manager', 'guest'],
'default' => 'manager',
],
'status' => [
'type' => 'ENUM',
'constraint' => ['active', 'invited', 'blocked'],
'default' => 'active',
],
'joined_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['organization_id', 'user_id']);
$this->forge->addForeignKey('organization_id', 'organizations', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('user_id', 'users', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('organization_users');
}
public function down()
{
$this->forge->dropTable('organization_users');
}
}
// app/Config/Generators.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Generators extends BaseConfig
{
/**
public array $views = [
'make:cell' => [
'class' => 'CodeIgniter\Commands\Generators\Views\cell.tpl.php',
'view' => 'CodeIgniter\Commands\Generators\Views\cell_view.tpl.php',
],
'make:command' => 'CodeIgniter\Commands\Generators\Views\command.tpl.php',
'make:config' => 'CodeIgniter\Commands\Generators\Views\config.tpl.php',
'make:controller' => 'CodeIgniter\Commands\Generators\Views\controller.tpl.php',
'make:entity' => 'CodeIgniter\Commands\Generators\Views\entity.tpl.php',
'make:filter' => 'CodeIgniter\Commands\Generators\Views\filter.tpl.php',
'make:migration' => 'CodeIgniter\Commands\Generators\Views\migration.tpl.php',
'make:model' => 'CodeIgniter\Commands\Generators\Views\model.tpl.php',
'make:seeder' => 'CodeIgniter\Commands\Generators\Views\seeder.tpl.php',
'make:validation' => 'CodeIgniter\Commands\Generators\Views\validation.tpl.php',
'session:migration' => 'CodeIgniter\Commands\Generators\Views\migration.tpl.php',
];
}
// app/Config/Cookie.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use DateTimeInterface;
class Cookie extends BaseConfig
{
/**
public string $prefix = '';
/**
public $expires = 0;
/**
public string $path = '/';
/**
public string $domain = '';
/**
public bool $secure = false;
/**
public bool $httponly = true;
/**
public string $samesite = 'Lax';
/**
public bool $raw = false;
}
// app/Config/Cache.php
<?php
namespace Config;
use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Cache\Handlers\DummyHandler;
use CodeIgniter\Cache\Handlers\FileHandler;
use CodeIgniter\Cache\Handlers\MemcachedHandler;
use CodeIgniter\Cache\Handlers\PredisHandler;
use CodeIgniter\Cache\Handlers\RedisHandler;
use CodeIgniter\Cache\Handlers\WincacheHandler;
use CodeIgniter\Config\BaseConfig;
class Cache extends BaseConfig
{
/**
public string $handler = 'file';
/**
public string $backupHandler = 'dummy';
/**
public string $prefix = '';
/**
public int $ttl = 60;
/**
public string $reservedCharacters = '{}()/\@:';
/**
public array $file = [
'storePath' => WRITEPATH . 'cache/',
'mode' => 0640,
];
/**
public array $memcached = [
'host' => '127.0.0.1',
'port' => 11211,
'weight' => 1,
'raw' => false,
];
/**
public array $redis = [
'host' => '127.0.0.1',
'password' => null,
'port' => 6379,
'timeout' => 0,
'database' => 0,
];
/**
public array $validHandlers = [
'dummy' => DummyHandler::class,
'file' => FileHandler::class,
'memcached' => MemcachedHandler::class,
'predis' => PredisHandler::class,
'redis' => RedisHandler::class,
'wincache' => WincacheHandler::class,
];
/**
public $cacheQueryString = false;
}
// app/Config/Images.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Images\Handlers\GDHandler;
use CodeIgniter\Images\Handlers\ImageMagickHandler;
class Images extends BaseConfig
{
/**
public string $defaultHandler = 'gd';
/**
public string $libraryPath = '/usr/local/bin/convert';
/**
public array $handlers = [
'gd' => GDHandler::class,
'imagick' => ImageMagickHandler::class,
];
}
// app/Config/ContentSecurityPolicy.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
class ContentSecurityPolicy extends BaseConfig
{
/**
public bool $reportOnly = false;
/**
public ?string $reportURI = null;
/**
public bool $upgradeInsecureRequests = false;
/**
public $defaultSrc;
/**
public $scriptSrc = 'self';
/**
public $styleSrc = 'self';
/**
public $imageSrc = 'self';
/**
public $baseURI;
/**
public $childSrc = 'self';
/**
public $connectSrc = 'self';
/**
public $fontSrc;
/**
public $formAction = 'self';
/**
public $frameAncestors;
/**
public $frameSrc;
/**
public $mediaSrc;
/**
public $objectSrc = 'self';
/**
public $manifestSrc;
/**
public $pluginTypes;
/**
public $sandbox;
/**
public string $styleNonceTag = '{csp-style-nonce}';
/**
public string $scriptNonceTag = '{csp-script-nonce}';
/**
public bool $autoNonce = true;
}
// app/Config/View.php
<?php
namespace Config;
use CodeIgniter\Config\View as BaseView;
use CodeIgniter\View\ViewDecoratorInterface;
/**
class View extends BaseView
{
/**
public $saveData = true;
/**
public $filters = [];
/**
public $plugins = [];
/**
public array $decorators = [];
}
// app/Config/CURLRequest.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class CURLRequest extends BaseConfig
{
/**
public bool $shareOptions = false;
}
// app/Config/UserAgents.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
class UserAgents extends BaseConfig
{
/**
public array $platforms = [
'windows nt 10.0' => 'Windows 10',
'windows nt 6.3' => 'Windows 8.1',
'windows nt 6.2' => 'Windows 8',
'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista',
'windows nt 5.2' => 'Windows 2003',
'windows nt 5.1' => 'Windows XP',
'windows nt 5.0' => 'Windows 2000',
'windows nt 4.0' => 'Windows NT 4.0',
'winnt4.0' => 'Windows NT 4.0',
'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT',
'windows 98' => 'Windows 98',
'win98' => 'Windows 98',
'windows 95' => 'Windows 95',
'win95' => 'Windows 95',
'windows phone' => 'Windows Phone',
'windows' => 'Unknown Windows OS',
'android' => 'Android',
'blackberry' => 'BlackBerry',
'iphone' => 'iOS',
'ipad' => 'iOS',
'ipod' => 'iOS',
'os x' => 'Mac OS X',
'ppc mac' => 'Power PC Mac',
'freebsd' => 'FreeBSD',
'ppc' => 'Macintosh',
'linux' => 'Linux',
'debian' => 'Debian',
'sunos' => 'Sun Solaris',
'beos' => 'BeOS',
'apachebench' => 'ApacheBench',
'aix' => 'AIX',
'irix' => 'Irix',
'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX',
'netbsd' => 'NetBSD',
'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD',
'gnu' => 'GNU/Linux',
'unix' => 'Unknown Unix OS',
'symbian' => 'Symbian OS',
];
/**
public array $browsers = [
'OPR' => 'Opera',
'Flock' => 'Flock',
'Edge' => 'Spartan',
'Edg' => 'Edge',
'Chrome' => 'Chrome',
'Opera.*?Version' => 'Opera',
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
'Trident.* rv' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird',
'Camino' => 'Camino',
'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb',
'Safari' => 'Safari',
'Mozilla' => 'Mozilla',
'Konqueror' => 'Konqueror',
'icab' => 'iCab',
'Lynx' => 'Lynx',
'Links' => 'Links',
'hotjava' => 'HotJava',
'amaya' => 'Amaya',
'IBrowse' => 'IBrowse',
'Maxthon' => 'Maxthon',
'Ubuntu' => 'Ubuntu Web Browser',
'Vivaldi' => 'Vivaldi',
];
/**
public array $mobiles = [
'mobileexplorer' => 'Mobile Explorer',
'palmsource' => 'Palm',
'palmscape' => 'Palmscape',
'motorola' => 'Motorola',
'nokia' => 'Nokia',
'palm' => 'Palm',
'iphone' => 'Apple iPhone',
'ipad' => 'iPad',
'ipod' => 'Apple iPod Touch',
'sony' => 'Sony Ericsson',
'ericsson' => 'Sony Ericsson',
'blackberry' => 'BlackBerry',
'cocoon' => 'O2 Cocoon',
'blazer' => 'Treo',
'lg' => 'LG',
'amoi' => 'Amoi',
'xda' => 'XDA',
'mda' => 'MDA',
'vario' => 'Vario',
'htc' => 'HTC',
'samsung' => 'Samsung',
'sharp' => 'Sharp',
'sie-' => 'Siemens',
'alcatel' => 'Alcatel',
'benq' => 'BenQ',
'ipaq' => 'HP iPaq',
'mot-' => 'Motorola',
'playstation portable' => 'PlayStation Portable',
'playstation 3' => 'PlayStation 3',
'playstation vita' => 'PlayStation Vita',
'hiptop' => 'Danger Hiptop',
'nec-' => 'NEC',
'panasonic' => 'Panasonic',
'philips' => 'Philips',
'sagem' => 'Sagem',
'sanyo' => 'Sanyo',
'spv' => 'SPV',
'zte' => 'ZTE',
'sendo' => 'Sendo',
'nintendo dsi' => 'Nintendo DSi',
'nintendo ds' => 'Nintendo DS',
'nintendo 3ds' => 'Nintendo 3DS',
'wii' => 'Nintendo Wii',
'open web' => 'Open Web',
'openweb' => 'OpenWeb',
'android' => 'Android',
'symbian' => 'Symbian',
'SymbianOS' => 'SymbianOS',
'elaine' => 'Palm',
'series60' => 'Symbian S60',
'windows ce' => 'Windows CE',
'obigo' => 'Obigo',
'netfront' => 'Netfront Browser',
'openwave' => 'Openwave Browser',
'mobilexplorer' => 'Mobile Explorer',
'operamini' => 'Opera Mini',
'opera mini' => 'Opera Mini',
'opera mobi' => 'Opera Mobile',
'fennec' => 'Firefox Mobile',
'digital paths' => 'Digital Paths',
'avantgo' => 'AvantGo',
'xiino' => 'Xiino',
'novarra' => 'Novarra Transcoder',
'vodafone' => 'Vodafone',
'docomo' => 'NTT DoCoMo',
'o2' => 'O2',
'mobile' => 'Generic Mobile',
'wireless' => 'Generic Mobile',
'j2me' => 'Generic Mobile',
'midp' => 'Generic Mobile',
'cldc' => 'Generic Mobile',
'up.link' => 'Generic Mobile',
'up.browser' => 'Generic Mobile',
'smartphone' => 'Generic Mobile',
'cellphone' => 'Generic Mobile',
];
/**
public array $robots = [
'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot',
'baiduspider' => 'Baiduspider',
'bingbot' => 'Bing',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'ask jeeves' => 'Ask Jeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos',
'yandex' => 'YandexBot',
'mediapartners-google' => 'MediaPartners Google',
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
'adsbot-google' => 'AdsBot Google',
'feedfetcher-google' => 'Feedfetcher Google',
'curious george' => 'Curious George',
'ia_archiver' => 'Alexa Crawler',
'MJ12bot' => 'Majestic-12',
'Uptimebot' => 'Uptimebot',
];
}
// app/Config/Constants.php
<?php
/*
| --------------------------------------------------------------------
| App Namespace
| --------------------------------------------------------------------
|
| This defines the default Namespace that is used throughout
| CodeIgniter to refer to the Application directory. Change
| this constant to change the namespace that all application
| classes should use.
|
| NOTE: changing this will require manually modifying the
| existing namespaces of App\* namespaced-classes.
defined('APP_NAMESPACE') || define('APP_NAMESPACE', 'App');
/*
| --------------------------------------------------------------------------
| Composer Path
| --------------------------------------------------------------------------
|
| The path that Composer's autoload file is expected to live. By default,
| the vendor folder is in the Root directory, but you can customize that here.
defined('COMPOSER_PATH') || define('COMPOSER_PATH', ROOTPATH . 'vendor/autoload.php');
/*
|--------------------------------------------------------------------------
| Timing Constants
|--------------------------------------------------------------------------
|
| Provide simple ways to work with the myriad of PHP functions that
| require information to be in seconds.
defined('SECOND') || define('SECOND', 1);
defined('MINUTE') || define('MINUTE', 60);
defined('HOUR') || define('HOUR', 3600);
defined('DAY') || define('DAY', 86400);
defined('WEEK') || define('WEEK', 604800);
defined('MONTH') || define('MONTH', 2_592_000);
defined('YEAR') || define('YEAR', 31_536_000);
defined('DECADE') || define('DECADE', 315_360_000);
/*
| --------------------------------------------------------------------------
| Exit Status Codes
| --------------------------------------------------------------------------
|
| Used to indicate the conditions under which the script is exit()ing.
| While there is no universal standard for error codes, there are some
| broad conventions. Three such conventions are mentioned below, for
| those who wish to make use of them. The CodeIgniter defaults were
| chosen for the least overlap with these conventions, while still
| leaving room for others to be defined in future versions and user
| applications.
|
| The three main conventions used for determining exit status codes
| are as follows:
|
| Standard C/C++ Library (stdlibc):
| http:
| (This link also contains other GNU-specific conventions)
| BSD sysexits.h:
| http:
| Bash scripting:
| http:
|
defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0);
defined('EXIT_ERROR') || define('EXIT_ERROR', 1);
defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3);
defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4);
defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5);
defined('EXIT_UNKNOWN_METHOD') || define('EXIT_UNKNOWN_METHOD', 6);
defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7);
defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8);
defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9);
defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125);
// app/Config/Autoload.php
<?php
namespace Config;
use CodeIgniter\Config\AutoloadConfig;
/**
class Autoload extends AutoloadConfig
{
/**
public $psr4 = [
APP_NAMESPACE => APPPATH,
'App\Modules' => APPPATH . 'Modules',
'App\Libraries\Twig' => APPPATH . 'Libraries/Twig',
];
/**
public $classmap = [];
/**
public $files = [];
/**
public $helpers = [];
}
// app/Config/Exceptions.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\ExceptionHandler;
use CodeIgniter\Debug\ExceptionHandlerInterface;
use Psr\Log\LogLevel;
use Throwable;
/**
class Exceptions extends BaseConfig
{
/**
public bool $log = true;
/**
public array $ignoreCodes = [404];
/**
public string $errorViewPath = APPPATH . 'Views/errors';
/**
public array $sensitiveDataInTrace = [];
/**
public bool $logDeprecations = true;
/**
public string $deprecationLogLevel = LogLevel::WARNING;
/*
public function handler(int $statusCode, Throwable $exception): ExceptionHandlerInterface
{
return new ExceptionHandler($this);
}
}
// app/Config/Services.php
<?php
namespace Config;
use CodeIgniter\Config\BaseService;
/**
class Services extends BaseService
{
/*
}
// app/Config/Session.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Session\Handlers\BaseHandler;
use CodeIgniter\Session\Handlers\FileHandler;
class Session extends BaseConfig
{
/**
public string $driver = FileHandler::class;
/**
public string $cookieName = 'ci_session';
/**
public int $expiration = 7200;
/**
public string $savePath = WRITEPATH . 'session';
/**
public bool $matchIP = false;
/**
public int $timeToUpdate = 300;
/**
public bool $regenerateDestroy = false;
/**
public ?string $DBGroup = null;
/**
public int $lockRetryInterval = 100_000;
/**
public int $lockMaxRetries = 300;
}
// app/Config/Format.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Format\JSONFormatter;
use CodeIgniter\Format\XMLFormatter;
class Format extends BaseConfig
{
/**
public array $supportedResponseFormats = [
'application/json',
'application/xml',
'text/xml',
];
/**
public array $formatters = [
'application/json' => JSONFormatter::class,
'application/xml' => XMLFormatter::class,
'text/xml' => XMLFormatter::class,
];
/**
public array $formatterOptions = [
'application/json' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
'application/xml' => 0,
'text/xml' => 0,
];
}
// app/Config/Cors.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
class Cors extends BaseConfig
{
/**
public array $default = [
/**
'allowedOrigins' => [],
/**
'allowedOriginsPatterns' => [],
/**
'supportsCredentials' => false,
/**
'allowedHeaders' => [],
/**
'exposedHeaders' => [],
/**
'allowedMethods' => [],
/**
'maxAge' => 7200,
];
}
// app/Config/Honeypot.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Honeypot extends BaseConfig
{
/**
public bool $hidden = true;
/**
public string $label = 'Fill This Field';
/**
public string $name = 'honeypot';
/**
public string $template = '<label>{label}</label><input type="text" name="{name}" value="">';
/**
public string $container = '<div style="display:none">{template}</div>';
/**
public string $containerId = 'hpc';
}
// app/Config/Mimes.php
<?php
namespace Config;
/**
class Mimes
{
/**
public static array $mimes = [
'hqx' => [
'application/mac-binhex40',
'application/mac-binhex',
'application/x-binhex40',
'application/x-mac-binhex40',
],
'cpt' => 'application/mac-compactpro',
'csv' => [
'text/csv',
'text/x-comma-separated-values',
'text/comma-separated-values',
'application/vnd.ms-excel',
'application/x-csv',
'text/x-csv',
'application/csv',
'application/excel',
'application/vnd.msexcel',
'text/plain',
],
'bin' => [
'application/macbinary',
'application/mac-binary',
'application/octet-stream',
'application/x-binary',
'application/x-macbinary',
],
'dms' => 'application/octet-stream',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'exe' => [
'application/octet-stream',
'application/vnd.microsoft.portable-executable',
'application/x-dosexec',
'application/x-msdownload',
],
'class' => 'application/octet-stream',
'psd' => [
'application/x-photoshop',
'image/vnd.adobe.photoshop',
],
'so' => 'application/octet-stream',
'sea' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'oda' => 'application/oda',
'pdf' => [
'application/pdf',
'application/force-download',
'application/x-download',
],
'ai' => [
'application/pdf',
'application/postscript',
],
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => [
'application/vnd.ms-excel',
'application/msexcel',
'application/x-msexcel',
'application/x-ms-excel',
'application/x-excel',
'application/x-dos_ms_excel',
'application/xls',
'application/x-xls',
'application/excel',
'application/download',
'application/vnd.ms-office',
'application/msword',
],
'ppt' => [
'application/vnd.ms-powerpoint',
'application/powerpoint',
'application/vnd.ms-office',
'application/msword',
],
'pptx' => [
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
],
'wbxml' => 'application/wbxml',
'wmlc' => 'application/wmlc',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar',
'gz' => 'application/x-gzip',
'gzip' => 'application/x-gzip',
'php' => [
'application/x-php',
'application/x-httpd-php',
'application/php',
'text/php',
'text/x-php',
'application/x-httpd-php-source',
],
'php4' => 'application/x-httpd-php',
'php3' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'js' => [
'application/x-javascript',
'text/plain',
],
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar',
'tgz' => [
'application/x-tar',
'application/x-gzip-compressed',
],
'z' => 'application/x-compress',
'xhtml' => 'application/xhtml+xml',
'xht' => 'application/xhtml+xml',
'zip' => [
'application/x-zip',
'application/zip',
'application/x-zip-compressed',
'application/s-compressed',
'multipart/x-zip',
],
'rar' => [
'application/vnd.rar',
'application/x-rar',
'application/rar',
'application/x-rar-compressed',
],
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => [
'audio/mpeg',
'audio/mpg',
'audio/mpeg3',
'audio/mp3',
],
'aif' => [
'audio/x-aiff',
'audio/aiff',
],
'aiff' => [
'audio/x-aiff',
'audio/aiff',
],
'aifc' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'rv' => 'video/vnd.rn-realvideo',
'wav' => [
'audio/x-wav',
'audio/wave',
'audio/wav',
],
'bmp' => [
'image/bmp',
'image/x-bmp',
'image/x-bitmap',
'image/x-xbitmap',
'image/x-win-bitmap',
'image/x-windows-bmp',
'image/ms-bmp',
'image/x-ms-bmp',
'application/bmp',
'application/x-bmp',
'application/x-win-bitmap',
],
'gif' => 'image/gif',
'jpg' => [
'image/jpeg',
'image/pjpeg',
],
'jpeg' => [
'image/jpeg',
'image/pjpeg',
],
'jpe' => [
'image/jpeg',
'image/pjpeg',
],
'jp2' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'j2k' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'jpf' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'jpg2' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'jpx' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'jpm' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'mj2' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'mjp2' => [
'image/jp2',
'video/mj2',
'image/jpx',
'image/jpm',
],
'png' => [
'image/png',
'image/x-png',
],
'webp' => 'image/webp',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'css' => [
'text/css',
'text/plain',
],
'html' => [
'text/html',
'text/plain',
],
'htm' => [
'text/html',
'text/plain',
],
'shtml' => [
'text/html',
'text/plain',
],
'txt' => 'text/plain',
'text' => 'text/plain',
'log' => [
'text/plain',
'text/x-log',
],
'rtx' => 'text/richtext',
'rtf' => 'text/rtf',
'xml' => [
'application/xml',
'text/xml',
'text/plain',
],
'xsl' => [
'application/xml',
'text/xsl',
'text/xml',
],
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'avi' => [
'video/x-msvideo',
'video/msvideo',
'video/avi',
'application/x-troff-msvideo',
],
'movie' => 'video/x-sgi-movie',
'doc' => [
'application/msword',
'application/vnd.ms-office',
],
'docx' => [
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/zip',
'application/msword',
'application/x-zip',
],
'dot' => [
'application/msword',
'application/vnd.ms-office',
],
'dotx' => [
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/zip',
'application/msword',
],
'xlsx' => [
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/zip',
'application/vnd.ms-excel',
'application/msword',
'application/x-zip',
],
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
'word' => [
'application/msword',
'application/octet-stream',
],
'xl' => 'application/excel',
'eml' => 'message/rfc822',
'json' => [
'application/json',
'text/json',
],
'pem' => [
'application/x-x509-user-cert',
'application/x-pem-file',
'application/octet-stream',
],
'p10' => [
'application/x-pkcs10',
'application/pkcs10',
],
'p12' => 'application/x-pkcs12',
'p7a' => 'application/x-pkcs7-signature',
'p7c' => [
'application/pkcs7-mime',
'application/x-pkcs7-mime',
],
'p7m' => [
'application/pkcs7-mime',
'application/x-pkcs7-mime',
],
'p7r' => 'application/x-pkcs7-certreqresp',
'p7s' => 'application/pkcs7-signature',
'crt' => [
'application/x-x509-ca-cert',
'application/x-x509-user-cert',
'application/pkix-cert',
],
'crl' => [
'application/pkix-crl',
'application/pkcs-crl',
],
'der' => 'application/x-x509-ca-cert',
'kdb' => 'application/octet-stream',
'pgp' => 'application/pgp',
'gpg' => 'application/gpg-keys',
'sst' => 'application/octet-stream',
'csr' => 'application/octet-stream',
'rsa' => 'application/x-pkcs7',
'cer' => [
'application/pkix-cert',
'application/x-x509-ca-cert',
],
'3g2' => 'video/3gpp2',
'3gp' => [
'video/3gp',
'video/3gpp',
],
'mp4' => 'video/mp4',
'm4a' => 'audio/x-m4a',
'f4v' => [
'video/mp4',
'video/x-f4v',
],
'flv' => 'video/x-flv',
'webm' => 'video/webm',
'aac' => 'audio/x-acc',
'm4u' => 'application/vnd.mpegurl',
'm3u' => 'text/plain',
'xspf' => 'application/xspf+xml',
'vlc' => 'application/videolan',
'wmv' => [
'video/x-ms-wmv',
'video/x-ms-asf',
],
'au' => 'audio/x-au',
'ac3' => 'audio/ac3',
'flac' => 'audio/x-flac',
'ogg' => [
'audio/ogg',
'video/ogg',
'application/ogg',
],
'kmz' => [
'application/vnd.google-earth.kmz',
'application/zip',
'application/x-zip',
],
'kml' => [
'application/vnd.google-earth.kml+xml',
'application/xml',
'text/xml',
],
'ics' => 'text/calendar',
'ical' => 'text/calendar',
'zsh' => 'text/x-scriptzsh',
'7zip' => [
'application/x-compressed',
'application/x-zip-compressed',
'application/zip',
'multipart/x-zip',
],
'cdr' => [
'application/cdr',
'application/coreldraw',
'application/x-cdr',
'application/x-coreldraw',
'image/cdr',
'image/x-cdr',
'zz-application/zz-winassoc-cdr',
],
'wma' => [
'audio/x-ms-wma',
'video/x-ms-asf',
],
'jar' => [
'application/java-archive',
'application/x-java-application',
'application/x-jar',
'application/x-compressed',
],
'svg' => [
'image/svg+xml',
'image/svg',
'application/xml',
'text/xml',
],
'vcf' => 'text/x-vcard',
'srt' => [
'text/srt',
'text/plain',
],
'vtt' => [
'text/vtt',
'text/plain',
],
'ico' => [
'image/x-icon',
'image/x-ico',
'image/vnd.microsoft.icon',
],
'stl' => [
'application/sla',
'application/vnd.ms-pki.stl',
'application/x-navistyle',
'model/stl',
'application/octet-stream',
],
];
/**
public static function guessTypeFromExtension(string $extension)
{
$extension = trim(strtolower($extension), '. ');
if (! array_key_exists($extension, static::$mimes)) {
return null;
}
return is_array(static::$mimes[$extension]) ? static::$mimes[$extension][0] : static::$mimes[$extension];
}
/**
public static function guessExtensionFromType(string $type, ?string $proposedExtension = null)
{
$type = trim(strtolower($type), '. ');
$proposedExtension = trim(strtolower($proposedExtension ?? ''));
if (
$proposedExtension !== ''
&& array_key_exists($proposedExtension, static::$mimes)
&& in_array($type, (array) static::$mimes[$proposedExtension], true)
) {
return $proposedExtension;
}
foreach (static::$mimes as $ext => $types) {
if (in_array($type, (array) $types, true)) {
return $ext;
}
}
return null;
}
}
// app/Config/Migrations.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Migrations extends BaseConfig
{
/**
public bool $enabled = true;
/**
public string $table = 'migrations';
/**
public string $timestampFormat = 'Y-m-d-His_';
}
// app/Config/Pager.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Pager extends BaseConfig
{
/**
public array $templates = [
'default_full' => 'App\Views\pager\bootstrap_full',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
'default_head' => 'CodeIgniter\Pager\Views\default_head',
];
/**
public int $perPage = 20;
}
// app/Config/Feature.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
class Feature extends BaseConfig
{
/**
public bool $autoRoutesImproved = true;
/**
public bool $oldFilterOrder = false;
/**
public bool $limitZeroAsAll = true;
/**
public bool $strictLocaleNegotiation = false;
}
// app/Config/Publisher.php
<?php
namespace Config;
use CodeIgniter\Config\Publisher as BasePublisher;
/**
class Publisher extends BasePublisher
{
/**
public $restrictions = [
ROOTPATH => '*',
FCPATH => '#\.(s?css|js|map|html?|xml|json|webmanifest|ttf|eot|woff2?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
];
}
// app/Config/ForeignCharacters.php
<?php
namespace Config;
use CodeIgniter\Config\ForeignCharacters as BaseForeignCharacters;
/**
class ForeignCharacters extends BaseForeignCharacters
{
}
// app/Config/Email.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Email extends BaseConfig
{
public string $fromEmail = '';
public string $fromName = '';
public string $recipients = '';
/**
public string $userAgent = 'CodeIgniter';
/**
public string $protocol = 'mail';
/**
public string $mailPath = '/usr/sbin/sendmail';
/**
public string $SMTPHost = '';
/**
public string $SMTPUser = '';
/**
public string $SMTPPass = '';
/**
public int $SMTPPort = 25;
/**
public int $SMTPTimeout = 5;
/**
public bool $SMTPKeepAlive = false;
/**
public string $SMTPCrypto = 'tls';
/**
public bool $wordWrap = true;
/**
public int $wrapChars = 76;
/**
public string $mailType = 'text';
/**
public string $charset = 'UTF-8';
/**
public bool $validate = false;
/**
public int $priority = 3;
/**
public string $CRLF = "\r\n";
/**
public string $newline = "\r\n";
/**
public bool $BCCBatchMode = false;
/**
public int $BCCBatchSize = 200;
/**
public bool $DSN = false;
}
// app/Config/Routes.php
<?php
use CodeIgniter\Router\RouteCollection;
/**
$routes->get('/', 'Home::index');
$routes->get('login', 'Auth::login');
$routes->post('login', 'Auth::login');
$routes->get('register', 'Auth::register');
$routes->post('register', 'Auth::register');
$routes->get('register/success', 'Auth::registerSuccess');
$routes->get('logout', 'Auth::logout');
$routes->get('auth/verify/(:any)', 'Auth::verify/$1');
$routes->get('auth/resend-verification', 'Auth::resendVerification');
$routes->post('auth/resend-verification', 'Auth::resendVerification');
$routes->group('', ['filter' => 'org'], static function ($routes) {
$routes->get('organizations', 'Organizations::index');
$routes->get('organizations/create', 'Organizations::create');
$routes->post('organizations/create', 'Organizations::create');
$routes->get('organizations/edit/(:num)', 'Organizations::edit/$1');
$routes->post('organizations/edit/(:num)', 'Organizations::edit/$1');
$routes->get('organizations/delete/(:num)', 'Organizations::delete/$1');
$routes->post('organizations/delete/(:num)', 'Organizations::delete/$1');
$routes->get('organizations/switch/(:num)', 'Organizations::switch/$1');
});
require_once APPPATH . 'Modules/Clients/Config/Routes.php';
// app/Config/Database.php
<?php
namespace Config;
use CodeIgniter\Database\Config;
/**
class Database extends Config
{
/**
public string $filesPath = APPPATH . 'Database' . DIRECTORY_SEPARATOR;
/**
public string $defaultGroup = 'default';
/**
public array $default = [
'DSN' => '',
'hostname' => 'localhost',
'username' => '',
'password' => '',
'database' => '',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => false,
'DBDebug' => true,
'charset' => 'utf8mb4',
'DBCollat' => 'utf8mb4_general_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
'failover' => [],
'port' => 3306,
'numberNative' => false,
'foundRows' => false,
'dateFormat' => [
'date' => 'Y-m-d',
'datetime' => 'Y-m-d H:i:s',
'time' => 'H:i:s',
],
];
/**
public array $tests = [
'DSN' => '',
'hostname' => '127.0.0.1',
'username' => '',
'password' => '',
'database' => ':memory:',
'DBDriver' => 'SQLite3',
'DBPrefix' => 'db_',
'pConnect' => false,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => '',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
'failover' => [],
'port' => 3306,
'foreignKeys' => true,
'busyTimeout' => 1000,
'synchronous' => null,
'dateFormat' => [
'date' => 'Y-m-d',
'datetime' => 'Y-m-d H:i:s',
'time' => 'H:i:s',
],
];
public function __construct()
{
parent::__construct();
if (ENVIRONMENT === 'testing') {
$this->defaultGroup = 'tests';
}
}
}
// app/Config/Paths.php
<?php
namespace Config;
/**
class Paths
{
/**
public string $systemDirectory = __DIR__ . '/../../vendor/codeigniter4/framework/system';
/**
public string $appDirectory = __DIR__ . '/..';
/**
public string $writableDirectory = __DIR__ . '/../../writable';
/**
public string $testsDirectory = __DIR__ . '/../../tests';
/**
public string $viewDirectory = __DIR__ . '/../Views';
}
// app/Config/Kint.php
<?php
namespace Config;
use Kint\Parser\ConstructablePluginInterface;
use Kint\Renderer\Rich\TabPluginInterface;
use Kint\Renderer\Rich\ValuePluginInterface;
/**
class Kint
{
/*
|--------------------------------------------------------------------------
| Global Settings
|--------------------------------------------------------------------------
/**
public $plugins;
public int $maxDepth = 6;
public bool $displayCalledFrom = true;
public bool $expanded = false;
/*
|--------------------------------------------------------------------------
| RichRenderer Settings
|--------------------------------------------------------------------------
public string $richTheme = 'aante-light.css';
public bool $richFolder = false;
/**
public $richObjectPlugins;
/**
public $richTabPlugins;
/*
|--------------------------------------------------------------------------
| CLI Settings
|--------------------------------------------------------------------------
public bool $cliColors = true;
public bool $cliForceUTF8 = false;
public bool $cliDetectWidth = true;
public int $cliMinWidth = 40;
}
// app/Config/Routing.php
<?php
/**
namespace Config;
use CodeIgniter\Config\Routing as BaseRouting;
/**
class Routing extends BaseRouting
{
/**
public array $routeFiles = [
APPPATH . 'Config/Routes.php',
];
/**
public string $defaultNamespace = 'App\Controllers';
/**
public string $defaultController = 'Home';
/**
public string $defaultMethod = 'index';
/**
public bool $translateURIDashes = false;
/**
public ?string $override404 = null;
/**
public bool $autoRoute = false;
/**
public bool $prioritize = false;
/**
public bool $multipleSegmentsOneParam = false;
/**
public array $moduleRoutes = [];
/**
public bool $translateUriToCamelCase = true;
}
// app/Config/Logger.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Log\Handlers\FileHandler;
use CodeIgniter\Log\Handlers\HandlerInterface;
class Logger extends BaseConfig
{
/**
public $threshold = (ENVIRONMENT === 'production') ? 4 : 9;
/**
public string $dateFormat = 'Y-m-d H:i:s';
/**
public array $handlers = [
/*
FileHandler::class => [
'handles' => [
'critical',
'alert',
'emergency',
'debug',
'error',
'info',
'notice',
'warning',
],
/*
'fileExtension' => '',
/*
'filePermissions' => 0644,
/*
'path' => '',
],
/*
/*
];
}
// app/Config/Events.php
<?php
namespace Config;
use CodeIgniter\Events\Events;
use CodeIgniter\Exceptions\FrameworkException;
use CodeIgniter\HotReloader\HotReloader;
/*
Events::on('pre_system', static function (): void {
if (ENVIRONMENT !== 'testing') {
if (ini_get('zlib.output_compression')) {
throw FrameworkException::forEnabledZlibOutputCompression();
}
while (ob_get_level() > 0) {
ob_end_flush();
}
ob_start(static fn ($buffer) => $buffer);
}
/*
if (CI_DEBUG && ! is_cli()) {
Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect');
service('toolbar')->respond();
if (ENVIRONMENT === 'development') {
service('routes')->get('__hot-reload', static function (): void {
(new HotReloader())->run();
});
}
}
});
// app/Config/DocTypes.php
<?php
namespace Config;
class DocTypes
{
/**
public array $list = [
'xhtml11' => '<!DOCTYPE html PUBLIC "-
'xhtml1-strict' => '<!DOCTYPE html PUBLIC "-
'xhtml1-trans' => '<!DOCTYPE html PUBLIC "-
'xhtml1-frame' => '<!DOCTYPE html PUBLIC "-
'xhtml-basic11' => '<!DOCTYPE html PUBLIC "-
'html5' => '<!DOCTYPE html>',
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-
'mathml1' => '<!DOCTYPE math SYSTEM "http:
'mathml2' => '<!DOCTYPE math PUBLIC "-
'svg10' => '<!DOCTYPE svg PUBLIC "-
'svg11' => '<!DOCTYPE svg PUBLIC "-
'svg11-basic' => '<!DOCTYPE svg PUBLIC "-
'svg11-tiny' => '<!DOCTYPE svg PUBLIC "-
'xhtml-math-svg-xh' => '<!DOCTYPE html PUBLIC "-
'xhtml-math-svg-sh' => '<!DOCTYPE svg:svg PUBLIC "-
'xhtml-rdfa-1' => '<!DOCTYPE html PUBLIC "-
'xhtml-rdfa-2' => '<!DOCTYPE html PUBLIC "-
];
/**
public bool $html5 = true;
}
// app/Config/Security.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Security extends BaseConfig
{
/**
public string $csrfProtection = 'cookie';
/**
public bool $tokenRandomize = false;
/**
public string $tokenName = 'csrf_bp';
/**
public string $headerName = 'X-CSRF-TOKEN';
/**
public string $cookieName = 'csrf_cookie_name';
/**
public int $expires = 7200;
/**
public bool $regenerate = true;
/**
public bool $redirect = (ENVIRONMENT === 'production');
}
// app/Config/Encryption.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
class Encryption extends BaseConfig
{
/**
public string $key = '';
/**
public string $driver = 'OpenSSL';
/**
public int $blockSize = 16;
/**
public string $digest = 'SHA512';
/**
public bool $rawData = true;
/**
public string $encryptKeyInfo = '';
/**
public string $authKeyInfo = '';
/**
public string $cipher = 'AES-256-CTR';
}
// app/Config/Toolbar.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\Toolbar\Collectors\Database;
use CodeIgniter\Debug\Toolbar\Collectors\Events;
use CodeIgniter\Debug\Toolbar\Collectors\Files;
use CodeIgniter\Debug\Toolbar\Collectors\Logs;
use CodeIgniter\Debug\Toolbar\Collectors\Routes;
use CodeIgniter\Debug\Toolbar\Collectors\Timers;
use CodeIgniter\Debug\Toolbar\Collectors\Views;
/**
class Toolbar extends BaseConfig
{
/**
public array $collectors = [
Timers::class,
Database::class,
Logs::class,
Views::class,
Files::class,
Routes::class,
Events::class,
];
/**
public bool $collectVarData = true;
/**
public int $maxHistory = 20;
/**
public string $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/';
/**
public int $maxQueries = 100;
/**
public array $watchedDirectories = [
'app',
];
/**
public array $watchedExtensions = [
'php', 'css', 'js', 'html', 'svg', 'json', 'env',
];
}
// app/Config/Optimize.php
<?php
namespace Config;
/**
class Optimize
{
/**
public bool $configCacheEnabled = false;
/**
public bool $locatorCacheEnabled = false;
}
// app/Config/Boot/testing.php
<?php
/*
/*
|--------------------------------------------------------------------------
| ERROR DISPLAY
|--------------------------------------------------------------------------
| In development, we want to show as many errors as possible to help
| make sure they don't make it to production. And save us hours of
| painful debugging.
error_reporting(E_ALL);
ini_set('display_errors', '1');
/*
|--------------------------------------------------------------------------
| DEBUG BACKTRACES
|--------------------------------------------------------------------------
| If true, this constant will tell the error screens to display debug
| backtraces along with the other error information. If you would
| prefer to not see this, set this value to false.
defined('SHOW_DEBUG_BACKTRACE') || define('SHOW_DEBUG_BACKTRACE', true);
/*
|--------------------------------------------------------------------------
| DEBUG MODE
|--------------------------------------------------------------------------
| Debug mode is an experimental flag that can allow changes throughout
| the system. It's not widely used currently, and may not survive
| release of the framework.
defined('CI_DEBUG') || define('CI_DEBUG', true);
// app/Config/Boot/production.php
<?php
/*
|--------------------------------------------------------------------------
| ERROR DISPLAY
|--------------------------------------------------------------------------
| Don't show ANY in production environments. Instead, let the system catch
| it and display a generic error message.
|
| If you set 'display_errors' to '1', CI4's detailed error report will show.
error_reporting(E_ALL & ~E_DEPRECATED);
ini_set('display_errors', '0');
/*
|--------------------------------------------------------------------------
| DEBUG MODE
|--------------------------------------------------------------------------
| Debug mode is an experimental flag that can allow changes throughout
| the system. It's not widely used currently, and may not survive
| release of the framework.
defined('CI_DEBUG') || define('CI_DEBUG', false);
// app/Config/Boot/development.php
<?php
/*
|--------------------------------------------------------------------------
| ERROR DISPLAY
|--------------------------------------------------------------------------
| In development, we want to show as many errors as possible to help
| make sure they don't make it to production. And save us hours of
| painful debugging.
|
| If you set 'display_errors' to '1', CI4's detailed error report will show.
error_reporting(E_ALL);
ini_set('display_errors', '1');
/*
|--------------------------------------------------------------------------
| DEBUG BACKTRACES
|--------------------------------------------------------------------------
| If true, this constant will tell the error screens to display debug
| backtraces along with the other error information. If you would
| prefer to not see this, set this value to false.
defined('SHOW_DEBUG_BACKTRACE') || define('SHOW_DEBUG_BACKTRACE', true);
/*
|--------------------------------------------------------------------------
| DEBUG MODE
|--------------------------------------------------------------------------
| Debug mode is an experimental flag that can allow changes throughout
| the system. This will control whether Kint is loaded, and a few other
| items. It can always be used within your own application too.
defined('CI_DEBUG') || define('CI_DEBUG', true);
// app/Config/Filters.php
<?php
namespace Config;
use CodeIgniter\Config\Filters as BaseFilters;
use CodeIgniter\Filters\Cors;
use CodeIgniter\Filters\CSRF;
use CodeIgniter\Filters\DebugToolbar;
use CodeIgniter\Filters\ForceHTTPS;
use CodeIgniter\Filters\Honeypot;
use CodeIgniter\Filters\InvalidChars;
use CodeIgniter\Filters\PageCache;
use CodeIgniter\Filters\PerformanceMetrics;
use CodeIgniter\Filters\SecureHeaders;
class Filters extends BaseFilters
{
/**
public array $aliases = [
'csrf' => CSRF::class,
'toolbar' => DebugToolbar::class,
'honeypot' => Honeypot::class,
'invalidchars' => InvalidChars::class,
'secureheaders' => SecureHeaders::class,
'cors' => Cors::class,
'forcehttps' => ForceHTTPS::class,
'pagecache' => PageCache::class,
'performance' => PerformanceMetrics::class,
'org' => \App\Filters\OrganizationFilter::class,
];
/**
public array $required = [
'before' => [
'forcehttps',
'pagecache',
],
'after' => [
'pagecache',
'performance',
'toolbar',
],
];
/**
public array $globals = [
'before' => [
'csrf',
],
'after' => [
],
];
/**
public array $methods = [];
/**
public array $filters = [];
}
// app/Config/App.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class App extends BaseConfig
{
/**
public string $baseURL = 'http:
/**
public array $allowedHostnames = [];
/**
public string $indexPage = '';
/**
public string $uriProtocol = 'REQUEST_URI';
/*
|--------------------------------------------------------------------------
| Allowed URL Characters
|--------------------------------------------------------------------------
|
| This lets you specify which characters are permitted within your URLs.
| When someone tries to submit a URL with disallowed characters they will
| get a warning message.
|
| As a security measure you are STRONGLY encouraged to restrict URLs to
| as few characters as possible.
|
| By default, only these are allowed: `a-z 0-9~%.:_-`
|
| Set an empty string to allow all characters -- but only if you are insane.
|
| The configured value is actually a regular expression character group
| and it will be used as: '/\A[<permittedURIChars>]+\z/iu'
|
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
public string $permittedURIChars = 'a-z 0-9~%.:_\-';
/**
public string $defaultLocale = 'en';
/**
public bool $negotiateLocale = false;
/**
public array $supportedLocales = ['en'];
/**
public string $appTimezone = 'UTC';
/**
public string $charset = 'UTF-8';
/**
public bool $forceGlobalSecureRequests = false;
/**
public array $proxyIPs = [];
/**
public bool $CSPEnabled = false;
}
// app/Config/Validation.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Validation\StrictRules\CreditCardRules;
use CodeIgniter\Validation\StrictRules\FileRules;
use CodeIgniter\Validation\StrictRules\FormatRules;
use CodeIgniter\Validation\StrictRules\Rules;
class Validation extends BaseConfig
{
/**
public array $ruleSets = [
Rules::class,
FormatRules::class,
FileRules::class,
CreditCardRules::class,
];
/**
public array $templates = [
'list' => 'CodeIgniter\Validation\Views\list',
'single' => 'CodeIgniter\Validation\Views\single',
];
}
// app/Config/Modules.php
<?php
namespace Config;
use CodeIgniter\Modules\Modules as BaseModules;
/**
class Modules extends BaseModules
{
/**
public $enabled = true;
/**
public $discoverInComposer = true;
/**
public $composerPackages = [];
/**
public $aliases = [
'events',
'filters',
'registrars',
'routes',
'services',
];
}
// app/Config/Twig.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Twig extends \Daycry\Twig\Config\Twig
{
public string $extension = '.twig';
/**
public string $cachePath = WRITEPATH . 'cache/twig';
/**
public array $functions_safe = ['form_open', 'form_close', 'form_hidden', 'form_error', 'json_decode', 'set_value', 'csrf_field'];
/**
public array $functions_asis = ['session', 'current_url', 'base_url', 'site_url'];
/**
public array $paths = [
APPPATH . 'Views',
[APPPATH . 'Views/components', 'components'],
[APPPATH . 'Modules/Clients/Views', 'Clients']
];
/**
public array $filters = [];
/**
public array $extensions = [
\App\Libraries\Twig\TwigJsonDecodeExtension::class,
\App\Libraries\Twig\TwigGlobalsExtension::class,
];
/**
public bool $strictVariables = false;
/**
public bool $saveData = true;
/**
public bool $leanMode = false;
/**
public ?bool $enableDiscoverySnapshot = null;
/**
public ?bool $enableWarmupSummary = null;
/**
public ?bool $enableInvalidationHistory = null;
/**
public ?bool $enableDynamicMetrics = null;
/**
public ?bool $enableExtendedDiagnostics = null;
/**
public int $cacheTtl = 0;
/**
public bool $toolbarMinimal = false;
public bool $toolbarShowTemplates = true;
public int $toolbarMaxTemplates = 50;
public bool $toolbarShowCapabilities = true;
public bool $toolbarShowPersistence = true;
}
// app/ThirdParty/.gitkeep
// app/Modules/Clients/Views/form.twig
{% extends 'layouts/base.twig' %}
{% import 'macros/forms.twig' as forms %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<div class="d-flex align-items-center">
<a href="{{ base_url('/clients') }}" class="btn btn-outline-secondary me-3">
<i class="fa-solid fa-arrow-left"></i>
</a>
<div>
<h1 class="h4 mb-0">{{ title }}</h1>
</div>
</div>
</div>
<div class="card-body">
{{ forms.form_open(client ? base_url('/clients/update/' ~ client.id) : base_url('/clients/create')) }}
<div class="mb-3">
<label for="name" class="form-label fw-bold">Имя / Название *</label>
<input type="text" name="name" id="name" class="form-control {{ errors.name ? 'is-invalid' : '' }}"
value="{{ old.name ?? client.name ?? '' }}" required autofocus>
{% if errors.name %}
<div class="invalid-feedback">{{ errors.name }}</div>
{% endif %}
<div class="form-text">ФИО клиента или название компании</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" id="email" class="form-control {{ errors.email ? 'is-invalid' : '' }}"
value="{{ old.email ?? client.email ?? '' }}">
{% if errors.email %}
<div class="invalid-feedback">{{ errors.email }}</div>
{% endif %}
</div>
<div class="col-md-6 mb-3">
<label for="phone" class="form-label">Телефон</label>
<input type="tel" name="phone" id="phone" class="form-control {{ errors.phone ? 'is-invalid' : '' }}"
value="{{ old.phone ?? client.phone ?? '' }}">
{% if errors.phone %}
<div class="invalid-feedback">{{ errors.phone }}</div>
{% endif %}
</div>
</div>
<div class="mb-4">
<label for="notes" class="form-label">Заметки</label>
<textarea name="notes" id="notes" rows="4" class="form-control {{ errors.notes ? 'is-invalid' : '' }}"
placeholder="Дополнительная информация о клиенте...">{{ old.notes ?? client.notes ?? '' }}</textarea>
{% if errors.notes %}
<div class="invalid-feedback">{{ errors.notes }}</div>
{% endif %}
</div>
<div class="d-flex justify-content-end gap-2">
<a href="{{ base_url('/clients') }}" class="btn btn-secondary">Отмена</a>
<button type="submit" class="btn btn-primary">
<i class="fa-solid fa-check me-2"></i>
{{ client ? 'Сохранить изменения' : 'Добавить клиента' }}
</button>
</div>
{{ forms.form_close() }}
</div>
</div>
</div>
</div>
{% endblock %}
// app/Modules/Clients/Views/_table.twig
{# app/Modules/Clients/Views/_table.twig #}
{# Определяем тип запроса: AJAX = только tbody + footer #}
{% set isAjax = app.request.headers.get('X-Requested-With') == 'XMLHttpRequest' %}
{# Настройки пагинации #}
{% if pager %}
{% set pagination = {
currentPage: pager.getCurrentPage()|default(1),
totalPages: pager.getPageCount()|default(1),
totalRecords: pager.getTotal()|default(0),
perPage: perPage|default(10),
from: ((pager.getCurrentPage() - 1) * perPage + 1)|default(0),
to: min(pager.getCurrentPage() * perPage, pager.getTotal())|default(0)
} %}
{% else %}
{% set pagination = {
currentPage: 1,
totalPages: 1,
totalRecords: clients|length|default(0),
perPage: perPage|default(10),
from: 1,
to: clients|length|default(0)
} %}
{% endif %}
{# Проверка на пустое состояние #}
{% set isEmpty = clients is empty or clients|length == 0 %}
{# AJAX запрос - tbody + footer #}
<tbody>
{% if isEmpty %}
<tr>
<td colspan="4" class="text-center py-5">
<i class="fa-solid fa-users text-muted mb-3" style="font-size: 3rem;"></i>
<p class="text-muted mb-3">
{% if filters.name or filters.email or filters.phone %}
Клиенты не найдены
{% else %}
Клиентов пока нет
{% endif %}
</p>
<a href="{{ base_url('/clients/new') }}" class="btn btn-primary">
<i class="fa-solid fa-plus me-2"></i>Добавить клиента
</a>
</td>
</tr>
{% else %}
{% for client in clients %}
<tr>
<td>
<div class="d-flex align-items-center">
<div class="bg-primary text-white rounded-circle d-flex justify-content-center align-items-center me-3" style="width:40px;height:40px;">
{{ client.name|first|upper }}
</div>
<div>
<strong>{{ client.name }}</strong>
{% if client.notes %}
<br><small class="text-muted">{{ client.notes|slice(0, 50) }}{{ client.notes|length > 50 ? '...' : '' }}</small>
{% endif %}
</div>
</div>
</td>
<td>
{% if client.email %}
<a href="mailto:{{ client.email }}">{{ client.email }}</a>
{% else %}
<span class="text-muted">—</span>
{% endif %}
</td>
<td>
{% if client.phone %}
<a href="tel:{{ client.phone }}">{{ client.phone }}</a>
{% else %}
<span class="text-muted">—</span>
{% endif %}
</td>
<td class="text-end">
<a href="{{ base_url('/clients/edit/' ~ client.id) }}" class="btn btn-sm btn-outline-primary" title="Редактировать">
<i class="fa-solid fa-pen"></i>
</a>
<a href="{{ base_url('/clients/delete/' ~ client.id) }}" class="btn btn-sm btn-outline-danger" title="Удалить" onclick="return confirm('Вы уверены что хотите удалить этого клиента?');">
<i class="fa-solid fa-trash"></i>
</a>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
{% if not isEmpty and pager %}
<tfoot>
<tr>
<td colspan="4" class="p-0 border-0">
{{ include('@components/table/pagination.twig', { pagination: pagination, id: 'clients-table' }) }}
</td>
</tr>
</tfoot>
{% endif %}
// app/Modules/Clients/Views/index.twig
{% extends 'layouts/base.twig' %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="h3 mb-0">Клиенты</h1>
<p class="text-muted mb-0">Управление клиентами вашей организации</p>
</div>
<a href="{{ base_url('/clients/new') }}" class="btn btn-primary">
<i class="fa-solid fa-plus me-2"></i>Добавить клиента
</a>
</div>
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<div class="d-flex align-items-center justify-content-between">
<div class="text-muted small">
Нажмите на <i class="fa-solid fa-search text-muted"></i> для поиска по столбцу
</div>
</div>
</div>
{# Формируем строки таблицы из клиентов #}
{% set tableRows = [] %}
{% if clients is defined and clients|length > 0 %}
{% for client in clients %}
{% set tableRows = tableRows|merge([{
cells: [
{
content: '<div class="d-flex align-items-center">
<div class="bg-primary text-white rounded-circle d-flex justify-content-center align-items-center me-3" style="width:40px;height:40px;">' ~ client.name|first|upper ~ '</div>
<div><strong>' ~ client.name ~ '</strong>' ~ (client.notes ? '<br><small class="text-muted">' ~ client.notes|slice(0, 50) ~ (client.notes|length > 50 ? '...' : '') ~ '</small>') ~ '</div>
</div>',
class: ''
},
{
content: client.email ? '<a href="mailto:' ~ client.email ~ '">' ~ client.email ~ '</a>' : '<span class="text-muted">—</span>',
class: ''
},
{
content: client.phone ? '<a href="tel:' ~ client.phone ~ '">' ~ client.phone ~ '</a>' : '<span class="text-muted">—</span>',
class: ''
}
],
actions: '<a href="' ~ base_url('/clients/edit/' ~ client.id) ~ '" class="btn btn-sm btn-outline-primary" title="Редактировать"><i class="fa-solid fa-pen"></i></a>
<a href="' ~ base_url('/clients/delete/' ~ client.id) ~ '" class="btn btn-sm btn-outline-danger" title="Удалить" onclick="return confirm(\'Вы уверены что хотите удалить этого клиента?\');"><i class="fa-solid fa-trash"></i></a>'
}]) %}
{% endfor %}
{% endif %}
<div id="clients-table">
{{ include('@components/table/table.twig', {
id: 'clients-table',
url: '/clients/table',
perPage: perPage|default(10),
sort: sort|default(''),
order: order|default('asc'),
filters: filters|default({}),
columns: {
name: { label: 'Имя / Название', width: '40%' },
email: { label: 'Email', width: '25%' },
phone: { label: 'Телефон', width: '20%' }
},
rows: tableRows,
pagerDetails: {
currentPage: pagerDetails.currentPage|default(1),
pageCount: pagerDetails.pageCount|default(1),
total: pagerDetails.total|default(0),
perPage: perPage|default(10),
from: pagerDetails.from|default(1),
to: pagerDetails.to|default(clients|length|default(0))
},
actions: { label: 'Действия', width: '15%' },
emptyMessage: 'Клиентов пока нет'
}) }}
{# CSRF токен для AJAX запросов #}
{{ csrf_field()|raw }}
</div>
</div>
{% endblock %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="/assets/css/modules/data-table.css">
{% endblock %}
{% block scripts %}
{{ parent() }}
<script src="/assets/js/modules/DataTable.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.data-table').forEach(function(container) {
const id = container.id;
const url = container.dataset.url;
const perPage = parseInt(container.dataset.perPage) || 10;
if (window.dataTables && window.dataTables[id]) {
return;
}
const table = new DataTable(id, {
url: url,
perPage: perPage
});
window.dataTables = window.dataTables || {};
window.dataTables[id] = table;
});
});
</script>
{% endblock %}
// app/Modules/Clients/Models/ClientModel.php
<?php
namespace App\Modules\Clients\Models;
use CodeIgniter\Model;
class ClientModel extends Model
{
protected $table = 'organizations_clients';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['organization_id', 'name', 'email', 'phone', 'notes'];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
/**
public function forOrganization(int $organizationId)
{
return $this->where('organization_id', $organizationId);
}
/**
public function search(int $organizationId, string $query = '')
{
$builder = $this->forOrganization($organizationId);
if (!empty($query)) {
$builder->groupStart()
->like('name', $query)
->orLike('email', $query)
->orLike('phone', $query)
->groupEnd();
}
return $builder;
}
}
// app/Modules/Clients/Config/Routes.php
<?php
$routes->group('clients', ['filter' => 'org', 'namespace' => 'App\Modules\Clients\Controllers'], static function ($routes) {
$routes->get('/', 'Clients::index');
$routes->get('table', 'Clients::table');
$routes->get('new', 'Clients::new');
$routes->post('create', 'Clients::create');
$routes->get('edit/(:num)', 'Clients::edit/$1');
$routes->post('update/(:num)', 'Clients::update/$1');
$routes->get('delete/(:num)', 'Clients::delete/$1');
});
// app/Modules/Clients/Controllers/Clients.php
<?php
namespace App\Modules\Clients\Controllers;
use App\Controllers\BaseController;
use App\Modules\Clients\Models\ClientModel;
class Clients extends BaseController
{
protected $clientModel;
public function __construct()
{
$this->clientModel = new ClientModel();
}
public function index()
{
$config = $this->getTableConfig();
$data = $this->prepareTableData($config);
$data['title'] = 'Клиенты';
return $this->renderTwig($config['viewPath'], $data);
}
/**
protected function getTableConfig(): array
{
$organizationId = session()->get('active_org_id');
return [
'model' => $this->clientModel,
'columns' => [
'name' => ['label' => 'Имя / Название', 'width' => '40%'],
'email' => ['label' => 'Email', 'width' => '25%'],
'phone' => ['label' => 'Телефон', 'width' => '20%'],
],
'searchable' => ['name', 'email', 'phone'],
'sortable' => ['name', 'email', 'phone', 'created_at'],
'defaultSort' => 'name',
'order' => 'asc',
'viewPath' => '@Clients/index',
'partialPath' => '@Clients/_table',
'itemsKey' => 'clients',
'scope' => function ($builder) use ($organizationId) {
$builder->where('organization_id', $organizationId);
},
];
}
public function table()
{
return parent::table();
}
public function new()
{
$data = [
'title' => 'Добавить клиента',
'client' => null,
];
return $this->renderTwig('@Clients/form', $data);
}
public function create()
{
$organizationId = session()->get('active_org_id');
$rules = [
'name' => 'required|min_length[2]|max_length[255]',
'email' => 'permit_empty|valid_email',
'phone' => 'permit_empty|max_length[50]',
];
if (!$this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$this->clientModel->insert([
'organization_id' => $organizationId,
'name' => $this->request->getPost('name'),
'email' => $this->request->getPost('email') ?? null,
'phone' => $this->request->getPost('phone') ?? null,
'notes' => $this->request->getPost('notes') ?? null,
]);
if ($this->clientModel->errors()) {
return redirect()->back()->withInput()->with('error', 'Ошибка при создании клиента');
}
session()->setFlashdata('success', 'Клиент успешно добавлен');
return redirect()->to('/clients');
}
public function edit($id)
{
$organizationId = session()->get('active_org_id');
$client = $this->clientModel
->where('id', $id)
->where('organization_id', $organizationId)
->first();
if (!$client) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Клиент не найден');
}
$data = [
'title' => 'Редактировать клиента',
'client' => $client,
];
return $this->renderTwig('@Clients/form', $data);
}
public function update($id)
{
$organizationId = session()->get('active_org_id');
$client = $this->clientModel
->where('id', $id)
->where('organization_id', $organizationId)
->first();
if (!$client) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Клиент не найден');
}
$rules = [
'name' => 'required|min_length[2]|max_length[255]',
'email' => 'permit_empty|valid_email',
'phone' => 'permit_empty|max_length[50]',
];
if (!$this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$this->clientModel->update($id, [
'name' => $this->request->getPost('name'),
'email' => $this->request->getPost('email') ?? null,
'phone' => $this->request->getPost('phone') ?? null,
'notes' => $this->request->getPost('notes') ?? null,
]);
if ($this->clientModel->errors()) {
return redirect()->back()->withInput()->with('error', 'Ошибка при обновлении клиента');
}
session()->setFlashdata('success', 'Клиент успешно обновлён');
return redirect()->to('/clients');
}
public function delete($id)
{
$organizationId = session()->get('active_org_id');
$client = $this->clientModel
->where('id', $id)
->where('organization_id', $organizationId)
->first();
if (!$client) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Клиент не найден');
}
$this->clientModel->delete($id);
session()->setFlashdata('success', 'Клиент удалён');
return redirect()->to('/clients');
}
}
// app/Controllers/Auth.php
<?php
namespace App\Controllers;
use App\Models\UserModel;
use App\Models\OrganizationModel;
use App\Models\OrganizationUserModel;
use App\Libraries\EmailLibrary;
class Auth extends BaseController
{
protected $emailLibrary;
public function __construct()
{
$this->emailLibrary = new EmailLibrary();
}
public function register()
{
if ($this->request->getMethod() === 'POST') {
log_message('debug', 'POST запрос получен: ' . print_r($this->request->getPost(), true));
$rules = [
'name' => 'required|min_length[3]',
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|min_length[6]',
];
if (!$this->validate($rules)) {
return redirect()->back()->with('error', 'Ошибка регистрации');
}
$userModel = new UserModel();
$orgModel = new OrganizationModel();
$orgUserModel = new OrganizationUserModel();
$verificationToken = bin2hex(random_bytes(32));
$userData = [
'name' => $this->request->getPost('name'),
'email' => $this->request->getPost('email'),
'password' => $this->request->getPost('password'),
'verification_token' => $verificationToken,
'email_verified' => 0,
];
log_message('debug', 'Registration userData: ' . print_r($userData, true));
$userId = $userModel->insert($userData);
log_message('debug', 'Insert result, userId: ' . $userId);
$orgData = [
'owner_id' => $userId,
'name' => 'Личное пространство',
'type' => 'personal',
];
$orgId = $orgModel->insert($orgData);
$orgUserModel->insert([
'organization_id' => $orgId,
'user_id' => $userId,
'role' => 'owner',
'status' => 'active',
'joined_at' => date('Y-m-d H:i:s'),
]);
$this->emailLibrary->sendVerificationEmail(
$userData['email'],
$userData['name'],
$verificationToken
);
session()->setFlashdata('success', 'Регистрация успешна! Пожалуйста, проверьте вашу почту и подтвердите email.');
return redirect()->to('/register/success');
}
return $this->renderTwig('auth/register');
}
/**
public function registerSuccess()
{
return $this->renderTwig('auth/register_success');
}
/**
public function verify($token)
{
log_message('debug', 'Verify called with token: ' . $token);
if (empty($token)) {
return $this->renderTwig('auth/verify_error', [
'message' => 'Отсутствует токен подтверждения.'
]);
}
$userModel = new UserModel();
$user = $userModel->where('verification_token', $token)->first();
log_message('debug', 'User found: ' . ($user ? 'yes' : 'no'));
if ($user) {
log_message('debug', 'User email_verified: ' . $user['email_verified']);
}
if (!$user) {
return $this->renderTwig('auth/verify_error', [
'message' => 'Недействительная ссылка для подтверждения. Возможно, ссылка уже была использована или истек срок её действия.'
]);
}
if ($user['email_verified']) {
return $this->renderTwig('auth/verify_error', [
'message' => 'Email уже подтверждён. Вы можете войти в систему.'
]);
}
$updateData = [
'email_verified' => 1,
'verified_at' => date('Y-m-d H:i:s'),
'verification_token' => null,
];
$result = $userModel->update($user['id'], $updateData);
log_message('debug', 'Update result: ' . ($result ? 'success' : 'failed'));
log_message('debug', 'Update data: ' . print_r($updateData, true));
if (!$result) {
log_message('error', 'Update errors: ' . print_r($userModel->errors(), true));
}
$this->emailLibrary->sendWelcomeEmail($user['email'], $user['name']);
return $this->renderTwig('auth/verify_success', [
'name' => $user['name']
]);
}
/**
public function resendVerification()
{
if ($this->request->getMethod() === 'POST') {
$email = $this->request->getPost('email');
if (empty($email)) {
return redirect()->back()->with('error', 'Введите email');
}
$userModel = new UserModel();
$user = $userModel->where('email', $email)->first();
if (!$user) {
return redirect()->back()->with('error', 'Пользователь с таким email не найден');
}
if ($user['email_verified']) {
return redirect()->to('/login')->with('info', 'Email уже подтверждён. Вы можете войти.');
}
$newToken = bin2hex(random_bytes(32));
$userModel->update($user['id'], [
'verification_token' => $newToken
]);
$this->emailLibrary->sendVerificationEmail(
$user['email'],
$user['name'],
$newToken
);
return redirect()->back()->with('success', 'Письмо для подтверждения отправлено повторно. Проверьте почту.');
}
return $this->renderTwig('auth/resend_verification');
}
public function login()
{
if ($this->request->getMethod() === 'POST') {
$userModel = new \App\Models\UserModel();
$orgUserModel = new \App\Models\OrganizationUserModel();
$email = $this->request->getPost('email');
$password = $this->request->getPost('password');
$user = $userModel->where('email', $email)->first();
if ($user && password_verify($password, $user['password'])) {
if (!$user['email_verified']) {
session()->setFlashdata('warning', 'Email не подтверждён. Проверьте почту или <a href="/auth/resend-verification">запросите письмо повторно</a>.');
return redirect()->to('/login');
}
$userOrgs = $orgUserModel->where('user_id', $user['id'])->findAll();
if (empty($userOrgs)) {
session()->setFlashdata('error', 'Ваш аккаунт не привязан ни к одной организации. Обратитесь к поддержке.');
return redirect()->to('/login');
}
$sessionData = [
'user_id' => $user['id'],
'email' => $user['email'],
'name' => $user['name'],
'isLoggedIn' => true
];
if (count($userOrgs) === 1) {
$sessionData['active_org_id'] = $userOrgs[0]['organization_id'];
session()->set($sessionData);
return redirect()->to('/');
}
session()->remove('active_org_id');
session()->set($sessionData);
session()->setFlashdata('info', 'Выберите пространство для работы');
return redirect()->to('/organizations');
} else {
return redirect()->back()->with('error', 'Неверный логин или пароль');
}
}
return $this->renderTwig('auth/login');
}
public function logout()
{
session()->destroy();
session()->remove('active_org_id');
return redirect()->to('/');
}
}
// app/Controllers/BaseController.php
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use App\Models\OrganizationModel;
/**
abstract class BaseController extends Controller
{
/**
protected $session;
/**
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
parent::initController($request, $response, $logger);
$this->session = service('session');
}
public function renderTwig($template, $data = [])
{
helper('csrf');
$twig = \Config\Services::twig();
$oldInput = $this->session->get('_ci_old_input') ?? [];
$data['old'] = $data['old'] ?? $oldInput;
ob_start();
$twig->display($template, $data);
$content = ob_get_clean();
return $content;
}
/**
protected function getTableConfig(): array
{
return [
'model' => null,
'columns' => [],
'searchable' => [],
'sortable' => [],
'defaultSort' => 'id',
'order' => 'asc',
'viewPath' => '',
'partialPath' => '',
'itemsKey' => 'items',
'scope' => null,
];
}
/**
protected function isAjax(): bool
{
$header = $this->request->header('X-Requested-With');
$value = $header ? $header->getValue() : '';
return strtolower($value) === 'xmlhttprequest';
}
/**
protected function prepareTableData(?array $config = null): array
{
$config = array_merge($this->getTableConfig(), $config ?? []);
$page = (int) ($this->request->getGet('page') ?? 1);
$perPage = (int) ($this->request->getGet('perPage') ?? 10);
$sort = $this->request->getGet('sort') ?? $config['defaultSort'];
$order = $this->request->getGet('order') ?? $config['order'];
$filters = [];
foreach ($this->request->getGet() as $key => $value) {
if (str_starts_with($key, 'filters[') && str_ends_with($key, ']')) {
$field = substr($key, 9, -1);
$filters[$field] = $value;
}
}
$model = $config['model'];
$builder = $model->builder();
if (isset($config['scope']) && is_callable($config['scope'])) {
$config['scope']($builder);
}
foreach ($filters as $field => $value) {
if ($value && in_array($field, $config['searchable'])) {
$builder->like($field, $value);
}
}
if ($sort && in_array($sort, $config['sortable'])) {
$builder->orderBy($sort, $order);
}
$total = $builder->countAll();
$builder->select('*');
$items = $builder->limit($perPage, ($page - 1) * $perPage)->get()->getResult();
$from = ($page - 1) * $perPage + 1;
$to = min($page * $perPage, $total);
$pagerData = [
'currentPage' => $page,
'pageCount' => $total > 0 ? (int) ceil($total / $perPage) : 1,
'total' => $total,
'perPage' => $perPage,
'from' => $from,
'to' => $to,
];
$pagerStub = new class($pagerData) {
private $data;
public function __construct(array $data) { $this->data = $data; }
public function getCurrentPage(): int { return $this->data['currentPage'] ?? 1; }
public function getPageCount(): int { return $this->data['pageCount'] ?? 1; }
public function getTotal(): int { return $this->data['total'] ?? 0; }
public function getDetails(): array { return $this->data; }
};
$data = [
$config['itemsKey'] => $items,
'pager' => $pagerStub,
'pagerDetails' => $pagerData,
'perPage' => $perPage,
'sort' => $sort,
'order' => $order,
'filters' => $filters,
'columns' => $config['columns'],
];
return $data;
}
/**
public function table()
{
$config = $this->getTableConfig();
$data = $this->prepareTableData($config);
if (!$this->isAjax()) {
return redirect()->to('/');
}
return $this->renderTwig($config['partialPath'], $data);
}
}
// app/Controllers/Landing.php
<?php
namespace App\Controllers;
class Landing extends BaseController
{
public function index()
{
if (session()->get('isLoggedIn')) {
return redirect()->to('/organizations');
}
return $this->renderTwig('landing/index');
}
}
// app/Controllers/Organizations.php
<?php
namespace App\Controllers;
use App\Models\OrganizationModel;
use App\Models\OrganizationUserModel;
class Organizations extends BaseController
{
public function index()
{
$orgModel = new OrganizationModel();
$orgUserModel = new OrganizationUserModel();
$userId = session()->get('user_id');
$userOrgLinks = $orgUserModel->where('user_id', $userId)->findAll();
$orgIds = array_column($userOrgLinks, 'organization_id');
$organizations = [];
if (!empty($orgIds)) {
$organizations = $orgModel->whereIn('id', $orgIds)->findAll();
}
return $this->renderTwig('organizations/index', [
'organizations' => $organizations,
'count' => count($organizations)
]);
}
public function create()
{
if ($this->request->getMethod() === 'POST') {
$orgModel = new OrganizationModel();
$orgUserModel = new OrganizationUserModel();
$rules = [
'name' => 'required|min_length[2]',
];
if (!$this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$requisites = [
'inn' => trim($this->request->getPost('inn') ?? ''),
'ogrn' => trim($this->request->getPost('ogrn') ?? ''),
'kpp' => trim($this->request->getPost('kpp') ?? ''),
'legal_address' => trim($this->request->getPost('legal_address') ?? ''),
'actual_address' => trim($this->request->getPost('actual_address') ?? ''),
'phone' => trim($this->request->getPost('phone') ?? ''),
'email' => trim($this->request->getPost('email') ?? ''),
'website' => trim($this->request->getPost('website') ?? ''),
'bank_name' => trim($this->request->getPost('bank_name') ?? ''),
'bank_bik' => trim($this->request->getPost('bank_bik') ?? ''),
'checking_account' => trim($this->request->getPost('checking_account') ?? ''),
'correspondent_account' => trim($this->request->getPost('correspondent_account') ?? ''),
];
$orgId = $orgModel->insert([
'owner_id' => session()->get('user_id'),
'name' => $this->request->getPost('name'),
'type' => 'business',
'requisites' => json_encode($requisites),
'settings' => json_encode([]),
]);
$orgUserModel->insert([
'organization_id' => $orgId,
'user_id' => session()->get('user_id'),
'role' => 'owner',
'status' => 'active',
'joined_at' => date('Y-m-d H:i:s'),
]);
session()->set('active_org_id', $orgId);
session()->setFlashdata('success', 'Организация успешно создана!');
return redirect()->to('/');
}
return $this->renderTwig('organizations/create');
}
/**
public function edit($orgId)
{
$orgModel = new OrganizationModel();
$orgUserModel = new OrganizationUserModel();
$userId = session()->get('user_id');
$membership = $orgUserModel->where('organization_id', $orgId)
->where('user_id', $userId)
->first();
if (!$membership) {
session()->setFlashdata('error', 'Доступ запрещен');
return redirect()->to('/organizations');
}
$organization = $orgModel->find($orgId);
if (!$organization) {
session()->setFlashdata('error', 'Организация не найдена');
return redirect()->to('/organizations');
}
$requisites = json_decode($organization['requisites'] ?? '{}', true);
if ($this->request->getMethod() === 'POST') {
$rules = [
'name' => 'required|min_length[2]',
];
if (!$this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$newRequisites = [
'inn' => trim($this->request->getPost('inn') ?? ''),
'ogrn' => trim($this->request->getPost('ogrn') ?? ''),
'kpp' => trim($this->request->getPost('kpp') ?? ''),
'legal_address' => trim($this->request->getPost('legal_address') ?? ''),
'actual_address' => trim($this->request->getPost('actual_address') ?? ''),
'phone' => trim($this->request->getPost('phone') ?? ''),
'email' => trim($this->request->getPost('email') ?? ''),
'website' => trim($this->request->getPost('website') ?? ''),
'bank_name' => trim($this->request->getPost('bank_name') ?? ''),
'bank_bik' => trim($this->request->getPost('bank_bik') ?? ''),
'checking_account' => trim($this->request->getPost('checking_account') ?? ''),
'correspondent_account' => trim($this->request->getPost('correspondent_account') ?? ''),
];
$orgModel->update($orgId, [
'name' => $this->request->getPost('name'),
'requisites' => json_encode($newRequisites),
]);
session()->setFlashdata('success', 'Организация успешно обновлена!');
return redirect()->to('/organizations');
}
return $this->renderTwig('organizations/edit', [
'organization' => $organization,
'requisites' => $requisites
]);
}
/**
public function delete($orgId)
{
$orgModel = new OrganizationModel();
$orgUserModel = new OrganizationUserModel();
$userId = session()->get('user_id');
$membership = $orgUserModel->where('organization_id', $orgId)
->where('user_id', $userId)
->first();
if (!$membership) {
session()->setFlashdata('error', 'Доступ запрещен');
return redirect()->to('/organizations');
}
if ($membership['role'] !== 'owner') {
session()->setFlashdata('error', 'Только владелец может удалить организацию');
return redirect()->to('/organizations');
}
$organization = $orgModel->find($orgId);
if (!$organization) {
session()->setFlashdata('error', 'Организация не найдена');
return redirect()->to('/organizations');
}
if ($this->request->getMethod() === 'POST') {
$orgUserModel->where('organization_id', $orgId)->delete();
$orgModel->delete($orgId);
if (session()->get('active_org_id') == $orgId) {
session()->remove('active_org_id');
}
session()->setFlashdata('success', 'Организация "' . $organization['name'] . '" удалена');
return redirect()->to('/organizations');
}
return $this->renderTwig('organizations/delete', [
'organization' => $organization
]);
}
public function switch($orgId)
{
$userId = session()->get('user_id');
$orgUserModel = new OrganizationUserModel();
$membership = $orgUserModel->where('organization_id', $orgId)
->where('user_id', $userId)
->first();
if ($membership) {
session()->set('active_org_id', $orgId);
session()->setFlashdata('success', 'Организация изменена');
return redirect()->to('/');
} else {
session()->setFlashdata('error', 'Доступ запрещен');
return redirect()->to('/organizations');
}
}
}
// app/Controllers/Home.php
<?php
namespace App\Controllers;
use App\Models\OrganizationModel;
use App\Models\OrganizationUserModel;
class Home extends BaseController
{
public function index()
{
if (!session()->get('isLoggedIn')) {
return $this->renderTwig('landing/index');
}
$orgId = session()->get('active_org_id');
if (empty($orgId)){
session()->remove('active_org_id');
return redirect()->to('/organizations');
}
$data = [
'title' => 'Рабочий стол',
];
return $this->renderTwig('dashboard/index', $data);
}
}
// .gitignore
#-------------------------
# Operating Specific Junk Files
#-------------------------
# OS X
.DS_Store
.AppleDouble
.LSOverride
# OS X Thumbnails
._*
# Windows image file caches
Thumbs.db
ehthumbs.db
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# Linux
*~
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
#-------------------------
# Environment Files
#-------------------------
# These should never be under version control,
# as it poses a security risk.
.env
.vagrant
Vagrantfile
#-------------------------
# Temporary Files
#-------------------------
writable/cache/*
!writable/cache/index.html
writable/logs/*
!writable/logs/index.html
writable/session/*
!writable/session/index.html
writable/uploads/*
!writable/uploads/index.html
writable/debugbar/*
!writable/debugbar/index.html
php_errors.log
#-------------------------
# User Guide Temp Files
#-------------------------
user_guide_src/build/*
user_guide_src/cilexer/build/*
user_guide_src/cilexer/dist/*
user_guide_src/cilexer/pycilexer.egg-info/*
#-------------------------
# Test Files
#-------------------------
tests/coverage*
# Don't save phpunit under version control.
phpunit
#-------------------------
# Composer
#-------------------------
vendor/
#-------------------------
# IDE / Development Files
#-------------------------
# Modules Testing
_modules/*
# phpenv local config
.php-version
# Jetbrains editors (PHPStorm, etc)
.idea/
*.iml
# NetBeans
/nbproject/
/build/
/nbbuild/
/dist/
/nbdist/
/nbactions.xml
/nb-configuration.xml
/.nb-gradle/
# Sublime Text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
*.sublime-workspace
*.sublime-project
.phpintel
/api/
# Visual Studio Code
.vscode/
/results/
/phpunit*.xml
// public/assets/css/modules/data-table.css
/**
* DataTable - Универсальные стили для интерактивных таблиц
*
* Подключение: <link rel="stylesheet" href="/css/components/data-table.css">
*/
/* Основной контейнер таблицы */
.data-table {
width: 100%;
}
/* Интерактивные заголовки */
.data-table thead th {
vertical-align: middle;
white-space: nowrap;
padding: 0.5rem 0.75rem;
}
.data-table .header-content {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-width: 0; /* Предотвращает переполнение */
}
/* Текст заголовка */
.data-table .header-text {
cursor: pointer;
flex: 1 1 auto; /* grow, shrink, auto basis */
transition: color 0.2s ease;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0; /* Важно для flex */
}
.data-table .header-text:hover {
color: var(--bs-primary, #0d6efd);
}
/* Иконка поиска */
.data-table .search-trigger {
cursor: pointer;
opacity: 0.5;
transition: opacity 0.2s ease, color 0.2s ease;
flex-shrink: 0;
font-size: 0.875rem;
}
.data-table .search-trigger:hover {
opacity: 1;
color: var(--bs-primary, #0d6efd);
}
/* Иконки сортировки */
.data-table .sort-icon {
cursor: pointer;
transition: color 0.2s ease;
flex-shrink: 0;
font-size: 0.875rem;
}
.data-table .sort-icon:hover {
color: var(--bs-primary, #0d6efd) !important;
}
.data-table .sort-icon.active {
color: var(--bs-primary, #0d6efd) !important;
}
/* Поле поиска в заголовке */
.data-table .header-search-input {
display: none;
flex: 1 1 auto; /* grow, shrink, auto basis - занимает доступное пространство */
min-width: 0; /* Важно для flex-элементов - позволяет сжиматься */
max-width: 100%; /* Не выходить за пределы контейнера */
width: auto;
font-size: 0.875rem;
padding: 0.25rem 0.5rem;
}
.data-table .header-search-input:focus {
outline: none;
box-shadow: 0 0 0 0.2rem rgba(var(--bs-primary-rgb, 13, 110, 253), 0.25);
}
.data-table .header-search-input.visible {
display: flex; /* Используем flex вместо block для лучшего выравнивания */
}
/* Анимация появления */
@keyframes data-table-fade-in {
from {
opacity: 0;
transform: translateY(-2px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.data-table .header-search-input {
animation: data-table-fade-in 0.15s ease-in-out;
}
/* Индикатор сортировки - восходящая */
.data-table .sort-asc {
color: var(--bs-primary, #0d6efd) !important;
}
/* Индикатор сортировки - нисходящая */
.data-table .sort-desc {
color: var(--bs-primary, #0d6efd) !important;
}
/* Стили для загрузки */
.data-table .loading {
position: relative;
pointer-events: none;
}
.data-table .loading::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.7);
z-index: 1;
}
/* Пустое состояние */
.data-table .empty-state {
text-align: center;
padding: 3rem 1rem;
color: var(--bs-secondary-color, #6c757d);
}
.data-table .empty-state i {
font-size: 3rem;
opacity: 0.3;
margin-bottom: 1rem;
}
.data-table .empty-state h5 {
margin-bottom: 0.5rem;
}
.data-table .empty-state p {
margin-bottom: 0;
opacity: 0.8;
}
/* Адаптивность для мобильных */
@media (max-width: 768px) {
.data-table .header-content {
gap: 4px;
}
.data-table .header-text {
font-size: 0.875rem;
}
.data-table .search-trigger,
.data-table .sort-icon {
font-size: 0.875rem;
}
}
/* Стили пагинации */
.data-table .pagination-wrapper {
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: nowrap;
gap: 1rem;
padding: 0.75rem 1rem;
border-top: 1px solid var(--bs-border-color, #dee2e6);
width: 100%;
}
.data-table .pagination-info {
font-size: 0.875rem;
color: var(--bs-secondary-color, #6c757d);
flex-shrink: 0;
white-space: nowrap;
}
/* Пагинация - посередине, занимает доступное пространство */
.data-table .pagination-wrapper > nav.pagination {
flex: 0 1 auto;
margin-left: auto;
margin-right: auto;
}
/* Селектор - справа, не растягивается */
.data-table .per-page-selector {
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
white-space: nowrap;
}
.data-table .page-link {
display: flex;
align-items: center;
justify-content: center;
min-width: 2rem;
height: 2rem;
padding: 0 0.5rem;
border-radius: 0.375rem;
font-size: 0.875rem;
text-decoration: none;
transition: all 0.2s ease;
}
.data-table .page-link:hover:not(.disabled):not(.active) {
background-color: var(--bs-primary-bg-subtle, #e9ecef);
}
.data-table .page-link.active {
background-color: var(--bs-primary, #0d6efd);
color: #fff;
border-color: var(--bs-primary, #0d6efd);
}
.data-table .page-link.disabled {
color: var(--bs-secondary-color, #6c757d);
pointer-events: none;
opacity: 0.5;
}
/* Количество записей на странице */
.data-table .per-page-selector {
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
}
.data-table .per-page-selector label {
font-size: 0.875rem;
color: var(--bs-secondary-color, #6c757d);
margin-bottom: 0;
white-space: nowrap;
}
/* Компактный селектор - принудительно ограничиваем ширину */
.data-table .per-page-selector .per-page-select,
.data-table .per-page-selector select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
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 0.5rem center;
background-size: 16px 12px;
padding-right: 2rem !important;
width: 80px !important;
max-width: 80px !important;
min-width: 80px !important;
flex: 0 0 80px !important;
font-size: 0.875rem !important;
padding: 0.25rem 0.5rem !important;
padding-right: 1.5rem !important;
margin: 0 !important;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
background-color: #fff;
cursor: pointer;
}
/* Контейнер селектора не должен растягиваться */
.data-table .per-page-selector {
width: auto !important;
max-width: none !important;
flex-shrink: 0;
}
/* Стили для ячеек с действиями */
.data-table .actions-cell {
white-space: nowrap;
text-align: end;
}
.data-table .action-btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
margin-left: 0.25rem;
}
/* Hover эффекты для строк */
.data-table tbody tr {
transition: background-color 0.15s ease;
}
.data-table tbody tr:hover {
background-color: var(--bs-table-hover-bg, rgba(0, 0, 0, 0.02));
}
/* Статус активной сортировки в заголовке */
.data-table th.sorted-asc .sort-icon::before {
content: '\f160';
font-weight: 900;
}
.data-table th.sorted-desc .sort-icon::before {
content: '\f161';
font-weight: 900;
}
// public/assets/css/bootstrap.min.css
@charset "UTF-8";/*!
* Bootstrap v5.3.8 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/:root,[data-bs-theme=light]{--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-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--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-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,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));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.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;color:var(--bs-heading-color)}.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[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:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}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}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:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}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:var(--bs-secondary-color);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]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[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;line-height:inherit;font-size:calc(1.275rem + .3vw)}@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]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button{cursor:pointer;filter:grayscale(1)}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-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-weight:300;line-height:1.2;font-size:calc(1.625rem + 4.5vw)}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-weight:300;line-height:1.2;font-size:calc(1.575rem + 3.9vw)}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-weight:300;line-height:1.2;font-size:calc(1.525rem + 3.3vw)}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-weight:300;line-height:1.2;font-size:calc(1.475rem + 2.7vw)}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-weight:300;line-height:1.2;font-size:calc(1.425rem + 2.1vw)}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-weight:300;line-height:1.2;font-size:calc(1.375rem + 1.5vw)}@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:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);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}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.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.33333333%}.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.66666667%}.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{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.33333333%}.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.66666667%}.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{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.33333333%}.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.66666667%}.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{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.33333333%}.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.66666667%}.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{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.33333333%}.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.66666667%}.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{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.33333333%}.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.66666667%}.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-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#a6b5cc;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#b5b6b7;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--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:var(--bs-table-color);border-color:var(--bs-table-border-color)}.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 + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);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:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);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}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);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::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline: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 + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img: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 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@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:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.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-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-print-color-adjust:exact;color-adjust:exact;print-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]{--bs-form-check-bg-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 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-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;--bs-form-check-bg-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{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg: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");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);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{--bs-form-switch-bg: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;--bs-form-switch-bg: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-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.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}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.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;-webkit-appearance:none;appearance:none;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}@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:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;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}@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:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem .75rem;overflow:hidden;color:rgba(var(--bs-body-color-rgb),.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) 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,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:.75rem}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>textarea:focus~label::after,.form-floating>textarea:not(:placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>textarea:disabled~label::after{background-color:var(--bs-secondary-bg)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.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:var(--bs-border-radius-lg)}.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:var(--bs-border-radius-sm)}.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)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){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>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){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:calc(-1 * var(--bs-border-width));border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.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:var(--bs-success);border-radius:var(--bs-border-radius)}.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:var(--bs-form-valid-border-color);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.73.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.1'/%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:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.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:var(--bs-form-valid-border-color)}.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"]{--bs-form-select-bg-icon: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.73.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.1'/%3e%3c/svg%3e");padding-right:4.125rem;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:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.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:var(--bs-danger);border-radius:var(--bs-border-radius)}.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:var(--bs-form-invalid-border-color);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:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.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:var(--bs-form-invalid-border-color)}.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"]{--bs-form-select-bg-icon: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");padding-right:4.125rem;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:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);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:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.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}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{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{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.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:var(--bs-dropdown-spacer)}.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:var(--bs-dropdown-spacer)}.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:var(--bs-dropdown-spacer)}.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:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-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{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(-1 * var(--bs-border-width))}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.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:calc(-1 * var(--bs-border-width))}.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:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;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:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-grow:1;flex-basis:0;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{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.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:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-grow:1;flex-basis:100%;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@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 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.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-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg: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")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg: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")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.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:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@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{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.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-collapse,.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%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-.708'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%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-.708'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);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:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(-1 * var(--bs-border-width))}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:var(--bs-progress-height)}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@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:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.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:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:not(.active):focus,.list-group-item-action:not(.active):hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:not(.active):active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);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:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg: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 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;filter:var(--bs-btn-close-filter);border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}:root,[data-bs-theme=light]{--bs-btn-close-filter: }[data-bs-theme=dark]{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color:var(--bs-body-color);--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transform:translate(0,-50px);transition:transform .3s ease-out}@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% - var(--bs-modal-margin) * 2)}.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% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin-top:calc(-.5 * var(--bs-modal-header-padding-y));margin-right:calc(-.5 * var(--bs-modal-header-padding-x));margin-bottom:calc(-.5 * var(--bs-modal-header-padding-y));margin-left:auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-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-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@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-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);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;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);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;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.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:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.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;filter:var(--bs-carousel-control-icon-filter);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 0'/%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-.708'/%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%}.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:var(--bs-carousel-indicator-active-bg);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:var(--bs-carousel-caption-color);text-align:center}.carousel-dark{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}:root,[data-bs-theme=light]{--bs-carousel-indicator-active-bg:#fff;--bs-carousel-caption-color:#fff;--bs-carousel-control-icon-filter: }[data-bs-theme=dark]{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}.spinner-border,.spinner-grow{display:inline-block;flex-shrink:0;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y));margin-left:auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.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:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.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}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){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}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.visually-hidden *,.visually-hidden-focusable:not(:focus):not(:focus-within) *{overflow:hidden!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}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.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}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y: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-inline-grid{display:inline-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:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.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:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!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}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.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}.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}.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}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap: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-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!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{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!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:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap: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}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap: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}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap: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}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap: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}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit: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-inline-grid{display:inline-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}.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}.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}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap: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-inline-grid{display:inline-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 */
// public/assets/js/modules/DataTable.js
/**
class DataTable {
/**
constructor(containerId, options = {}) {
this.containerId = containerId;
this.options = {
url: options.url || '/api/table',
perPage: options.perPage || 10,
debounceTime: options.debounceTime || 300,
preserveSearchOnSort: options.preserveSearchOnSort !== false,
...options
};
this.state = {
page: 1,
perPage: this.options.perPage,
sort: '',
order: 'asc',
filters: {}
};
this.searchTimeout = null;
this.initWithDOM();
}
/**
initWithDOM() {
this.container = document.getElementById(this.containerId);
if (!this.container) {
setTimeout(() => {
this.container = document.getElementById(this.containerId);
if (this.container) {
this.init();
} else {
}
}, 0);
return;
}
this.init();
}
/**
init() {
if (this.container.dataset.datatableInitialized) {
console.log('DataTable: Already initialized for', this.containerId);
return;
}
this.container.dataset.datatableInitialized = 'true';
const tbody = this.container.querySelector('tbody');
const existingRows = tbody ? tbody.querySelectorAll('tr') : [];
console.log('DataTable init for', this.containerId, ':', existingRows.length, 'rows found');
if (existingRows.length > 0) {
const hasDataRow = Array.from(existingRows).some(tr => !tr.classList.contains('loading'));
if (hasDataRow) {
console.log('DataTable: Data exists, skipping AJAX load');
this.bindEvents();
return;
}
}
console.log('DataTable: No data found, loading via AJAX...');
this.bindEvents();
this.loadData();
}
/**
bindEvents() {
this.container.addEventListener('click', (e) => this.handleClick(e));
this.container.addEventListener('input', (e) => this.handleInput(e));
this.container.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && e.target.matches('[data-search-input]')) {
e.preventDefault();
e.target.blur();
}
});
this.container.addEventListener('change', (e) => {
if (e.target.matches('[id^="per-page-select-"]')) {
const value = e.target.value;
this.setPerPage(value);
}
});
}
/**
handleClick(e) {
const sortIcon = e.target.closest('[data-sort]');
if (sortIcon) {
e.stopPropagation();
const column = sortIcon.dataset.sort;
this.toggleSort(column);
return;
}
const searchTrigger = e.target.closest('[data-search-trigger]');
if (searchTrigger) {
e.stopPropagation();
const th = searchTrigger.closest('th');
this.openSearch(th);
return;
}
const headerText = e.target.closest('[data-header-text]');
if (headerText) {
e.stopPropagation();
const th = headerText.closest('th');
this.openSearch(th);
return;
}
const pageLink = e.target.closest('[data-page]');
if (pageLink && !e.target.closest('.disabled') && !e.target.closest('.active')) {
e.preventDefault();
const page = parseInt(pageLink.dataset.page);
if (page > 0) {
this.state.page = page;
this.loadData();
}
return;
}
const navLink = e.target.closest('[data-nav-page]');
if (navLink && !e.target.closest('.disabled')) {
e.preventDefault();
const direction = navLink.dataset.navPage;
this.state.page = direction === 'prev'
? Math.max(1, this.state.page - 1)
: this.state.page + 1;
this.loadData();
return;
}
if (!e.target.closest('thead')) {
this.closeAllSearches();
}
}
/**
handleInput(e) {
if (e.target.matches('[data-search-input]')) {
const column = e.target.dataset.searchInput;
this.debouncedSearch(column, e.target.value);
}
}
/**
debouncedSearch(column, value) {
clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(() => {
this.state.filters[column] = value;
this.state.page = 1;
this.loadData();
}, this.options.debounceTime);
}
/**
toggleSort(column) {
if (this.state.sort === column) {
this.state.order = this.state.order === 'asc' ? 'desc' : 'asc';
} else {
this.state.sort = column;
this.state.order = 'desc';
}
this.loadData();
}
/**
openSearch(th) {
const headerText = th.querySelector('[data-header-text]');
const searchInput = th.querySelector('[data-search-input]');
if (headerText && searchInput) {
headerText.style.display = 'none';
searchInput.style.display = 'block';
searchInput.focus();
}
}
/**
closeAllSearches() {
const inputs = this.container.querySelectorAll('[data-search-input]');
inputs.forEach(input => {
const th = input.closest('th');
const headerText = th.querySelector('[data-header-text]');
if (headerText) {
if (input.value.trim()) {
input.style.display = 'none';
headerText.style.display = 'inline';
} else {
input.style.display = 'none';
headerText.style.display = 'inline';
}
}
});
}
/**
getCsrfToken() {
if (this.container.dataset.csrfToken) {
return this.container.dataset.csrfToken;
}
const csrfInput = this.container.querySelector('input[name*="csrf"]');
if (csrfInput) {
return csrfInput.value;
}
const globalCsrfInput = document.querySelector('input[name*="csrf"]');
if (globalCsrfInput) {
return globalCsrfInput.value;
}
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
const [name, value] = cookie.trim().split('=');
if (name.toLowerCase().includes('csrf')) {
return decodeURIComponent(value);
}
}
console.warn('CSRF token not found');
return '';
}
/**
getCsrfTokenName() {
if (this.container.dataset.csrfTokenName) {
return this.container.dataset.csrfTokenName;
}
const csrfInput = this.container.querySelector('input[name*="csrf"]');
if (csrfInput) {
return csrfInput.name;
}
const globalCsrfInput = document.querySelector('input[name*="csrf"]');
if (globalCsrfInput) {
return globalCsrfInput.name;
}
return 'csrf_test_name';
}
/**
updateCsrfToken(response) {
const csrfHeader = response.headers.get('X-CSRF-TOKEN');
if (csrfHeader) {
document.querySelectorAll('input[name*="csrf"]').forEach(input => {
input.value = csrfHeader;
});
this.container.dataset.csrfToken = csrfHeader;
}
}
/**
async loadData() {
const params = this.buildParams();
const csrfToken = this.getCsrfToken();
const csrfTokenName = this.getCsrfTokenName();
const url = `${this.options.url}?${params}&${csrfTokenName}=${encodeURIComponent(csrfToken)}`;
console.log('DataTable: Loading from URL:', url);
console.log('DataTable: CSRF token:', csrfToken, 'name:', csrfTokenName);
const tableBody = this.container.querySelector('tbody');
if (tableBody) {
tableBody.innerHTML = `
<tr>
<td colspan="100" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Загрузка...</span>
</div>
</td>
</tr>
`;
}
try {
const response = await fetch(url, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': csrfToken
}
});
this.updateCsrfToken(response);
console.log('DataTable: Response status:', response.status);
console.log('DataTable: Response URL:', response.url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const html = await response.text();
console.log('DataTable: HTML length:', html.length);
console.log('DataTable: HTML preview:', html.substring(0, 200).replace(/\s+/g, ' '));
this.updateTable(html);
} catch (error) {
console.error('DataTable: Ошибка загрузки данных:', error);
this.showError();
}
}
/**
updateTable(html) {
console.log('DataTable: HTML received, length:', html.length);
console.log('DataTable: HTML starts with:', html.substring(0, 50).replace(/\s+/g, ' '));
const searchInputs = this.container.querySelectorAll('[data-search-input]');
const searchStates = {};
searchInputs.forEach(input => {
searchStates[input.dataset.searchInput] = input.value;
});
const tbodyMatch = html.match(/<tbody[^>]*>[\s\S]*?<\/tbody>/i);
const tfootMatch = html.match(/<tfoot[^>]*>[\s\S]*?<\/tfoot>/i);
const oldTableBody = this.container.querySelector('tbody');
const oldTableFooter = this.container.querySelector('tfoot');
const table = this.container.querySelector('table');
console.log('DataTable: tbodyMatch:', !!tbodyMatch, 'oldTableBody:', !!oldTableBody);
console.log('DataTable: tfootMatch:', !!tfootMatch, 'oldTableFooter:', !!oldTableFooter);
if (tbodyMatch && oldTableBody && table) {
const tbodyContent = tbodyMatch[0].replace(/<\/?tbody[^>]*>/gi, '');
oldTableBody.innerHTML = tbodyContent;
console.log('DataTable: tbody updated, rows:', oldTableBody.querySelectorAll('tr').length);
if (tfootMatch) {
const tfootContent = tfootMatch[0].replace(/<\/?tfoot[^>]*>/gi, '');
if (oldTableFooter) {
oldTableFooter.innerHTML = tfootContent;
} else {
const newTfoot = document.createElement('tfoot');
newTfoot.innerHTML = tfootContent;
table.appendChild(newTfoot);
}
} else if (oldTableFooter) {
oldTableFooter.remove();
}
} else {
console.log('DataTable: Full container replacement');
this.container.innerHTML = html;
}
this.restoreSearchStates(searchStates);
this.updateURL();
}
/**
restoreSearchStates(savedStates = null) {
const inputs = this.container.querySelectorAll('[data-search-input]');
inputs.forEach(input => {
const column = input.dataset.searchInput;
const value = savedStates && savedStates[column] !== undefined
? savedStates[column]
: input.value.trim();
const th = input.closest('th');
const headerText = th.querySelector('[data-header-text]');
if (headerText) {
if (value) {
headerText.style.display = 'none';
input.style.display = 'block';
input.value = value;
} else {
input.style.display = 'none';
headerText.style.display = 'inline';
}
}
});
}
/**
updateURL() {
const params = this.buildParams();
const baseUrl = this.options.url.split('?')[0];
const url = `${baseUrl}?${params}`;
window.history.pushState(this.state, '', url);
}
/**
buildParams() {
const params = new URLSearchParams();
params.set('page', this.state.page);
params.set('perPage', this.state.perPage);
if (this.state.sort) {
params.set('sort', this.state.sort);
params.set('order', this.state.order);
}
Object.entries(this.state.filters).forEach(([key, value]) => {
if (value && value.trim()) {
params.set(`filters[${key}]`, value);
}
});
return params.toString();
}
/**
showError() {
const tableBody = this.container.querySelector('tbody');
if (tableBody) {
tableBody.innerHTML = `
<tr>
<td colspan="100" class="alert alert-danger m-3">
Ошибка загрузки данных. Пожалуйста, обновите страницу.
</td>
</tr>
`;
}
}
/**
setFilter(column, value) {
this.state.filters[column] = value;
this.state.page = 1;
this.loadData();
}
/**
setPerPage(value) {
this.state.perPage = parseInt(value);
this.state.page = 1;
this.loadData();
}
/**
goToPage(page) {
this.state.page = Math.max(1, parseInt(page));
this.loadData();
}
}
window.DataTable = DataTable;
// public/.htaccess
# Disable directory browsing
Options -Indexes
# ----------------------------------------------------------------------
# Rewrite engine
# ----------------------------------------------------------------------
# Turning on the rewrite engine is necessary for the following rules and features.
# FollowSymLinks must be enabled for this to work.
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
# If you installed CodeIgniter in a subfolder, you will need to
# change the following line to match the subfolder you need.
# http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
# RewriteBase /
# Redirect Trailing Slashes...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Rewrite "www.example.com -> example.com"
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
# Checks to see if the user is attempting to access a valid file,
# such as an image or css document, if this isn't true it sends the
# request to the front controller, index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([\s\S]*)$ index.php/$1 [L,NC,QSA]
# Ensure Authorization header is passed along
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>
<IfModule !mod_rewrite.c>
# If we don't have mod_rewrite installed, all 404's
# can be sent to index.php, and everything works as normal.
ErrorDocument 404 index.php
</IfModule>
# Disable server signature start
ServerSignature Off
# Disable server signature end
// public/index.php
<?php
use CodeIgniter\Boot;
use Config\Paths;
/*
$minPhpVersion = '8.1';
if (version_compare(PHP_VERSION, $minPhpVersion, '<')) {
$message = sprintf(
'Your PHP version must be %s or higher to run CodeIgniter. Current version: %s',
$minPhpVersion,
PHP_VERSION,
);
header('HTTP/1.1 503 Service Unavailable.', true, 503);
echo $message;
exit(1);
}
/*
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);
if (getcwd() . DIRECTORY_SEPARATOR !== FCPATH) {
chdir(FCPATH);
}
/*
require FCPATH . '../app/Config/Paths.php';
$paths = new Paths();
require $paths->systemDirectory . '/Boot.php';
exit(Boot::bootWeb($paths));
// .env
#--------------------------------------------------------------------
# Example Environment Configuration file
#
# This file can be used as a starting point for your own
# custom .env files, and contains most of the possible settings
# available in a default install.
#
# By default, all of the settings are commented out. If you want
# to override the setting, you must un-comment it by removing the '#'
# at the beginning of the line.
#--------------------------------------------------------------------
#--------------------------------------------------------------------
# ENVIRONMENT
#--------------------------------------------------------------------
CI_ENVIRONMENT = development
#--------------------------------------------------------------------
# APP
#--------------------------------------------------------------------
app.baseURL = 'https://bp.taskms.ru'
# If you have trouble with `.`, you could also use `_`.
# app_baseURL = ''
# app.forceGlobalSecureRequests = false
# app.CSPEnabled = false
#--------------------------------------------------------------------
# DATABASE
#--------------------------------------------------------------------
database.default.hostname = localhost
database.default.database = bp_mirv_db
database.default.username = bp_mirv
database.default.password = bp_mirv_Moloko22
database.default.DBDriver = MySQLi
database.default.DBPrefix =
database.default.port = 3306
#--------------------------------------------------------------------
# ENCRYPTION
#--------------------------------------------------------------------
encryption.key = sadfonusdofuhsefiouhw9er87yhdf
#--------------------------------------------------------------------
# SMTP
#--------------------------------------------------------------------
email.protocol = 'smtp'
email.SMTPHost = 'smtp.yandex.ru'
email.SMTPCrypto = 'ssl'
email.SMTPPort = 465
email.SMTPUser = 'mirvtop@yandex.ru'
email.SMTPPass = 'azpudcybqsqbbqns'
email.fromEmail = 'mirvtop@yandex.ru'
email.fromName = 'Бизнес.Точка'
email.mailType = 'html'
#--------------------------------------------------------------------
# SESSION
#--------------------------------------------------------------------
# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
# session.savePath = null
#--------------------------------------------------------------------
# LOGGER
#--------------------------------------------------------------------
# logger.threshold = 4