diff --git a/bp.txt b/bp.txt
deleted file mode 100644
index acefcc2..0000000
--- a/bp.txt
+++ /dev/null
@@ -1,7468 +0,0 @@
-// app/Language/en/Validation.php
- ['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
-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
-
-
-
- 403 Forbidden
-
-
-
-Directory access is forbidden.
-
-
-
-
-// app/Common.php
-
-
-
-
-
- {% block title %}Бизнес.Точка{% endblock %}
-
-
-
-
-
-
- {% block styles %}{% endblock %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% if current_org %}
-
-
- {{ current_org.name }}
- {{ current_org.type == 'personal' ? 'Личное' : 'Бизнес' }}
-
- {% else %}
-
- Выберите организацию
-
- {% endif %}
-
- {% if current_org %}
-
- {% endif %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% include 'components/alerts.twig' %}
-
- {% block content %}{% endblock %}
-
-
-
-
-
-
-
-
-
-{% block scripts %}{% endblock %}
-
-
-// app/Views/layouts/public.twig
-
-
-
-
-
- Вход - Бизнес.Точка
-
-
-
-
-{% include 'components/alerts.twig' %}
-
-{% block content %}{% endblock %}
-
-
-
-{% block scripts %}{% endblock %}
-
-
-// app/Views/pager/bootstrap_full.php
-setSurroundCount(2);
-?>
-
-
-
-
-// app/Views/errors/cli/production.php
-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
-
-
-
-
-
- = lang('Errors.whoops') ?>
-
-
-
-
-
= lang('Errors.whoops') ?>
-
= lang('Errors.weHitASnag') ?>
-
-
-
-
-// app/Views/errors/html/error_400.php
-
-
-
-
- = lang('Errors.badRequest') ?>
-
-
-
-
-
400
-
-
- = nl2br(esc($message)) ?>
-
- = lang('Errors.sorryBadRequest') ?>
-
-
-
-
-
-
-// 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
-
-
-
-
- = lang('Errors.pageNotFound') ?>
-
-
-
-
-
404
-
-
- = nl2br(esc($message)) ?>
-
- = lang('Errors.sorryCannotFind') ?>
-
-
-
-
-
-
-// app/Views/errors/html/error_exception.php
-
-
-
-
-
-
- = esc($title) ?>
-
-
-
-
-
-
-
-
-
= esc(clean_path($file)) ?> at line = esc($line) ?>
-
-
- = static::highlightFile($file, $line, 15); ?>
-
-
-
-
- getPrevious()) {
- $last = $prevException;
- ?>
-
- Caused by:
- = esc($prevException::class), esc($prevException->getCode() ? ' #' . $prevException->getCode() : '') ?>
- = nl2br(esc($prevException->getMessage())) ?>
- search →
- = esc(clean_path($prevException->getFile()) . ':' . $prevException->getLine()) ?>
-
-
-
-
-
-
-
-
-
-
- $row) : ?>
-
-
-
-
-
-
- {PHP internal code}
-
-
-
- — = esc($row['class'] . $row['type'] . $row['function']) ?>
-
-
- ( arguments )
-
-
- getParameters();
- }
- foreach ($row['args'] as $key => $value) : ?>
-
- = esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?>
- = esc(print_r($value, true)) ?>
-
-
-
-
-
- ()
-
-
-
- — = esc($row['function']) ?>()
-
-
-
-
-
- = static::highlightFile($row['file'], $row['line']) ?>
-
-
-
-
-
-
-
-
-
-
-
$= esc($var) ?>
-
-
-
- Key
- Value
-
-
-
- $value) : ?>
-
- = esc($key) ?>
-
-
- = esc($value) ?>
-
- = esc(print_r($value, true)) ?>
-
-
-
-
-
-
-
-
-
-
-
Constants
-
-
-
- Key
- Value
-
-
-
- $value) : ?>
-
- = esc($key) ?>
-
-
- = esc($value) ?>
-
- = esc(print_r($value, true)) ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Path
- = esc($request->getUri()) ?>
-
-
- HTTP Method
- = esc($request->getMethod()) ?>
-
-
- IP Address
- = esc($request->getIPAddress()) ?>
-
-
- Is AJAX Request?
- = $request->isAJAX() ? 'yes' : 'no' ?>
-
-
- Is CLI Request?
- = $request->isCLI() ? 'yes' : 'no' ?>
-
-
- Is Secure Request?
- = $request->isSecure() ? 'yes' : 'no' ?>
-
-
- User Agent
- = esc($request->getUserAgent()->getAgentString()) ?>
-
-
-
-
-
-
-
-
$= esc($var) ?>
-
-
-
- Key
- Value
-
-
-
- $value) : ?>
-
- = esc($key) ?>
-
-
- = esc($value) ?>
-
- = esc(print_r($value, true)) ?>
-
-
-
-
-
-
-
-
-
- No $_GET, $_POST, or $_COOKIE Information to show.
-
-
- headers(); ?>
-
-
Headers
-
-
-
- Header
- Value
-
-
-
- $value) : ?>
-
- = esc($name, 'html') ?>
-
- getValueLine(), 'html');
- } else {
- foreach ($value as $i => $header) {
- echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
- }
- }
- ?>
-
-
-
-
-
-
-
-
- setStatusCode(http_response_code());
- ?>
-
-
-
- Response Status
- = esc($response->getStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
-
-
- headers(); ?>
-
-
Headers
-
-
-
- Header
- Value
-
-
-
- $value) : ?>
-
- = esc($name, 'html') ?>
-
- getHeaderLine($name), 'html');
- } else {
- foreach ($value as $i => $header) {
- echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
- }
- }
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
- = esc(clean_path($file)) ?>
-
-
-
-
-
-
-
-
- Memory Usage
- = esc(static::describeMemory(memory_get_usage(true))) ?>
-
-
- Peak Memory Usage:
- = esc(static::describeMemory(memory_get_peak_usage(true))) ?>
-
-
- Memory Limit:
- = esc(ini_get('memory_limit')) ?>
-
-
-
-
-
-
-
-
-
-
-// app/Views/emails/verification.twig
-
-
-
-
-
- Подтверждение регистрации
-
-
-
-
-
-
{{ app_name }}
-
-
Добрый день, {{ name }}!
-
-
Спасибо за регистрацию в {{ app_name }}.
-
-
Для завершения регистрации и подтверждения вашего email адреса, пожалуйста, нажмите на кнопку ниже:
-
-
- Подтвердить email
-
-
-
Если кнопка не работает, вы можете скопировать ссылку и вставить её в адресную строку браузера:
-
-
- {{ verification_url }}
-
-
-
Ссылка действительна в течение 24 часов.
-
-
Если вы не регистрировались на {{ app_name }}, просто проигнорируйте это письмо.
-
-
-
-
-
-
-
-// app/Views/emails/welcome.twig
-
-
-
-
-
- Добро пожаловать
-
-
-
-
-
-
{{ app_name }}
-
-
Добро пожаловать, {{ name }}!
-
-
Поздравляем! Ваш email успешно подтверждён.
-
-
Теперь вы можете:
-
- Создавать и управлять организациями
- Приглашать сотрудников
- Использовать все функции платформы
-
-
-
- Перейти в личный кабинет
-
-
-
Если у вас возникнут вопросы, наша служба поддержки всегда готова помочь.
-
-
С уважением, Команда {{ app_name }}
-
-
-
-
-
-
-
-// app/Views/organizations/delete.twig
-{% extends 'layouts/base.twig' %}
-
-{% block content %}
-
-
-
-
-
Вы уверены, что хотите удалить организацию?
-
-
-
{{ organization.name }}
- {% if organization.inn %}
-
ИНН: {{ organization.inn }}
- {% endif %}
- {% if organization.type == 'personal' %}
-
Личное пространство
- {% else %}
-
Организация
- {% endif %}
-
-
-
-
Внимание! Это действие нельзя отменить.
- Будут удалены:
-
- Все данные организации
- Все связи с пользователями
- Все записи, связанные с организацией
-
-
-
- {% from 'macros/forms.twig' import form_open, form_close %}
-
- {{ form_open(base_url('/organizations/delete/' ~ organization.id)) }}
-
- {{ form_close() }}
-
-
-
-
-{% endblock %}
-
-// app/Views/organizations/edit.twig
-{% extends 'layouts/base.twig' %}
-
-{% block content %}
-
-
-
-
-
- {% from 'macros/forms.twig' import form_open, form_close %}
-
- {{ form_open(base_url('/organizations/edit/' ~ organization.id)) }}
-
- {# Основная информация #}
-
Основная информация
-
-
Название организации *
-
- {% if errors.name is defined %}
-
{{ errors.name }}
- {% endif %}
-
-
-
-
- {# Регистрационные данные #}
-
Регистрационные данные
-
-
-
-
- {# Адреса #}
-
Адреса
-
- Юридический адрес
-
-
-
-
Фактический адрес
-
-
Если совпадает с юридическим — оставьте пустым
-
-
-
-
- {# Контакты #}
-
Контакты
-
-
-
-
- {# Банковские реквизиты #}
-
Банковские реквизиты
-
- Название банка
-
-
-
-
-
-
- {{ form_close() }}
-
-
-
-
-{% endblock %}
-
-// app/Views/organizations/create.twig
-{% extends 'layouts/base.twig' %}
-
-{% block content %}
-
-
-
-
-
- {% from 'macros/forms.twig' import form_open, form_close %}
-
- {{ form_open(base_url('/organizations/create')) }}
-
- {# Основная информация #}
-
Основная информация
-
-
Название организации *
-
- {% if errors.name is defined %}
-
{{ errors.name }}
- {% endif %}
-
-
-
-
- {# Регистрационные данные #}
-
Регистрационные данные
-
-
-
-
- {# Адреса #}
-
Адреса
-
- Юридический адрес
-
-
-
-
Фактический адрес
-
-
Если совпадает с юридическим — оставьте пустым
-
-
-
-
- {# Контакты #}
-
Контакты
-
-
-
-
- {# Банковские реквизиты #}
-
Банковские реквизиты
-
- Название банка
-
-
-
-
-
-
- {{ form_close() }}
-
-
-
-
-{% endblock %}
-
-// app/Views/organizations/index.twig
-{% extends 'layouts/base.twig' %}
-
-{% block content %}
-
-
-
- {% if organizations is empty %}
-
- {% else %}
-
- {% for org in organizations %}
- {# Декодируем JSON реквизиты #}
- {% set req = org.requisites ? org.requisites|json_decode : {} %}
-
-
-
-
-
-
- {% if org.type == 'personal' %}
-
- {% else %}
-
- {% endif %}
- {{ org.name }}
-
-
- {% if org.type == 'personal' %}
- Личное пространство
- {% else %}
- {% if req.inn %}ИНН: {{ req.inn }}{% endif %}
- {% endif %}
-
-
- {% if org.id == session.get('active_org_id') %}
-
Активна
- {% endif %}
-
-
- {# Краткая информация #}
- {% if req.phone or req.email %}
-
- {% if req.phone %}
-
- {{ req.phone }}
-
- {% endif %}
- {% if req.email %}
-
- {{ req.email }}
-
- {% endif %}
-
- {% endif %}
-
-
- {# Кнопка выбора/переключения #}
- {% if org.id != session.get('active_org_id') %}
-
- Выбрать
-
- {% endif %}
-
- {# Кнопка редактирования #}
-
- Ред.
-
-
- {# Кнопка удаления (только для бизнес-организаций) #}
- {% if org.type == 'business' %}
-
- Удалить
-
- {% endif %}
-
-
-
-
-
- {% endfor %}
-
- {% endif %}
-
- {# Статистика #}
-
- Всего организаций: {{ count }}
-
-
-{% endblock %}
-
-// app/Views/dashboard/index.twig
-{# app/Views/dashboard/index.twig #}
-{% extends 'layouts/base.twig' %}
-
-{% block content %}
-
-
-
-
-
{% if current_org %}Добро пожаловать в {{ current_org.name }}!{% else %}Добро пожаловать!{% endif %}
-
Ваш личный кабинет для управления бизнесом.
-
-
-
-
-
-
-
-
-
-
CRM
-
Управление клиентами
-
Скоро
-
-
-
-
-
-
-
-
-
Booking
-
Запись на приём
-
Скоро
-
-
-
-
-
-
-
-
-
Proof
-
Согласование файлов
-
Скоро
-
-
-
-
-
-
-
-
-
Tasks
-
Задачи и проекты
-
Скоро
-
-
-
-
-
-{% endblock %}
-// app/Views/welcome_message.php
-
-
-
-
- Welcome to CodeIgniter 4!
-
-
-
-
-
-
-
-
-
-
-
- About this page
- The page you are looking at is being generated dynamically by CodeIgniter.
- If you would like to edit this page you will find it located at:
- app/Views/welcome_message.php
- The corresponding controller for this page can be found at:
- app/Controllers/Home.php
-
-
-
- Go further
-
-
- 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 CodeIgniter's forum , or chat on Slack !
-
- CodeIgniter Foundation. CodeIgniter is open source project released under the MIT
- open source licence.
-
-
-
-
-
-
-
-
-// app/Views/landing/index.twig
-{# app/Views/Landing/index.twig #}
-
-
-
-
- Бизнес.Точка - Автоматизация для бизнеса
-
-
-
-
-
-
-
- Бизнес.Точка
-
-
-
-
-
Всё для вашего бизнеса
-
CRM, Запись клиентов, Задачи и Согласование файлов в одном месте.
-
-
-
-
-
-
-// app/Views/auth/register_success.twig
-{% extends 'layouts/public.twig' %}
-
-{% block content %}
-
-
-
-
-
-
-
-
Регистрация успешна!
-
-
- Мы отправили письмо с ссылкой для подтверждения email на вашу почту.
-
-
-
-
-
Что делать дальше:
-
- Откройте почтовый ящик
- Найдите письмо от нас
- Нажмите на ссылку для подтверждения
-
-
-
-
-
-
-
-
- На главную
-
-
-
-
-{% endblock %}
-
-// app/Views/auth/resend_verification.twig
-{% extends 'layouts/public.twig' %}
-
-{% block content %}
-
-
-
-
-
-
Повторная отправка письма
-
Введите ваш email для получения новой ссылки подтверждения
-
-
- {% from 'macros/forms.twig' import form_open, form_close %}
-
- {{ form_open(base_url('/auth/resend-verification')) }}
-
-
-
Email
-
- {% if errors.email is defined %}
-
{{ errors.email }}
- {% endif %}
-
-
-
-
- {{ form_close() }}
-
-
-
-
-{% endblock %}
-
-// app/Views/auth/register.twig
-{% extends 'layouts/public.twig' %}
-
-{% block content %}
-
-
-
-
Регистрация
-
- {# ИСПОЛЬЗУЕМ МАКРОС form_open. CSRF добавится АВТОМАТИЧЕСКИ #}
- {{ form_open(base_url('/register'), 'class="needs-validation"') }}
-
-
- Ваше имя
-
-
-
- Email
-
-
-
- Пароль
-
-
-
Создать аккаунт
-
- {# ЗАКРЫВАЕМ ФОРМУ МАКРОСОМ #}
- {{ form_close() }}
-
-
-
Уже есть аккаунт? Войти
-
-
-
-
-{% endblock %}
-// app/Views/auth/verify_error.twig
-{% extends 'layouts/public.twig' %}
-
-{% block content %}
-
-
-
-
-
-
-
-
Ошибка подтверждения
-
-
- {{ message|default('Недействительная ссылка для подтверждения.') }}
-
-
-
-
-
-
-
-
-
-
-{% endblock %}
-
-// app/Views/auth/verify_success.twig
-{% extends 'layouts/public.twig' %}
-
-{% block content %}
-
-
-
-
-
-
-
-
Email подтверждён!
-
-
- {% if name %}
- {{ name }}, спасибо за подтверждение.
- {% else %}
- Спасибо за подтверждение.
- {% endif %}
-
-
-
- Теперь вы можете войти в систему и начать работу.
-
-
-
-
-
-
-{% endblock %}
-
-// app/Views/auth/login.twig
-{% extends 'layouts/public.twig' %}
-
-{% block content %}
-
-
-
-
-
Бизнес.Точка
-
Вход в систему
-
-
- {{ form_open(base_url('/login'), 'class="needs-validation"') }}
-
-
- Email
-
-
-
- Пароль
-
-
-
Войти
-
- {{ form_close() }}
-
-
-
Нет аккаунта? Зарегистрироваться
-
-
-
-
-{% endblock %}
-// app/Views/components/alerts.twig
-{# app/Views/components/alerts.twig #}
-
-{% set alerts = get_alerts() %}
-
-{% if alerts is not empty %}
-
- {% for alert in alerts %}
- {# Преобразуем наш тип 'error' в класс Bootstrap 'danger' #}
- {% set bs_type = alert.type == 'error' ? 'danger' : alert.type %}
-
-
-
-
- {{ alert.message }}
-
-
-
-
- {% endfor %}
-
-
-
-{% 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 %}
-
-
-
-// app/Views/components/table/table_header.twig
-{#
- table_header.twig - Переиспользуемый шаблон заголовка таблицы
-
- Параметры:
- - columns: Ассоциативный массив столбцов ['name' => ['label' => 'Name', 'width' => '40%']]
- - sort: Текущий столбец сортировки
- - order: Направление сортировки (asc/desc)
- - filters: Текущие значения фильтров
-#}
-
-
- {% for columnKey, column in columns %}
-
-
-
- {% endfor %}
-
- {# Колонка действий (опционально) #}
- {% if actions is defined and actions %}
-
- {{ actions.label|default('Действия') }}
-
- {% endif %}
-
-
-
-// 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() }}
-
-{% endblock %}
-
-{% block scripts %}
-{{ parent() }}
-
-
-{% 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 #}
-
- {% for item in items %}
-
- {{ item.name }}
- {{ item.price }}
-
- Редактировать
-
-
- {% endfor %}
-
-
- {% if items is not empty and pager %}
-
-
-
- {{ include('@components/table/pagination.twig', {
- pagination: paginationData,
- id: 'your-table-id'
- }) }}
-
-
-
- {% endif %}
-{% else %}
- {# Обычный запрос: полная таблица #}
-
- {{ include('@components/table/table.twig', {
- id: 'your-table-id',
- url: '/your-module/table',
- perPage: perPage,
- columns: columns,
- pagination: paginationData,
- actions: { label: 'Действия' },
- emptyMessage: 'Нет данных'
- }) }}
-
-{% 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 %}
-
- {{ client.name }}
- {{ client.email }}
-
-
-
-
-
-
-{% endfor %}
-```
-
-### Кастомные строки
-
-Компонент поддерживает произвольное содержимое ячеек через параметр `rows`:
-
-```twig
-{% set rows = [] %}
-{% for product in products %}
-{% set rows = rows|merge([{
- cells: [
- { content: '' ~ product.name ~ ' ', class: '' },
- { content: product.price ~ ' ₽', class: 'text-end' }
- ],
- actions: 'Редактировать '
-}]) %}
-{% 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')
-#}
-
-
- {# Заголовок таблицы #}
- {{ include('@components/table/table_header.twig', {
- columns: columns,
- sort: sort|default(''),
- order: order|default('asc'),
- filters: filters|default({}),
- actions: actions|default(false)
- }) }}
-
- {# Тело таблицы #}
-
- {% if rows is defined and rows|length > 0 %}
- {# Старый формат: предварительно построенные строки #}
- {% for row in rows %}
-
- {% for cell in row.cells %}
- {{ cell.content|raw }}
- {% endfor %}
- {% if row.actions is defined %}
-
- {{ row.actions|raw }}
-
- {% endif %}
-
- {% endfor %}
- {% elseif items is defined and items|length > 0 %}
- {# Новый формат: автоматический рендеринг из объектов модели #}
- {% set columnKeys = columns|keys %}
- {% for item in items %}
-
- {# Ячейки данных #}
- {% for columnKey in columnKeys %}
- {{ attribute(item, columnKey)|default('—') }}
- {% endfor %}
-
- {# Колонка действий #}
- {% if actions is defined and actions %}
-
- {#Placeholder для действий - переопределяется в дочерних шаблонах#}
- {% if item.actions is defined %}{{ item.actions|raw }}{% endif %}
-
- {% endif %}
-
- {% endfor %}
- {% else %}
- {# Пустое состояние #}
-
-
- {{ emptyMessage|default('Нет данных для отображения') }}
-
-
- {% endif %}
-
-
- {# Футер с пагинацией - показываем всегда #}
-
-
-
- {{ 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
- }) }}
-
-
-
-
-
-
-// app/Views/macros/forms.twig
-{# app/Views/macros/forms.twig #}
-{% macro form_open(action, attributes = '') %}
-
-{% endmacro %}
-// app/.htaccess
-
- Require all denied
-
-
- Deny from all
-
-
-// app/Filters/OrganizationFilter.php
-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
-where('owner_id', $userId)->findAll();
- }
-}
-// app/Models/.gitkeep
-
-// app/Models/OrganizationUserModel.php
-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
- [
- '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
-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
-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
-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
-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
- [
- '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
- 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
- GDHandler::class,
- 'imagick' => ImageMagickHandler::class,
- ];
-}
-
-// app/Config/ContentSecurityPolicy.php
- '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
- APPPATH,
- 'App\Modules' => APPPATH . 'Modules',
- 'App\Libraries\Twig' => APPPATH . 'Libraries/Twig',
- ];
- /**
- public $classmap = [];
- /**
- public $files = [];
- /**
- public $helpers = [];
-}
-
-// app/Config/Exceptions.php
- 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
- [],
- /**
- 'allowedOriginsPatterns' => [],
- /**
- 'supportsCredentials' => false,
- /**
- 'allowedHeaders' => [],
- /**
- 'exposedHeaders' => [],
- /**
- 'allowedMethods' => [],
- /**
- 'maxAge' => 7200,
- ];
-}
-
-// app/Config/Honeypot.php
-{label} ';
- /**
- public string $container = '{template}
';
- /**
- public string $containerId = 'hpc';
-}
-
-// app/Config/Mimes.php
- [
- '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
- '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
- '*',
- 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
-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
- '',
- '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
- [
- 'handles' => [
- 'critical',
- 'alert',
- 'emergency',
- 'debug',
- 'error',
- 'info',
- 'notice',
- 'warning',
- ],
- /*
- 'fileExtension' => '',
- /*
- 'filePermissions' => 0644,
- /*
- 'path' => '',
- ],
- /*
- /*
- ];
-}
-
-// app/Config/Events.php
- 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
- ' ' ' ' ' ' ' ' ' 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
-]+\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
- 'CodeIgniter\Validation\Views\list',
- 'single' => 'CodeIgniter\Validation\Views\single',
- ];
-}
-
-// app/Config/Modules.php
-
-
-
-
-
-
- {{ forms.form_open(client ? base_url('/clients/update/' ~ client.id) : base_url('/clients/create')) }}
-
-
-
Имя / Название *
-
- {% if errors.name %}
-
{{ errors.name }}
- {% endif %}
-
ФИО клиента или название компании
-
-
-
-
-
Email
-
- {% if errors.email %}
-
{{ errors.email }}
- {% endif %}
-
-
-
-
Телефон
-
- {% if errors.phone %}
-
{{ errors.phone }}
- {% endif %}
-
-
-
-
-
Заметки
-
- {% if errors.notes %}
-
{{ errors.notes }}
- {% endif %}
-
-
-
-
Отмена
-
-
- {{ client ? 'Сохранить изменения' : 'Добавить клиента' }}
-
-
- {{ forms.form_close() }}
-
-
-
-
-{% 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 #}
-
-{% if isEmpty %}
-
-
-
-
- {% if filters.name or filters.email or filters.phone %}
- Клиенты не найдены
- {% else %}
- Клиентов пока нет
- {% endif %}
-
-
- Добавить клиента
-
-
-
-{% else %}
-{% for client in clients %}
-
-
-
-
- {{ client.name|first|upper }}
-
-
- {{ client.name }}
- {% if client.notes %}
- {{ client.notes|slice(0, 50) }}{{ client.notes|length > 50 ? '...' : '' }}
- {% endif %}
-
-
-
-
- {% if client.email %}
- {{ client.email }}
- {% else %}
- —
- {% endif %}
-
-
- {% if client.phone %}
- {{ client.phone }}
- {% else %}
- —
- {% endif %}
-
-
-
-
-
-
-
-
-
-
-{% endfor %}
-{% endif %}
-
-{% if not isEmpty and pager %}
-
-
-
- {{ include('@components/table/pagination.twig', { pagination: pagination, id: 'clients-table' }) }}
-
-
-
-{% endif %}
-
-// app/Modules/Clients/Views/index.twig
-{% extends 'layouts/base.twig' %}
-
-{% block content %}
-
-
-
Клиенты
-
Управление клиентами вашей организации
-
-
- Добавить клиента
-
-
-
-
-
-
- {# Формируем строки таблицы из клиентов #}
- {% set tableRows = [] %}
- {% if clients is defined and clients|length > 0 %}
- {% for client in clients %}
- {% set tableRows = tableRows|merge([{
- cells: [
- {
- content: '
-
' ~ client.name|first|upper ~ '
-
' ~ client.name ~ ' ' ~ (client.notes ? '' ~ client.notes|slice(0, 50) ~ (client.notes|length > 50 ? '...' : '') ~ ' ') ~ '
-
',
- class: ''
- },
- {
- content: client.email ? '
' ~ client.email ~ ' ' : '
— ',
- class: ''
- },
- {
- content: client.phone ? '
' ~ client.phone ~ ' ' : '
— ',
- class: ''
- }
- ],
- actions: '
-
'
- }]) %}
- {% endfor %}
- {% endif %}
-
-
- {{ 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 }}
-
-
-{% endblock %}
-
-{% block stylesheets %}
-{{ parent() }}
-
-{% endblock %}
-
-{% block scripts %}
-{{ parent() }}
-
-
-{% endblock %}
-
-// app/Modules/Clients/Models/ClientModel.php
-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
-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
-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
-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 не подтверждён. Проверьте почту или запросите письмо повторно .');
- 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
-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
-get('isLoggedIn')) {
- return redirect()->to('/organizations');
- }
- return $this->renderTwig('landing/index');
- }
-}
-// app/Controllers/Organizations.php
-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
-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 - Универсальные стили для интерактивных таблиц
- *
- * Подключение:
- */
-
-/* Основной контейнер таблицы */
-.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 = `
-
-
-
- Загрузка...
-
-
-
- `;
- }
- 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(/]*>[\s\S]*?<\/tbody>/i);
- const tfootMatch = html.match(/ ]*>[\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 = `
-
-
- Ошибка загрузки данных. Пожалуйста, обновите страницу.
-
-
- `;
- }
- }
- /**
- 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.
-
- 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}]
-
-
-
- # 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
-
-
-# Disable server signature start
-ServerSignature Off
-# Disable server signature end
-
-// public/index.php
-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
-
diff --git a/docs/ACCESS.md b/docs/ACCESS.md
new file mode 100644
index 0000000..70d6791
--- /dev/null
+++ b/docs/ACCESS.md
@@ -0,0 +1,296 @@
+# Справка по методам проверки прав доступа
+
+## AccessService — доступ через сервис
+
+AccessService подключается автоматически в BaseController как `$this->access`.
+
+```php
+// В контроллере:
+if (!$this->access->can('create', 'clients')) {
+ return $this->forbiddenResponse('Нет прав для создания клиентов');
+}
+```
+
+---
+
+## Методы проверки ролей
+
+### Проверка конкретной роли
+
+```php
+// Одной роли
+$this->access->isRole('owner');
+
+// Нескольких ролей
+$this->access->isRole(['owner', 'admin']);
+```
+
+### Удобные методы для часто используемых проверок
+
+```php
+// Владелец организации
+$this->access->isOwner();
+
+// Администратор или владелец
+$this->access->isAdmin();
+
+// Менеджер, администратор или владелец
+$this->access->isManagerOrHigher();
+```
+
+### Системные роли (суперадмин)
+
+```php
+// Суперадмин (доступ к панели суперадмина)
+$this->access->isSuperadmin();
+
+// Системный админ или суперадмин
+$this->access->isSystemAdmin();
+
+// Проверка произвольной системной роли
+$this->access->isSystemRole('admin');
+```
+
+---
+
+## Методы проверки прав на действия
+
+### Универсальный метод can()
+
+```php
+// Проверка конкретного действия над ресурсом
+$this->access->can('view', 'clients'); // Просмотр клиентов
+$this->access->can('create', 'clients'); // Создание клиентов
+$this->access->can('edit', 'clients'); // Редактирование клиентов
+$this->access->can('delete', 'clients'); // Удаление своих клиентов
+$this->access->can('delete_any', 'clients'); // Удаление любых клиентов
+```
+
+### Краткие методы для действий
+
+```php
+$this->access->canView('clients'); // Эквивалент can('view', 'clients')
+$this->access->canCreate('clients'); // Эквивалент can('create', 'clients')
+$this->access->canEdit('clients'); // Эквивалент can('edit', 'clients')
+$this->access->canDelete('clients'); // Эквивалент can('delete', 'clients')
+$this->access->canDelete('clients', true); // Эквивалент can('delete_any', 'clients')
+```
+
+### Права на специальные операции
+
+```php
+// Управление пользователями организации
+$this->access->canManageUsers();
+
+// Управление модулями (подписки)
+$this->access->canManageModules();
+
+// Просмотр финансовой информации
+$this->access->canViewFinance();
+
+// Удаление организации
+$this->access->canDeleteOrganization();
+
+// Передача прав владельца
+$this->access->canTransferOwnership();
+```
+
+---
+
+## Доступные ресурсы и действия
+
+### Стандартные ресурсы модулей
+
+| Ресурс | Описание | Доступные действия |
+|------------|----------------|-------------------------|
+| `clients` | Клиенты CRM | view, create, edit, delete, delete_any |
+| `deals` | Сделки CRM | view, create, edit, delete, delete_any |
+| `bookings` | Записи на приём | view, create, edit, delete, delete_any |
+| `projects` | Проекты Proof | view, create, edit, delete, delete_any |
+| `tasks` | Задачи | view, create, edit, delete, delete_any |
+| `users` | Пользователи | view, create, edit, delete |
+
+---
+
+## Матрица прав по ролям
+
+| Ресурс | Владелец | Администратор | Менеджер | Гость |
+|-------------|----------|---------------|----------|-------|
+| Клиенты | Полный | Полный | Полный | Просмотр |
+| Сделки | Полный | Полный | Полный | Просмотр |
+| Записи | Полный | Полный | Полный | Просмотр |
+| Проекты | Полный | Полный | Полный | Просмотр |
+| Задачи | Полный | Полный | Полный | Просмотр |
+| Пользователи| Полный | Просмотр, создание, редактирование | Только просмотр | Просмотр |
+| Модули | Полный | Управление | — | — |
+| Финансы | Полный | Просмотр | — | — |
+
+---
+
+## Использование в Twig-шаблонах
+
+Хелпер `access` автоматически доступен в шаблонах через TwigGlobalsExtension.
+
+### Проверка ролей
+
+```twig
+{# Проверка роли пользователя #}
+{% if access.isRole('owner') %}
+ Вы владелец организации
+{% endif %}
+
+{% if access.isRole(['owner', 'admin']) %}
+ Вы администратор или владелец
+{% endif %}
+
+{# Удобные проверки #}
+{% if access.isOwner() %}
+ Кнопка "Удалить организацию"
+{% endif %}
+
+{% if access.isAdmin() %}
+ Кнопка "Управление пользователями"
+{% endif %}
+```
+
+### Проверка действий
+
+```twig
+{# Кнопка создания (видима только если есть право create) #}
+{% if access.canCreate('clients') %}
+
+ Добавить клиента
+
+{% endif %}
+
+{# Кнопка редактирования #}
+{% if access.canEdit('clients') %}
+ Редактировать
+{% endif %}
+
+{# Кнопка удаления #}
+{% if access.canDelete('clients') %}
+ Удалить
+{% endif %}
+```
+
+### Проверка специальных прав
+
+```twig
+{# Управление пользователями #}
+{% if access.canManageUsers() %}
+
+ Управление пользователями
+
+{% endif %}
+
+{# Управление модулями #}
+{% if access.canManageModules() %}
+ Управление подписками
+{% endif %}
+
+{# Удаление организации (только владелец) #}
+{% if access.canDeleteOrganization() %}
+
+ Удалить организацию
+
+{% endif %}
+```
+
+---
+
+## Примеры использования в контроллерах
+
+### Базовый шаблон проверки
+
+```php
+public function index()
+{
+ // Проверка права на просмотр
+ if (!$this->access->canView('clients')) {
+ return $this->forbiddenResponse('Нет прав для просмотра клиентов');
+ }
+
+ // ... логика метода
+}
+```
+
+### Проверка нескольких условий
+
+```php
+public function delete($id)
+{
+ // Право на удаление
+ if (!$this->access->canDelete('clients')) {
+ return $this->forbiddenResponse('Нет прав для удаления');
+ }
+
+ // Дополнительная проверка: только владелец может удалять
+ if (!$this->access->isOwner()) {
+ return $this->forbiddenResponse('Только владелец может удалять');
+ }
+
+ // ... логика удаления
+}
+```
+
+### Условное выполнение в зависимости от роли
+
+```php
+public function update($id, $data)
+{
+ // Менеджер может только редактировать свои записи
+ // Админ и владелец — любые записи
+ $canEdit = $this->access->isRole('manager')
+ ? $this->isOwnerOfRecord($id) // своя запись?
+ : true; // любую запись
+
+ if (!$canEdit) {
+ return $this->forbiddenResponse('Можно редактировать только свои записи');
+ }
+
+ // ... обновление
+}
+```
+
+---
+
+## Важные примечания
+
+1. **Методы возвращают boolean** — используйте в условиях `if`
+
+2. **Проверка всегда идёт для текущей организации** из сессии (`active_org_id`)
+
+3. **Для личного пространства** (`type = 'personal'`) метод `canManageUsers()` возвращает `false` — в личном пространстве нет других пользователей
+
+4. **Системные роли** (`system_role`) проверяются отдельно от ролей организации:
+ - `isRole()` и `isOwner()`, `isAdmin()` — для организации
+ - `isSuperadmin()`, `isSystemAdmin()` — для всей системы
+
+5. **Кэширование** — `AccessService` кэширует membership в рамках одного запроса, но не между запросами. При переключении организации кэш сбрасывается автоматически.
+
+---
+
+## Получение текстового названия роли
+
+```php
+// В контроллере
+$roleLabel = $this->access->getRoleLabel('admin'); // "Администратор"
+
+// В шаблоне
+{{ access.getRoleLabel(currentMembership.role) }}
+```
+
+---
+
+## Список всех ролей
+
+```php
+// Получение всех ролей с описаниями
+$roles = \App\Services\AccessService::getAllRoles();
+// [
+// 'owner' => ['label' => 'Владелец', 'description' => 'Полный доступ', 'level' => 100],
+// 'admin' => ['label' => 'Администратор', 'description' => 'Управление пользователями', 'level' => 75],
+// ...
+// ]
+```
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
new file mode 100644
index 0000000..809f244
--- /dev/null
+++ b/docs/ARCHITECTURE.md
@@ -0,0 +1,119 @@
+# Архитектура системы
+
+## 1. Организации и типы пространств
+
+### 1.1. Концепция организации в системе
+
+Центральным понятием архитектуры системы является организация (`organization`), которая представляет собой контейнер для всех бизнес-данных и определяет границы доступа между различными пользователями и командами. Все данные системы, за исключением глобальных настроек и системных конфигураций, принадлежат конкретной организации и изолированы на уровне базы данных. Каждая запись в таблицах организационного пространства содержит поле `organization_id`, которое связывает её с конкретной организацией и обеспечивает автоматическую фильтрацию данных при работе с моделями.
+
+Организация в системе имеет несколько ключевых характеристик, определяющих её поведение и ограничения. К таким характеристикам относятся тип организации, статус подписки на модули, список пользователей с их ролями и дата создания. Информация об организации хранится в таблице `organizations` и доступна через соответствующую модель `OrganizationModel`. При аутентификации пользователя система определяет текущую организацию из сессии и использует её контекст для всех последующих операций с данными. Связь пользователя с организацией хранится в таблице `organization_users`, которая определяет роль пользователя в данной организации.
+
+### 1.2. Типы организаций
+
+Система поддерживает два основных типа организаций, каждый из которых имеет свои особенности и ограничения. Первый тип — это стандартная организация, предназначенная для компаний и команд, которым необходимо совместная работа нескольких пользователей. Стандартная организация может содержать неограниченное количество пользователей с различными ролями (Owner, Admin, Manager), имеет полный доступ ко всем функциям системы и может оформлять подписки на модули для всей команды. Этот тип организации является основным сценарием использования системы для бизнес-целей.
+
+Второй тип — личная организация (personal organization), которая создаётся для индивидуальных пользователей, таких как физические лица, индивидуальные предприниматели или самозанятые. Личная организация имеет принципиальное отличие от стандартной: в неё невозможно приглашать других пользователей. Владелец личной организации является её единственным участником и автоматически получает роль Owner. Личные организации используют ту же самую архитектуру и механизмы, что и стандартные организации, но имеют специальный флаг `is_personal = true`, который блокирует функционал приглашения пользователей и управления составом команды. Этот подход обеспечивает единообразие кода и позволяет использовать общие компоненты для обоих типов организаций.
+
+### 1.3. Мультитенантность и изоляция данных
+
+Изоляция данных между организациями обеспечивается на нескольких уровнях архитектуры системы, что гарантирует безопасность и конфиденциальность данных каждой организации. На уровне базы данных каждая таблица организационного пространства содержит поле `organization_id`, которое является внешним ключом к таблице организаций. Это поле имеет ограничение внешнего ключа и обеспечивает ссылочную целостность данных. При создании новой записи система автоматически устанавливает `organization_id` из текущей сессии пользователя, что гарантирует принадлежность записи к конкретной организации и исключает возможность случайного или намеренного создания записей в другой организации.
+
+На уровне приложения изоляция обеспечивается через trait `TenantScopedModel`, который автоматически добавляет условие `WHERE organization_id = session->get('organization_id')` ко всем запросам выборки. Это происходит прозрачно для разработчика — при использовании модели с этим trait не требуется явная фильтрация по организации. Trait также перехватывает методы создания, обновления и удаления, автоматически устанавливая `organization_id` для новых записей и предотвращая модификацию записей других организаций. Попытка обновить запись с `organization_id`, отличным от текущей сессии, приведёт к исключению `OrganizationMismatchException`. Важно понимать, что `TenantScopedModel` обеспечивает только фильтрацию данных — проверка прав доступа должна выполняться отдельно через `AccessService`.
+
+## 2. Система ролей
+
+### 2.1. Роли и их иерархия
+
+Система реализует иерархическую модель ролей с чётким распределением полномочий между участниками организации. Роль **Owner** (Владелец) является высшей ролью в организации и принадлежит пользователю, который создал организацию. Владелец имеет полный доступ ко всем функциям системы без ограничений, включая управление подпиской организации, удаление организации, назначение и снятие ролей с других пользователей. Владелец не может быть удалён из организации и всегда сохраняет свои полномочия независимо от изменений в подписке. Система гарантирует, что в каждой организации есть как минимум один владелец, и эта роль не может быть передана или снята последним владельцем.
+
+Роль **Admin** (Администратор) предоставляется ключевым сотрудникам для управления повседневными операциями организации. Администратор имеет доступ ко всем функциям модулей, на которые оформлена подписка, за исключением финансовых операций и управления подпиской. Администратор может управлять пользователями организации (приглашать, удалять, изменять роли), но не может удалить владельца или изменить его роль. Администратор также не может управлять биллингом организации. Роль Admin предназначена для делегирования административных полномочий без передачи прав на изменение структуры организации или финансовых обязательств.
+
+Роль **Manager** (Менеджер) предоставляется сотрудникам, которые работают с клиентами и сделками. Менеджер имеет доступ к функциям в рамках своих прав, определённых матрицей разрешений. Менеджер может создавать и редактировать клиентов, сделки и задачи, но не может удалять критически важные данные или управлять пользователями организации. Точный набор прав менеджера зависит от настроек конкретного модуля и может быть настроен индивидуально. Роль Manager является базовой ролью для обычных пользователей организации, которые несут ответственность за выполнение рабочих задач, но не управляют командой или системными настройками.
+
+### 2.2. Матрица разрешений
+
+Матрица разрешений определяет, какие действия доступны пользователям с каждой ролью для различных ресурсов системы. Матрица хранится в конфигурации сервиса `AccessService` и представляет собой ассоциативный массив, где ключом является название ресурса (например, `clients`, `deals`, `invoices`), а значением — массив допустимых действий для каждой роли. Каждое действие описывается строкой (`view`, `create`, `edit`, `delete`), которая соответствует методу проверки в сервисе доступа. При проверке прав сервис сравнивает роль пользователя и запрашиваемое действие с матрицей разрешений.
+
+Сервис `AccessService` предоставляет набор методов для проверки разрешений: `canView($resource)`, `canCreate($resource)`, `canEdit($resource)`, `canDelete($resource)` и `isOwner()`. Метод `canView` проверяет возможность просмотра ресурса, `canCreate` — создания новых записей, `canEdit` — редактирования существующих записей, `canDelete` — удаления записей. Метод `isOwner` проверяет, является ли текущий пользователь владельцем организации, и возвращает `true` для владельцев всех организаций. При отсутствии явного разрешения в матрице для данной роли и ресурса метод возвращает `false`, что приводит к генерации исключения `AccessDeniedException` или возврату ошибки в контроллере.
+
+## 3. Роутинг и структура URL
+
+### 3.1. Организация маршрутов
+
+Система использует стандартный роутинг CodeIgniter 4 с дополнительной логикой фильтрации и проверки доступа. Базовые маршруты определены в файле `app/Config/Routes.php` и следуют RESTful-паттернам с учётом специфики мультитенантности. Все маршруты, связанные с организационным пространством, проходят через фильтры аутентификации и проверки организации. Фильтр `auth` проверяет наличие авторизованного пользователя в сессии и перенаправляет на страницу входа при отсутствии. Фильтр `tenant` проверяет корректность контекста организации и валидность подписок на модули.
+
+Структура URL строится по принципу `/{module}/{controller}/{action}/{id?}`. Модули организованы в отдельные директории внутри `app/Modules/`, что позволяет добавлять новую функциональность без изменения основного ядра. Роуты модулей регистрируются через метод `$routes->group('modules', ['filter' => 'auth'], function ($routes) {...})`, который применяет фильтр аутентификации ко всем маршрутам внутри группы. Это обеспечивает централизованную проверку авторизации и упрощает управление доступом. Вложенные группы маршрутов используются для дополнительной логики, такой как проверка подписки на модуль или применение специфичных бизнес-правил.
+
+### 3.2. Контекст организации в URL
+
+Роутинг в системе построен таким образом, что URL не содержит явного указания организации — контекст определяется из сессии пользователя. Это обеспечивает безопасность и предотвращает попытки получить доступ к данным чужой организации через манипуляцию URL. При переключении между организациями система обновляет значения `organization_id` и `role` в сессии пользователя, сохраняя при этом `user_id` неизменным. Это позволяет пользователю иметь доступ к нескольким организациям и работать с данными каждой из них в изолированном контексте без необходимости повторной аутентификации.
+
+Все контроллеры организационного пространства автоматически получают доступ к `organization_id` через `TenantScopedModel` и не требуют явного указания организации в URL. При попытке доступа к данным другой организации система автоматически вернёт ошибку 403 или перенаправит на страницу ошибки. Важно понимать, что при смене организации все загруженные в память данные становятся неактуальными и должны быть перезагружены в контексте новой организации. На практике это означает, что после переключения организации пользователю может потребоваться обновить страницу или система автоматически перенаправит его на соответствующую страницу.
+
+## 4. Типичные паттерны разработки
+
+### 4.1. Создание модели организационного пространства
+
+Создание новой модели организационного пространства требует следования установленным паттернам для обеспечения корректной работы мультитенантности. Модель должна наследоваться от базового класса `CodeIgniter\Model` или `App\Models\BaseModel` и использовать trait `TenantScopedModel`. В классе модели определяются защищённые свойства `$table`, `$primaryKey`, `$allowedFields` и `$useTimestamps` в соответствии со структурой таблицы базы данных. Trait `TenantScopedModel` автоматически обрабатывает поля `organization_id` и временные метки (`created_at`, `updated_at`, `deleted_at` для мягкого удаления).
+
+При реализации модели необходимо добавить поля организации и временных меток в массив `$allowedFields`, чтобы они автоматически заполнялись при создании и обновлении записей. Trait перехватывает метод `insert()` и автоматически устанавливает значение `organization_id` из сессии. Методы `update()` и `delete()` автоматически добавляют условие `WHERE organization_id = session->get('organization_id')` для предотвращения модификации записей других организаций. При использовании метода `withDeleted()` для получения мягко удалённых записей фильтрация по организации также применяется. После создания модель готова к использованию в контроллерах с автоматической фильтрацией по организации.
+
+### 4.2. Проверка прав доступа в контроллерах
+
+Проверка прав доступа в контроллерах осуществляется через сервис `AccessService`, который предоставляет набор методов для проверки разрешений. Типичный паттерн проверки прав в контроллере выглядит следующим образом: в начале метода контроллера вызывается проверка соответствующего разрешения, и если проверка не проходит, генерируется исключение или возвращается ошибка. Например, метод создания нового клиента начинается с проверки `if (!$this->access->canCreate('clients')) throw new \Exception('Access denied');`. Это обеспечивает централизованный контроль доступа и предотвращает несанкционированные действия.
+
+Все проверки прав доступа логируются через `EventManager` для аудита и отладки. При необходимости можно добавить дополнительные условия в проверку, например, проверить, что пользователь является владельцем конкретной записи, а не просто имеет право на редактирование этого типа ресурсов. Для этого используется метод `isOwner()` в сочетании с дополнительной проверкой идентификатора записи. Документация по доступным методам и ресурсам находится в файле `ACCESS_HELP.md`, а подробное описание матрицы разрешений — в исходном коде `AccessService`.
+
+### 4.3. Проверка прав доступа в шаблонах Twig
+
+Проверка прав доступа в шаблонах Twig осуществляется через глобальные переменные, доступные во всех шаблонах. Объект `access` автоматически передаётся в каждый шаблон и содержит все методы проверки разрешений. Это позволяет использовать проверку прав непосредственно в разметке шаблона для скрытия или отображения элементов интерфейса. Синтаксис проверки в Twig аналогичен синтаксису в контроллерах: `{% if access.canEdit('clients') %} ... {% endif %}`.
+
+Практическое применение проверки прав в шаблонах включает скрытие кнопок редактирования и удаления для пользователей без соответствующих прав, отображение информационных сообщений о недоступных функциях, адаптацию интерфейса под роли пользователей. Например, кнопка удаления клиента оборачивается в условие `{% if access.canDelete('clients') %}... {% endif %}`, и пользователи без права удаления не увидят эту кнопку. Это обеспечивает интуитивно понятный интерфейс и предотвращает попытки выполнить недоступные действия. Комбинирование проверок позволяет создавать сложные условия отображения элементов в зависимости от нескольких факторов.
+
+## 5. Система событий
+
+### 5.1. Типы событий
+
+Система событий позволяет расширять функциональность модулей без модификации их исходного кода. События делятся на два типа: системные события (`systemOn`) и модульные события (`moduleOn`). Системные события срабатывают глобально для всей системы и используются для сквозной функциональности, такой как логирование, аудит, интеграции. Модульные события срабатывают только при активной подписке на соответствующий модуль, что позволяет создавать расширения для модулей, которые могут быть включены или выключены в зависимости от подписки организации.
+
+Именование событий следует паттерну `{resource}.{action}`, например, `clients.create`, `deals.update`, `invoices.delete`. При срабатывании события обработчики получают контекст, который обычно включает модель, действие и связанные данные. Контекст события позволяет обработчикам получить доступ к изменённым данным, выполнить дополнительные операции или отменить действие (для событий, поддерживающих отмену). Система событий поддерживает приоритеты обработчиков, что позволяет контролировать порядок выполнения нескольких обработчиков одного события.
+
+### 5.2. Подписка на события
+
+Подписка на события осуществляется через сервис `EventManager` методом `on($eventName, $callback, $priority)`. Callback-функция получает контекст события и может выполнять произвольные операции. Пример подписки на событие создания клиента: `$events->on('clients.create', function ($context) { ... });`. События можно использовать для автоматической отправки уведомлений, синхронизации данных с внешними системами, ведения аудита действий пользователей. Подписка на события обычно выполняется в сервис-провайдерах или при инициализации модулей.
+
+Модульные события требуют дополнительной проверки подписки на модуль. При использовании метода `moduleOn($moduleName, $eventName, $callback)` система автоматически проверяет, активна ли подписка организации на указанный модуль, и регистрирует обработчик только при наличии подписки. Это позволяет создавать расширения для модулей, которые устанавливаются отдельно и активируются только при наличии лицензии. Документация по событиям и их использованию находится в файле `EVENT_MANAGER_HELP.md`.
+
+## 6. Компонент динамических таблиц
+
+### 6.1. Обзор компонента
+
+Компонент динамических таблиц (`DataTable`) позволяет создавать интерактивные таблицы данных с сортировкой, фильтрацией, пагинацией и действиями. Компонент состоит из двух частей: серверной (PHP-контроллер, возвращающий данные в стандартизированном формате) и клиентской (JavaScript-класс, выполняющий AJAX-запросы и отображение таблицы). Серверная часть должна возвращать данные в формате JSON с ключами `data` (массив записей), `recordsTotal` (общее количество записей), `recordsFiltered` (количество записей после фильтрации) и `draw` (счётчик запросов для защиты от CSRF).
+
+Клиентская часть компонента инициализируется для каждой таблицы на странице и автоматически загружает данные при отображении. Компонент поддерживает серверную пагинацию, сортировку по колонкам, текстовый поиск и фильтрацию по дополнительным параметрам. Для каждой строки таблицы могут отображаться кнопки действий (редактирование, удаление, просмотр), которые настраиваются через конфигурацию компонента. Компонент также поддерживает массовые действия с использованием чекбоксов в первой колонке таблицы.
+
+### 6.2. Использование в контроллере и шаблоне
+
+Для использования компонента в контроллере необходимо подготовить данные в стандартизированном формате JSON и передать их в шаблон. Контроллер должен получить параметры запроса (номер страницы, количество записей, параметры сортировки, поисковый запрос), выполнить запрос к базе данных с применением фильтров, и вернуть результат в требуемом формате. Параметры `page` и `perPage` используются для расчёта смещения (`offset`) при пагинации, параметры `orderBy` и `orderDir` — для сортировки, параметр `search` — для текстового поиска.
+
+В шаблоне Twig компонент подключается через макросы из файла `table.twig`, который находится в директории макросов модуля. Для отображения таблицы вызывается макрос `table.render()` с передачей конфигурационного объекта, содержащего параметры отображения и URL для загрузки данных. Конфигурация включает определение колонок (заголовок, поле данных, формат отображения), наличие чекбоксов для массовых действий, кнопки действий для каждой строки. Документация по параметрам и использованию компонента находится в файле `DATATABLE_HELP.md`.
+
+## 7. Развитие системы
+
+### 7.1. Создание нового модуля
+
+Создание нового модуля требует следования установленной структуре директорий и файловой организации. Модуль размещается в директории `app/Modules/{ModuleName}/` и содержит поддиректории для контроллеров (`Controllers/`), моделей (`Models/`), представлений (`Views/`) и ресурсов (`Assets/`). Каждый модуль должен иметь файл конфигурации подписки `Modules/{ModuleName}/Config/Subscription.php`, который определяет название модуля, описание, стоимость и зависимости от других модулей. Файл конфигурации также определяет события, которые генерирует модуль, и может содержать настройки по умолчанию.
+
+После создания структуры модуля необходимо зарегистрировать его роуты в файле `app/Config/Routes.php` внутри группы модулей с применением фильтра аутентификации. Контроллеры модуля должны наследоваться от `BaseController` для обеспечения доступа к общим сервисам и функциональности. Модели организационного пространства должны использовать trait `TenantScopedModel`. После создания модуль автоматически появится в списке доступных модулей в админке, и его можно будет активировать через систему подписок. При разработке модуля рекомендуется использовать существующие модули (Clients, Deals) в качестве образца структуры и паттернов.
+
+### 7.2. Интеграция внешних сервисов
+
+Интеграция внешнего сервиса осуществляется через создание адаптера в директории `app/Services/External/` и подписку на соответствующие события системы. Адаптер инкапсулирует логику взаимодействия с внешним API, включая аутентификацию, обработку ошибок и преобразование данных. После создания адаптера необходимо зарегистрировать его как сервис в `app/Config/Services.php` для удобного доступа из других частей приложения. Адаптеры должны быть независимыми от контекста организации и обрабатывать аутентификацию внешних сервисов через конфигурацию системы.
+
+Связывание внешнего сервиса с системой осуществляется через подписку на события. Например, для отправки SMS при создании заказа подписываемся на событие `orders.create` и вызываем метод адаптера отправки SMS. Это позволяет добавлять и удалять интеграции без изменения бизнес-логики модулей. Для сложных интеграций рекомендуется создавать отдельные классы-коннекторы с методами для каждого типа операции и использовать их из обработчиков событий. Все обращения к внешним сервисам должны быть обёрнуты в try-catch блоки для корректной обработки ошибок и логирования сбоев интеграции.
+
+### 7.3. Система подписок модулей
+
+Система подписок управляет доступом к функциональности модулей в зависимости от оплаченного плана организации. Каждый модуль имеет файл конфигурации, который определяет его стоимость, название, описание и требуемые разрешения. При активации модуля система проверяет, что у организации достаточно средств или соответствующий план подписки, и создаёт запись в таблице подписок. Статус подписки хранится в базе данных и проверяется при каждом доступе к функциональности модуля. Информация о текущих подписках доступна через `ModuleSubscriptionService`, который предоставляет методы для проверки статуса модуля, получения списка активных подписок и управления ими.
+
+Доступ к модульным событиям и функциональности автоматически ограничивается статусом подписки. При неактивной подписке попытка доступа к модулю возвращает ошибку 403 или перенаправляет на страницу оплаты. Система также обеспечивает автоматическое списание средств при истечении срока подписки и уведомление пользователей о необходимости продления. Для личных организаций доступен упрощённый набор модулей, оптимизированный для индивидуального использования, без функционала командной работы.
diff --git a/docs/DATATABLE.md b/docs/DATATABLE.md
new file mode 100644
index 0000000..b676b98
--- /dev/null
+++ b/docs/DATATABLE.md
@@ -0,0 +1,756 @@
+# Компонент динамических таблиц DataTable
+
+## Общее описание
+
+Компонент DataTable представляет собой универсальную систему для отображения интерактивных таблиц с поддержкой AJAX-загрузки данных, сортировки по столбцам, поиска и пагинации. Система построена на трёх уровнях: серверная часть (контроллер с методом `prepareTableData`), клиентская часть (JavaScript-модуль DataTable) и уровень представления (компоненты Twig).
+
+Архитектура компонента обеспечивает бесшовную работу как при серверном рендеринге (первичная загрузка страницы), так и при AJAX-обновлениях (фильтрация, сортировка, пагинация). При серверном рендеринге таблица отображается сразу с данными, при этом JavaScript автоматически определяет наличие данных и пропускает избыточный AJAX-запрос. При любых действиях пользователя (сортировка, фильтрация, переход по страницам) данные подгружаются через AJAX, а клиентский модуль обновляет только tbody и tfoot, сохраняя заголовок таблицы неизменным.
+
+---
+
+## Структура компонентов
+
+### Файловая структура
+
+Компонент таблицы состоит из нескольких файлов, организованных по функциональному признаку. JavaScript-модуль расположен в `public/assets/js/modules/DataTable.js` и отвечает за все интерактивные взаимодействия на клиенте. Стили находятся в `public/assets/css/modules/data-table.css` и обеспечивают визуальное оформление элементов управления таблицей. Шаблоны Twig размещены в директории `app/Views/components/table/` и включают основной компонент таблицы, заголовок, пагинацию и макросы для рендеринга действий.
+
+Основные файлы компонента:
+- `table.twig` — универсальный компонент таблицы, включающий заголовок, тело и футер с пагинацией
+- `table_header.twig` — переиспользуемый заголовок с поддержкой сортировки и поиска
+- `pagination.twig` — компонент пагинации с навигацией по страницам
+- `ajax_table.twig` — упрощённый tbody для AJAX-ответов без заголовка
+- `macros.twig` — Twig-макросы для рендеринга кнопок действий
+
+### Интеграция с BaseController
+
+Класс BaseController предоставляет готовую инфраструктуру для работы с таблицами через методы `getTableConfig()`, `prepareTableData()`, `renderTable()` и `table()`. Метод `getTableConfig()` возвращает конфигурацию таблицы, определяющую модель данных, колонки, правила поиска и сортировки, а также действия для каждой строки. Метод `prepareTableData()` выполняет всю логику обработки параметров запроса (пагинация, сортировка, фильтрация), формирует данные и возвращает их в структурированном виде для передачи в шаблон. Метод `renderTable()` принимает конфигурацию и возвращает HTML-код таблицы. Метод `table()` является HTTP-обработчиком для AJAX-запросов, который возвращает только tbody и tfoot без заголовка.
+
+---
+
+## Подключение в контроллере
+
+### Конфигурация таблицы
+
+Каждый контроллер модуля должен определить конфигурацию таблицы через метод `getTableConfig()`. Конфигурация представляет собой ассоциативный массив с обязательными и опциональными параметрами. Обязательными параметрами являются `model` (экземпляр модели для выборки данных) и `columns` (описание колонок таблицы). Опциональные параметры позволяют настроить поведение поиска, сортировки, действий и отображения пустого состояния.
+
+```php
+class Clients extends BaseController
+{
+ protected ClientModel $clientModel;
+
+ public function __construct()
+ {
+ $this->clientModel = new ClientModel();
+ }
+
+ protected function getTableConfig(): array
+ {
+ return [
+ 'id' => 'clients-table',
+ 'url' => '/clients/table',
+ 'model' => $this->clientModel,
+ 'columns' => [
+ 'name' => ['label' => 'Имя / Название', 'width' => '40%'],
+ 'email' => ['label' => 'Email', 'width' => '25%'],
+ 'phone' => ['label' => 'Телефон', 'width' => '20%'],
+ 'created_at' => ['label' => 'Создан', 'width' => '15%'],
+ ],
+ 'searchable' => ['name', 'email', 'phone'],
+ 'sortable' => ['name', 'email', 'phone', 'created_at'],
+ 'defaultSort' => 'name',
+ 'order' => 'asc',
+ 'actions' => ['label' => 'Действия', 'width' => '15%'],
+ 'actionsConfig' => [
+ [
+ 'label' => 'Редактировать',
+ 'url' => '/clients/edit/{id}',
+ 'icon' => 'fa-solid fa-pen',
+ 'class' => 'btn-outline-primary',
+ 'type' => 'edit',
+ ],
+ [
+ 'label' => 'Удалить',
+ 'url' => '/clients/delete/{id}',
+ 'icon' => 'fa-solid fa-trash',
+ 'class' => 'btn-outline-danger',
+ 'type' => 'delete',
+ 'confirm' => 'Вы уверены, что хотите удалить этого клиента?',
+ ],
+ ],
+ 'emptyMessage' => 'Клиентов пока нет',
+ 'emptyIcon' => 'fa-solid fa-users',
+ 'emptyActionUrl' => base_url('/clients/new'),
+ 'emptyActionLabel' => 'Добавить клиента',
+ 'emptyActionIcon' => 'fa-solid fa-plus',
+ 'can_edit' => $this->access->canEdit('clients'),
+ 'can_delete' => $this->access->canDelete('clients'),
+ ];
+ }
+}
+```
+
+### Параметры конфигурации
+
+Параметр `id` задаёт уникальный идентификатор контейнера таблицы и используется для инициализации JavaScript-модуля. Параметр `url` определяет endpoint для AJAX-загрузки данных. Параметр `model` указывает экземпляр модели CodeIgniter, которая используется для запроса данных. Модель автоматически фильтруется по организации через трейт `TenantScopedModel` при его наличии.
+
+Параметр `columns` описывает структуру колонок таблицы. Ключ массива соответствует имени поля в данных, а значение — ассоциативному массиву с параметрами отображения. Параметр `label` задаёт заголовок колонки, параметр `width` — ширину колонки в процентах или пикселях. Опционально можно указать `placeholder` для поля поиска, `searchTitle` для tooltip-подсказки и `align` для CSS-класса выравнивания содержимого.
+
+Параметр `searchable` определяет массив имён колонок, по которым разрешён поиск. Эти колонки получат иконку поиска в заголовке. Параметр `sortable` определяет массив имён колонок, по которым разрешена сортировка. При клике по заголовку сортируемой колонки таблица пересортируется по этому полю. Параметры `defaultSort` и `order` задают поле и направление сортировки по умолчанию.
+
+### Методы контроллера для таблицы
+
+Основной метод для отображения страницы с таблицей выглядит следующим образом:
+
+```php
+public function index()
+{
+ // Проверка прав доступа
+ if (!$this->access->canView('clients')) {
+ return $this->forbiddenResponse('Нет прав для просмотра клиентов');
+ }
+
+ $config = $this->getTableConfig();
+
+ return $this->renderTwig('@Clients/index', [
+ 'title' => 'Клиенты',
+ 'tableHtml' => $this->renderTable($config),
+ 'can_create' => $this->access->canCreate('clients'),
+ 'can_edit' => $this->access->canEdit('clients'),
+ 'can_delete' => $this->access->canDelete('clients'),
+ ]);
+}
+```
+
+Метод для AJAX-загрузки данных таблицы использует встроенную логику BaseController:
+
+```php
+public function table(?array $config = null, ?string $pageUrl = null)
+{
+ // Проверка прав доступа
+ if (!$this->access->canView('clients')) {
+ return $this->forbiddenResponse('Нет прав для просмотра клиентов');
+ }
+
+ return parent::table($config, '/clients');
+}
+```
+
+Метод `table()` автоматически определяет тип запроса (обычный или AJAX) и возвращает либо полную таблицу, либо только tbody и tfoot соответственно. Для определения типа запроса используется заголовок `X-Requested-With` или параметр `format=partial`.
+
+---
+
+## Подключение в шаблоне
+
+### Базовое подключение
+
+Для подключения таблицы в шаблоне Twig используется компонент `table.twig`. Компонент принимает данные из метода `renderTable()` контроллера, который возвращает полностью сформированный HTML:
+
+```twig
+{# app/Modules/Clients/Views/index.twig #}
+
+{% extends 'layouts/base.twig' %}
+
+{% block title %}Клиенты{% endblock %}
+
+{% block content %}
+
+
+
+
+ {{ tableHtml|raw }}
+
+
+{% endblock %}
+
+{% block scripts %}
+{{ parent() }}
+
+
+{% endblock %}
+```
+
+### Прямое использование компонента
+
+При необходимости таблицу можно подключить напрямую через `include`, передав все параметры вручную:
+
+```twig
+{% from '@components/table/macros.twig' import render_actions %}
+
+
+ {{ include('@components/table/table.twig', {
+ id: 'my-table',
+ url: '/my-module/table',
+ perPage: 25,
+ sort: sort|default(''),
+ order: order|default('asc'),
+ filters: filters|default({}),
+ items: items,
+ pagerDetails: pagerDetails,
+ columns: {
+ name: { label: 'Название', width: '40%' },
+ email: { label: 'Email', width: '30%' },
+ status: { label: 'Статус', width: '20%' },
+ },
+ actions: { label: 'Действия', width: '10%' },
+ actionsConfig: [
+ { label: 'Ред.', url: '/edit/{id}', icon: 'fa-solid fa-pen', class: 'btn-outline-primary' },
+ ],
+ can_edit: can_edit|default(true),
+ can_delete: can_delete|default(true),
+ emptyMessage: 'Записей не найдено',
+ emptyIcon: 'fa-solid fa-inbox',
+ emptyActionUrl: url('/create'),
+ emptyActionLabel: 'Создать',
+ emptyActionIcon: 'fa-solid fa-plus',
+ onRowClick: 'openClientDetails',
+ tableClass: 'table-sm',
+ }) }}
+
+```
+
+### Поддержка render_cell и render_actions
+
+В шаблонах Twig доступны глобальные функции для рендеринга ячеек и действий. Функция `render_cell()` автоматически обрабатывает значение ячейки в зависимости от типа данных:
+
+```twig
+{# Ячейка с автоматическим форматированием #}
+{{ render_cell(item, 'name')|raw }}
+{{ render_cell(item, 'email')|raw }}
+{{ render_cell(item, 'price')|raw }}
+{{ render_cell(item, 'created_at')|raw }}
+
+{# Ячейка с кастомным классом #}
+{{ render_cell(item, 'status', { class: 'badge bg-success' })|raw }}
+```
+
+Функция `render_actions()` рендерит кнопки действий для строки таблицы:
+
+```twig
+{% set actions = [
+ { label: 'Ред.', url: '/edit/' ~ item.id, icon: 'fa-solid fa-pen', class: 'btn-outline-primary' },
+ { label: 'Удалить', url: '/delete/' ~ item.id, icon: 'fa-solid fa-trash', class: 'btn-outline-danger' },
+] %}
+{{ render_actions(actions)|raw }}
+```
+
+---
+
+## Конфигурация колонок
+
+### Параметры колонки
+
+Каждая колонка описывается массивом с возможными параметрами. Обязательным параметром является только `label`, остальные опциональны:
+
+```php
+'columns' => [
+ 'name' => [
+ 'label' => 'Название',
+ 'width' => '40%',
+ 'placeholder' => 'Поиск по названию',
+ 'searchTitle' => 'Нажмите для поиска',
+ 'align' => 'text-start',
+ ],
+ 'price' => [
+ 'label' => 'Цена',
+ 'width' => '15%',
+ 'align' => 'text-end',
+ ],
+ 'status' => [
+ 'label' => 'Статус',
+ 'width' => '15%',
+ ],
+]
+```
+
+Параметр `width` задаёт ширину колонки и может быть указан в процентах или пикселях. Рекомендуется использовать проценты для адаптивности или комбинировать фиксированные и относительные значения. Сумма ширин всех колонок обычно должна составлять 100% с учётом колонки действий.
+
+### Поля searchable и sortable
+
+Массив `searchable` определяет поля, по которым разрешён поиск. При указании поля в этом массиве в заголовке колонки появится иконка поиска, при клике на которую отобразится поле ввода:
+
+```php
+'searchable' => ['name', 'email', 'phone', 'company'],
+```
+
+Массив `sortable` определяет поля, по которым разрешена сортировка. При клике по заголовку сортируемой колонки таблица пересортируется по этому полю, при повторном клике направление сортировки меняется на противоположное:
+
+```php
+'sortable' => ['name', 'email', 'phone', 'created_at', 'price'],
+```
+
+Важно: имена полей в `searchable` и `sortable` должны соответствовать ключам массива `columns` и именам полей в базе данных.
+
+---
+
+## Конфигурация действий
+
+### Структура actionsConfig
+
+Параметр `actionsConfig` определяет кнопки действий для каждой строки таблицы. Каждое действие описывается массивом с параметрами:
+
+```php
+'actionsConfig' => [
+ [
+ 'label' => '',
+ 'url' => '/clients/edit/{id}',
+ 'icon' => 'fa-solid fa-pen',
+ 'class' => 'btn-outline-primary',
+ 'title' => 'Редактировать',
+ 'type' => 'edit',
+ 'confirm' => null,
+ ],
+ [
+ 'label' => '',
+ 'url' => '/clients/delete/{id}',
+ 'icon' => 'fa-solid fa-trash',
+ 'class' => 'btn-outline-danger',
+ 'title' => 'Удалить',
+ 'type' => 'delete',
+ 'confirm' => 'Вы уверены?',
+ ],
+],
+```
+
+Параметр `url` поддерживает подстановку `{id}` для автоматической замены на идентификатор записи. Параметр `type` используется для фильтрации действий по правам доступа: действия с `type === 'edit'` показываются только при `can_edit === true`, действия с `type === 'delete'` — только при `can_delete === true`. Параметр `confirm` добавляет подтверждение действия через стандартный `confirm()` в JavaScript.
+
+### Кастомные действия
+
+Помимо типовых действий редактирования и удаления, можно определять кастомные действия:
+
+```php
+'actionsConfig' => [
+ [
+ 'label' => 'Просмотр',
+ 'url' => '/clients/view/{id}',
+ 'icon' => 'fa-solid fa-eye',
+ 'class' => 'btn-outline-secondary',
+ 'title' => 'Просмотр клиента',
+ ],
+ [
+ 'label' => 'Создать сделку',
+ 'url' => '/deals/create?client_id={id}',
+ 'icon' => 'fa-solid fa-file-contract',
+ 'class' => 'btn-outline-success',
+ 'title' => 'Создать сделку',
+ ],
+ [
+ 'label' => 'Записать',
+ 'url' => '/bookings/new?client_id={id}',
+ 'icon' => 'fa-solid fa-calendar',
+ 'class' => 'btn-outline-primary',
+ 'title' => 'Запись на приём',
+ ],
+],
+```
+
+---
+
+## Клиентская инициализация
+
+### Базовая инициализация
+
+JavaScript-модуль DataTable инициализируется для каждой таблицы на странице. При инициализации передаются параметры конфигурации:
+
+```javascript
+document.addEventListener('DOMContentLoaded', function() {
+ new DataTable('clients-table', {
+ url: '/clients/table',
+ perPage: 10,
+ debounceTime: 300,
+ preserveSearchOnSort: true
+ });
+});
+```
+
+Параметр `url` задаёт endpoint для AJAX-загрузки данных. Параметр `perPage` определяет количество записей на странице по умолчанию. Параметр `debounceTime` задаёт задержку в миллисекундах перед выполнением поиска (защита от частых запросов при вводе). Параметр `preserveSearchOnSort` определяет, сохранять ли видимость полей поиска при сортировке.
+
+### Методы DataTable
+
+После инициализации экземпляр DataTable предоставляет методы для программного управления таблицей:
+
+```javascript
+const table = new DataTable('my-table', options);
+
+// Установка фильтра
+table.setFilter('name', 'Поисковый запрос');
+
+// Установка количества записей на странице
+table.setPerPage(25);
+
+// Переход на конкретную страницу
+table.goToPage(3);
+
+// Перезагрузка данных
+table.loadData();
+```
+
+---
+
+## Пустое состояние и действия
+
+### Конфигурация пустого состояния
+
+При отсутствии данных в таблице отображается пустое состояние с возможностью действия. Параметры конфигурации:
+
+```php
+'emptyMessage' => 'Клиентов пока нет',
+'emptyIcon' => 'fa-solid fa-users',
+'emptyActionUrl' => base_url('/clients/new'),
+'emptyActionLabel' => 'Добавить клиента',
+'emptyActionIcon' => 'fa-solid fa-plus',
+```
+
+Параметр `emptyMessage` задаёт текст сообщения. Параметр `emptyIcon` указывает FontAwesome-иконку для отображения над сообщением. Параметры `emptyActionUrl`, `emptyActionLabel` и `emptyActionIcon` определяют кнопку действия при пустом состоянии.
+
+### Условное скрытие действия
+
+Кнопка действия при пустом состоянии отображается только если у пользователя есть право на создание:
+
+```php
+'emptyActionUrl' => $this->access->canCreate('clients')
+ ? base_url('/clients/new')
+ : null,
+'emptyActionLabel' => $this->access->canCreate('clients')
+ ? 'Добавить клиента'
+ : null,
+```
+
+---
+
+## Обработка special fieldMap
+
+### Проблема несоответствия имён полей
+
+При работе с моделями часто возникает ситуация, когда имя поля в базе данных отличается от имени свойства в Twig-шаблоне или имени параметра для фильтрации. Например, поле `client_name` в базе данных должно отображаться как «Клиент» и фильтроваться по параметру `client`. Для решения этой проблемы используется параметр `fieldMap`:
+
+```php
+protected function getTableConfig(): array
+{
+ return [
+ 'id' => 'deals-table',
+ 'url' => '/deals/table',
+ 'model' => $this->dealModel,
+ 'columns' => [
+ 'client_name' => ['label' => 'Клиент', 'width' => '30%'],
+ 'title' => ['label' => 'Сделка', 'width' => '25%'],
+ 'amount' => ['label' => 'Сумма', 'width' => '15%'],
+ 'stage' => ['label' => 'Этап', 'width' => '15%'],
+ 'created_at' => ['label' => 'Создан', 'width' => '15%'],
+ ],
+ 'searchable' => ['client_name', 'title', 'stage'],
+ 'sortable' => ['client_name', 'title', 'amount', 'stage', 'created_at'],
+ 'defaultSort' => 'created_at',
+ 'order' => 'desc',
+ // fieldMap для маппинга параметров фильтрации на реальные поля
+ 'fieldMap' => [
+ 'client' => 'client_name', // filters[client] -> client_name
+ 'stage' => 'stage',
+ ],
+ // ... остальные параметры
+ ];
+}
+```
+
+При использовании `fieldMap` параметры фильтрации из URL (`filters[client]`) автоматически маппятся на реальное поле базы данных (`client_name`). Это позволяет использовать понятные имена параметров в URL при сохранении корректных имён полей в запросе к базе данных.
+
+---
+
+## Кастомные scope для запросов
+
+### Использование callable scope
+
+Когда стандартной фильтрации недостаточно (например, нужны JOIN-ы с другими таблицами или сложные условия), можно использовать параметр `scope`. Это callable-функция, которая получает builder и полностью контролирует формирование запроса:
+
+```php
+protected function getTableConfig(): array
+{
+ return [
+ 'id' => 'deals-table',
+ 'url' => '/deals/table',
+ 'model' => $this->dealModel,
+ // ... columns, searchable, sortable и т.д.
+
+ // Кастомный scope для сложных запросов
+ 'scope' => function($builder) {
+ $builder->resetQuery();
+
+ $builder->select('d.*, c.name as client_name, c.email as client_email')
+ ->from('deals d')
+ ->join('clients c', 'c.id = d.client_id', 'left')
+ ->where('d.organization_id', session()->get('active_org_id'));
+
+ // Дополнительная фильтрация по статусу
+ $status = $this->request->getGet('filters[status]');
+ if ($status && $status !== 'all') {
+ $builder->where('d.status', $status);
+ }
+
+ // Фильтрация по диапазону дат
+ $dateFrom = $this->request->getGet('filters[date_from]');
+ $dateTo = $this->request->getGet('filters[date_to]');
+ if ($dateFrom) {
+ $builder->where('d.created_at >=', $dateFrom);
+ }
+ if ($dateTo) {
+ $builder->where('d.created_at <=', $dateTo . ' 23:59:59');
+ }
+ },
+
+ // fieldMap для JOIN-полей
+ 'fieldMap' => [
+ 'client' => 'c.name',
+ 'client_email' => 'c.email',
+ ],
+ ];
+}
+```
+
+При использовании `scope` параметр `model` игнорируется для построения запроса, и `scope` полностью контролирует SELECT, FROM, JOIN и WHERE. Параметры сортировки и фильтрации всё ещё применяются к builder после выполнения scope, поэтому в `fieldMap` нужно указывать полные имена полей с алиасами таблиц.
+
+---
+
+## Практические примеры
+
+### Пример 1: Таблица клиентов
+
+```php
+class Clients extends BaseController
+{
+ protected ClientModel $clientModel;
+
+ public function __construct()
+ {
+ $this->clientModel = new ClientModel();
+ }
+
+ public function index()
+ {
+ if (!$this->access->canView('clients')) {
+ return $this->forbiddenResponse('Нет прав для просмотра');
+ }
+
+ return $this->renderTwig('@Clients/index', [
+ 'title' => 'Клиенты',
+ 'tableHtml' => $this->renderTable($this->getTableConfig()),
+ 'can_create' => $this->access->canCreate('clients'),
+ ]);
+ }
+
+ public function table()
+ {
+ if (!$this->access->canView('clients')) {
+ return $this->forbiddenResponse('Нет прав');
+ }
+ return parent::table($this->getTableConfig(), '/clients');
+ }
+
+ protected function getTableConfig(): array
+ {
+ return [
+ 'id' => 'clients-table',
+ 'url' => '/clients/table',
+ 'model' => $this->clientModel,
+ 'columns' => [
+ 'name' => ['label' => 'Имя / Название', 'width' => '35%'],
+ 'email' => ['label' => 'Email', 'width' => '25%'],
+ 'phone' => ['label' => 'Телефон', 'width' => '20%'],
+ 'source' => ['label' => 'Источник', 'width' => '10%'],
+ 'created_at' => ['label' => 'Создан', 'width' => '10%'],
+ ],
+ 'searchable' => ['name', 'email', 'phone'],
+ 'sortable' => ['name', 'email', 'phone', 'source', 'created_at'],
+ 'defaultSort' => 'created_at',
+ 'order' => 'desc',
+ 'actions' => ['label' => '', 'width' => '5%'],
+ 'actionsConfig' => [
+ [
+ 'label' => '',
+ 'url' => '/clients/edit/{id}',
+ 'icon' => 'fa-solid fa-pen',
+ 'class' => 'btn-outline-primary btn-sm',
+ 'title' => 'Редактировать',
+ 'type' => 'edit',
+ ],
+ [
+ 'label' => '',
+ 'url' => '/clients/delete/{id}',
+ 'icon' => 'fa-solid fa-trash',
+ 'class' => 'btn-outline-danger btn-sm',
+ 'title' => 'Удалить',
+ 'type' => 'delete',
+ 'confirm' => 'Удалить клиента?',
+ ],
+ ],
+ 'emptyMessage' => 'Клиентов пока нет',
+ 'emptyIcon' => 'fa-solid fa-users',
+ 'emptyActionUrl' => $this->access->canCreate('clients') ? '/clients/new' : null,
+ 'emptyActionLabel' => $this->access->canCreate('clients') ? 'Добавить клиента' : null,
+ 'emptyActionIcon' => 'fa-solid fa-plus',
+ 'can_edit' => $this->access->canEdit('clients'),
+ 'can_delete' => $this->access->canDelete('clients'),
+ ];
+ }
+}
+```
+
+### Пример 2: Таблица с кастомным рендерингом ячеек
+
+```php
+protected function getTableConfig(): array
+{
+ return [
+ 'id' => 'deals-table',
+ 'url' => '/deals/table',
+ 'model' => $this->dealModel,
+ 'columns' => [
+ 'client_name' => ['label' => 'Клиент', 'width' => '25%'],
+ 'title' => ['label' => 'Сделка', 'width' => '25%'],
+ 'amount' => ['label' => 'Сумма', 'width' => '15%'],
+ 'stage' => ['label' => 'Этап', 'width' => '15%'],
+ 'status' => ['label' => 'Статус', 'width' => '10%'],
+ 'created_at' => ['label' => 'Создан', 'width' => '10%'],
+ ],
+ 'searchable' => ['client_name', 'title', 'stage'],
+ 'sortable' => ['client_name', 'title', 'amount', 'stage', 'created_at'],
+ 'defaultSort' => 'created_at',
+ 'order' => 'desc',
+ 'actions' => ['label' => '', 'width' => '5%'],
+ 'actionsConfig' => [
+ [
+ 'label' => '',
+ 'url' => '/deals/edit/{id}',
+ 'icon' => 'fa-solid fa-pen',
+ 'class' => 'btn-outline-primary btn-sm',
+ 'type' => 'edit',
+ ],
+ ],
+ 'can_edit' => $this->access->canEdit('deals'),
+ ];
+}
+```
+
+В шаблоне Twig можно добавить кастомный рендеринг ячеек через Twig-фильтры:
+
+```twig
+{# В шаблоне ячейки с форматированием #}
+
+ {{ item.title }}
+ {% if item.description %}
+ {{ item.description|slice(0, 50) }}...
+ {% endif %}
+
+
+ {{ item.amount|number_format(0, ',', ' ') }} ₽
+
+
+ {% if item.status == 'active' %}
+ Активна
+ {% elseif item.status == 'won' %}
+ Выиграна
+ {% elseif item.status == 'lost' %}
+ Проиграна
+ {% endif %}
+
+```
+
+---
+
+## Проверка при создании модуля
+
+### Чек-лист при добавлении новой таблицы
+
+При создании нового модуля с таблицей необходимо выполнить следующие действия:
+
+**В контроллере:**
+- Определить метод `getTableConfig()` с обязательными параметрами (`id`, `url`, `model`, `columns`)
+- Указать `searchable` и `sortable` массивы с корректными именами полей
+- Настроить `actionsConfig` с кнопками действий и проверкой прав
+- Добавить метод `table()` для AJAX-загрузки данных
+- Вызвать `parent::table()` для использования встроенной логики
+- Проверить права доступа перед вызовом родительского метода
+
+**В шаблоне:**
+- Подключить DataTable.js в блоке `scripts`
+- Инициализировать DataTable с корректным `id` и `url`
+- Передать `tableHtml` из контроллера в шаблон
+- Убедиться, что CSS стили таблицы подключены
+
+**Модель:**
+- Использовать трейт `TenantScopedModel` для автоматической фильтрации по организации
+- Убедиться, что модель имеет поле `organization_id`
+- Проверить, что модель возвращает данные в ожидаемом формате
+
+---
+
+## Типичные ошибки и их устранение
+
+### Таблица не загружается
+
+Если данные не загружаются, проверьте следующее:
+- URL в конфигурации и при инициализации DataTable должны совпадать
+- Метод `table()` контроллера должен вызывать `parent::table()`
+- Модель должна использовать трейт `TenantScopedModel` или обрабатывать фильтрацию вручную
+- Проверьте консоль браузера на наличие ошибок JavaScript
+- Убедитесь, что CSRF-токен передаётся корректно
+
+### Сортировка не работает
+
+Если сортировка не работает:
+- Поле должно быть указано в массиве `sortable`
+- Имя поля в `sortable` должно соответствовать ключу в `columns` и имени поля в базе данных
+- Для JOIN-запросов используйте алиасы таблиц в `sortable` (`c.name` вместо `client_name`)
+
+### Поиск не работает
+
+Если поиск не работает:
+- Поле должно быть указано в массиве `searchable`
+- При использовании JOIN проверьте `fieldMap` для маппинга параметров
+- Убедитесь, что в контроллере используется метод `like()` для фильтрации
+
+### Действия не отображаются
+
+Если кнопки действий не отображаются:
+- Проверьте `can_edit` и `can_delete` в конфигурации
+- Убедитесь, что `type` действия соответствует проверяемому праву (`'edit'` или `'delete'`)
+- Проверьте параметр `actions` в конфигурации (должен быть `{label: 'Действия'}` или `true`)
+
+---
+
+## Сводка параметров конфигурации
+
+| Параметр | Тип | Обязательный | Описание |
+|----------|-----|--------------|----------|
+| `id` | string | Да | Идентификатор контейнера таблицы |
+| `url` | string | Да | URL для AJAX-загрузки |
+| `model` | Model | Да | Экземпляр модели CodeIgniter |
+| `columns` | array | Да | Конфигурация колонок |
+| `searchable` | array | Нет | Поля для поиска |
+| `sortable` | array | Нет | Поля для сортировки |
+| `defaultSort` | string | Нет | Поле сортировки по умолчанию |
+| `order` | string | Нет | Направление сортировки по умолчанию |
+| `actions` | array\|bool | Нет | Конфигурация колонки действий |
+| `actionsConfig` | array | Нет | Кнопки действий |
+| `emptyMessage` | string | Нет | Сообщение при отсутствии данных |
+| `emptyIcon` | string | Нет | Иконка при пустом состоянии |
+| `emptyActionUrl` | string | Нет | URL действия при пустом состоянии |
+| `emptyActionLabel` | string | Нет | Текст кнопки действия |
+| `can_edit` | bool | Нет | Разрешено ли редактирование |
+| `can_delete` | bool | Нет | Разрешено ли удаление |
+| `fieldMap` | array | Нет | Маппинг параметров фильтрации |
+| `scope` | callable | Нет | Кастомный запрос к базе данных |
diff --git a/docs/EVENTS.md b/docs/EVENTS.md
new file mode 100644
index 0000000..f69ba69
--- /dev/null
+++ b/docs/EVENTS.md
@@ -0,0 +1,611 @@
+# Справка по системе событий EventManager
+
+## Общее описание
+
+EventManager — это сервис для работы с событиями в системе «Бизнес.Точка». Он является обёрткой над встроенной системой событий CodeIgniter 4 и предоставляет два типа подписок на события:
+
+- **moduleOn()** — обработчик выполняется только при наличии активной подписки на модуль
+- **systemOn()** — обработчик выполняется всегда, без проверки статуса подписки
+
+EventManager используется для создания интеграций между модулями, когда действия в одном модуле должны автоматически вызывать события в другом. Например, при создании клиента в CRM может автоматически создаваться задача в модуле Tasks.
+
+---
+
+## Подключение EventManager
+
+EventManager подключается как сервис через `service('eventManager')`:
+
+```php
+$eventManager = service('eventManager');
+```
+
+Доступно в контроллерах через BaseController:
+
+```php
+// В контроллере:
+$em = service('eventManager');
+```
+
+---
+
+## Основные методы
+
+### forModule() — привязка к модулю
+
+Метод `forModule()` привязывает все последующие вызовы `moduleOn()` к указанному модулю. Это означает, что подписки на события будут создаваться только если организация имеет активную подписку на этот модуль.
+
+```php
+$em = service('eventManager');
+
+// Привязываем события к модулю CRM
+$em->forModule('crm');
+```
+
+После вызова `forModule()` все события, добавленные через `moduleOn()`, будут проверять подписку организации на модуль CRM. Если подписка не активна — обработчик не будет выполнен.
+
+```php
+// Цепочка вызовов
+service('eventManager')
+ ->forModule('crm')
+ ->moduleOn('client.created', function($client) {
+ // Этот код выполнится только если подписка на CRM активна
+ });
+```
+
+**Важно:** Метод `forModule()` необходимо вызвать перед `moduleOn()`, иначе будет выброшено исключение.
+
+---
+
+### moduleOn() — подписка с проверкой модуля
+
+Метод `moduleOn()` создаёт подписку на событие, которая выполняется только при соблюдении условий:
+
+1. Модуль существует в конфигурации `BusinessModules`
+2. Модуль глобально включён в настройках
+3. Организация имеет активную подписку на модуль
+
+```php
+$em = service('eventManager');
+$em->forModule('crm');
+
+$em->moduleOn('client.created', function($client) {
+ // Обработчик выполнится только если CRM подписка активна
+ log_message('debug', 'Клиент создан: ' . $client['name']);
+});
+```
+
+**Сигнатура метода:**
+
+```php
+public function moduleOn(
+ string $event, // Имя события
+ callable $callback, // Обработчик события
+ int $priority = 100 // Приоритет выполнения
+): bool
+```
+
+**Возвращаемое значение:**
+
+- `true` — подписка создана, обработчик будет выполнен
+- `false` — подписка не создана (модуль не активен, отключён или не существует)
+
+**Параметр `$callback`:**
+
+Обработчик события получает параметры, переданные при вызове события:
+
+```php
+$em->forModule('crm');
+$em->moduleOn('client.created', function($client, $userId) {
+ echo 'Создан клиент ' . $client['name'] . ' пользователем ' . $userId;
+});
+
+// Где-то в коде:
+Events::trigger('client.created', $clientData, $currentUserId);
+```
+
+**Приоритет выполнения:**
+
+Параметр `$priority` определяет порядок выполнения обработчиков. Меньшее значение — более высокий приоритет:
+
+```php
+// Выполнится раньше (приоритет 50)
+$em->moduleOn('client.created', function($client) {
+ // Логирование
+}, 50);
+
+// Выполнится позже (приоритет 100, значение по умолчанию)
+$em->moduleOn('client.created', function($client) {
+ // Отправка уведомлений
+});
+```
+
+---
+
+### systemOn() — подписка без проверки модуля
+
+Метод `systemOn()` создаёт подписку на событие без проверки статуса подписки. Обработчик будет выполнен всегда, независимо от того, какие модули активированы у организации.
+
+Используется для системных событий, которые должны работать для всех организаций:
+
+```php
+$em = service('eventManager');
+
+// Этот обработчик выполнится для всех организаций
+$em->systemOn('user.login', function($user) {
+ log_message('info', 'Пользователь вошёл: ' . $user['email']);
+});
+
+// Для отправки email-уведомлений при любом действии
+$em->systemOn('email.send', function($to, $subject, $body) {
+ // Логирование отправки
+});
+```
+
+**Сигнатура метода:**
+
+```php
+public function systemOn(
+ string $event,
+ callable $callback,
+ int $priority = 100
+): void
+```
+
+---
+
+### off() — отписка от события
+
+Метод `off()` удаляет подписку на событие:
+
+```php
+$em = service('eventManager');
+
+// Удаление всех обработчиков события
+$em->off('client.created');
+
+// Удаление конкретного обработчика
+$em->off('client.created', $specificCallback);
+```
+
+---
+
+### currentModuleActive() — проверка статуса модуля
+
+Метод `currentModuleActive()` возвращает `true` если текущий модуль (установленный через `forModule()`) активен для организации.
+
+Используется внутри обработчиков для проверки:
+
+```php
+$em->service('eventManager');
+$em->forModule('tasks');
+
+$em->moduleOn('deal.won', function($deal) {
+ // Проверяем, активен ли модуль Tasks
+ if ($em->currentModuleActive()) {
+ // Создаём задачу
+ createTaskForDeal($deal);
+ }
+});
+```
+
+---
+
+### getCurrentModuleCode() — получение кода модуля
+
+Метод `getCurrentModuleCode()` возвращает код модуля, установленного через `forModule()`:
+
+```php
+$em = service('eventManager');
+$em->forModule('crm');
+
+$code = $em->getCurrentModuleCode(); // Вернёт 'crm'
+```
+
+---
+
+## Встроенные события системы
+
+### События пользователя
+
+```php
+// После успешной регистрации пользователя
+Events::trigger('user.registered', $user);
+
+// После подтверждения email
+Events::trigger('user.verified', $user);
+
+// При каждом входе в систему
+Events::trigger('user.login', $user);
+
+// При выходе из системы
+Events::trigger('user.logout', $user);
+
+// При смене пароля
+Events::trigger('user.passwordChanged', $user);
+
+// При изменении профиля
+Events::trigger('user.profileUpdated', $user, $oldData);
+```
+
+### События организации
+
+```php
+// При создании организации
+Events::trigger('organization.created', $organization);
+
+// При изменении данных организации
+Events::trigger('organization.updated', $organization, $changes);
+
+// При удалении организации (до удаления)
+Events::trigger('organization.deleting', $organization);
+
+// После удаления организации
+Events::trigger('organization.deleted', $organizationId);
+
+// При присоединении пользователя к организации
+Events::trigger('organization.userJoined', $organization, $user, $role);
+
+// При выходе пользователя из организации
+Events::trigger('organization.userLeft', $organization, $user);
+
+// При изменении роли пользователя
+Events::trigger('organization.userRoleChanged', $organization, $user, $oldRole, $newRole);
+```
+
+### События модуля Клиенты
+
+```php
+// При создании клиента
+Events::trigger('client.created', $client, $userId);
+
+// При обновлении клиента
+Events::trigger('client.updated', $client, $changes, $userId);
+
+// При удалении клиента
+Events::trigger('client.deleted', $clientId, $userId);
+
+// При импорте клиентов
+Events::trigger('client.imported', $clients, $userId);
+```
+
+---
+
+## Примеры интеграции модулей
+
+### Пример 1: Создание задачи при создании клиента
+
+```php
+// В модуле Tasks — файл bootstrap или Config/Events.php
+
+service('eventManager')
+ ->forModule('tasks')
+ ->moduleOn('client.created', function($client, $userId) {
+ // Создаём задачу на первичный контакт
+ $taskModel = new \App\Modules\Tasks\Models\TaskModel();
+
+ $taskModel->insert([
+ 'organization_id' => $client['organization_id'],
+ 'title' => 'Первый контакт с клиентом: ' . $client['name'],
+ 'description' => 'Необходимо связаться с клиентом для уточнения потребностей',
+ 'assigned_to' => $userId,
+ 'status' => 'todo',
+ 'priority' => 'medium',
+ 'due_at' => date('Y-m-d H:i:s', strtotime('+1 day')),
+ 'created_by' => $userId,
+ ]);
+ });
+```
+
+### Пример 2: Автоматический переход сделки при завершении задачи
+
+```php
+// В модуле CRM
+service('eventManager')
+ ->forModule('crm')
+ ->moduleOn('task.completed', function($task, $userId) {
+ if ($task['related_type'] === 'deal' && $task['related_id']) {
+ $dealModel = new \App\Modules\CRM\Models\DealModel();
+
+ // Получаем сделку
+ $deal = $dealModel->find($task['related_id']);
+
+ if ($deal && $deal['stage'] === 'proposal') {
+ // Переводим сделку на следующий этап
+ $dealModel->update($deal['id'], [
+ 'stage' => 'negotiation',
+ 'updated_at' => date('Y-m-d H:i:s'),
+ ]);
+
+ // Логируем переход
+ log_message('info', 'Сделка #' . $deal['id'] . ' переведена на этап переговоров после завершения задачи');
+ }
+ }
+ });
+```
+
+### Пример 3: Уведомление при записи на приём
+
+```php
+// В модуле Booking
+service('eventManager')
+ ->forModule('booking')
+ ->moduleOn('booking.created', function($booking, $client, $userId) {
+ // Отправляем уведомление клиенту
+ $emailService = service('email');
+
+ $emailService->send(
+ $client['email'],
+ 'Подтверждение записи',
+ 'Уважаемый ' . $client['name'] . ',
+ Ваша запись на ' . $booking['service_name'] . ' подтверждена на ' .
+ date('d.m.Y в H:i', strtotime($booking['starts_at']))
+ );
+
+ // Создаём задачу для менеджера
+ $taskModel = new \App\Modules\Tasks\Models\TaskModel();
+ $taskModel->insert([
+ 'organization_id' => $booking['organization_id'],
+ 'title' => 'Подготовка к записи: ' . $client['name'],
+ 'description' => 'Клиент записан на услугу ' . $booking['service_name'],
+ 'assigned_to' => $booking['staff_id'],
+ 'status' => 'todo',
+ 'priority' => 'normal',
+ 'due_at' => $booking['starts_at'],
+ ]);
+ });
+```
+
+### Пример 4: Создание проекта Proof при выигрыше сделки
+
+```php
+// В модуле CRM
+service('eventManager')
+ ->forModule('crm')
+ ->moduleOn('deal.won', function($deal, $userId) {
+ // Проверяем, активен ли модуль Proof
+ if (service('moduleSubscription')->isModuleActive('proof')) {
+ $projectModel = new \App\Modules\Proof\Models\ProjectModel();
+
+ $projectModel->insert([
+ 'organization_id' => $deal['organization_id'],
+ 'client_id' => $deal['client_id'],
+ 'title' => 'Проект по сделке #' . $deal['id'],
+ 'description' => 'Автоматически создан при выигрыше сделки',
+ 'status' => 'active',
+ 'created_by' => $userId,
+ ]);
+ }
+ });
+```
+
+---
+
+## Правила именования событий
+
+Для консистентности системы используйте следующие правила именования событий:
+
+### Формат: `сущность.действие`
+
+| Сущность | Действия | Пример события |
+|----------|----------|----------------|
+| user | registered, verified, login, logout, passwordChanged, profileUpdated | `user.login` |
+| organization | created, updated, deleting, deleted, userJoined, userLeft, userRoleChanged | `organization.created` |
+| client | created, updated, deleted, imported | `client.created` |
+| deal | created, updated, deleted, won, lost | `deal.won` |
+| booking | created, updated, cancelled, completed | `booking.created` |
+| task | created, updated, deleted, started, completed | `task.completed` |
+| project | created, updated, deleted, archived | `project.created` |
+| file | uploaded, deleted, approved, rejected | `file.uploaded` |
+| email | send, sent, failed | `email.send` |
+
+### Группы событий модулей
+
+- **CRM:** `client.*`, `deal.*`, `pipeline.*`
+- **Booking:** `booking.*`, `service.*`, `staff.*`
+- **Proof:** `project.*`, `file.*`, `comment.*`
+- **Tasks:** `task.*`, `board.*`, `comment.*`
+
+---
+
+## Вызов событий в коде
+
+Для вызова события используйте `Events::trigger()` из CodeIgniter 4:
+
+```php
+use CodeIgniter\Events\Events;
+
+// Простой вызов
+Events::trigger('client.created', $clientData);
+
+// С несколькими параметрами
+Events::trigger('deal.won', $deal, $userId);
+
+// С именованными параметрами (начиная с CI 4.3+)
+Events::trigger('client.updated', [
+ 'client' => $clientData,
+ 'changes' => $changes,
+ 'userId' => $userId,
+]);
+```
+
+---
+
+## Порядок инициализации событий
+
+События модулей должны инициализироваться в файле `app/Config/Events.php`:
+
+```php
+forModule('crm');
+ $em->moduleOn('client.created', function($client, $userId) {
+ // Логика создания задачи
+ });
+
+ // Интеграция CRM → Proof
+ $em->moduleOn('deal.won', function($deal, $userId) {
+ // Логика создания проекта
+ });
+ }
+}
+
+register_crm_events();
+```
+
+---
+
+## Логирование событий
+
+EventManager автоматически логирует информацию о подписках и выполнении событий:
+
+- **Подписка создана:** `"EventManager: Subscribed to event 'client.created' for module 'crm'"`
+- **Модуль отключён:** `"EventManager: Module 'crm' is disabled globally"`
+- **Подписка не активна:** `"EventManager: Organization subscription not active for module 'crm'"`
+- **Системное событие:** `"EventManager: System event subscribed: 'user.login'"`
+
+Уровень логирования — `debug` для информации и `error` для ошибок конфигурации.
+
+---
+
+## Тестирование событий
+
+### Ручное тестирование в разработке
+
+```php
+// В контроллере для тестирования
+public function testEvent()
+{
+ $testClient = [
+ 'id' => 999,
+ 'name' => 'Тестовый клиент',
+ 'email' => 'test@example.com',
+ 'organization_id' => session()->get('active_org_id'),
+ ];
+
+ // Вызываем событие напрямую
+ Events::trigger('client.created', $testClient, session()->get('user_id'));
+
+ return 'Событие вызвано, проверьте логи';
+}
+```
+
+### Отладка подписок
+
+```php
+// Получение всех обработчиков события
+$handlers = Events::listeners('client.created');
+
+foreach ($handlers as $handler) {
+ log_message('debug', 'Handler: ' . print_r($handler, true));
+}
+```
+
+---
+
+## Типичные ошибки и их устранение
+
+### Ошибка: "Module code not set"
+
+```php
+// Неправильно:
+$em->moduleOn('client.created', $callback);
+
+// Правильно:
+$em->forModule('crm')->moduleOn('client.created', $callback);
+```
+
+### Событие не срабатывает
+
+Возможные причины:
+
+1. Модуль не активирован для организации
+2. Модуль отключён глобально в конфигурации
+3. Ошибка в имени события
+4. Исключение в обработчике блокирует выполнение
+
+Проверка:
+
+```php
+// Проверка статуса модуля
+$em = service('eventManager');
+$em->forModule('crm');
+
+if ($em->currentModuleActive()) {
+ echo 'Модуль активен';
+} else {
+ echo 'Модуль не активен';
+}
+```
+
+### Конфликты приоритетов
+
+При использовании нескольких обработчиков одного события убедитесь в корректном порядке выполнения:
+
+```php
+// Сначала сохраняем данные
+$em->moduleOn('deal.won', function($deal) {
+ saveDealToArchive($deal);
+}, 10); // Выполнится первым
+
+// Затем отправляем уведомления
+$em->moduleOn('deal.won', function($deal) {
+ sendDealWonNotification($deal);
+}, 100); // Выполнится вторым
+```
+
+---
+
+## Сводка методов
+
+| Метод | Описание | Возвращает |
+|-------|----------|------------|
+| `forModule($code)` | Привязать к модулю | `$this` |
+| `moduleOn($event, $callback, $priority)` | Подписка с проверкой | `bool` |
+| `systemOn($event, $callback, $priority)` | Системная подписка | `void` |
+| `off($event, $callback)` | Отписка | `void` |
+| `currentModuleActive()` | Проверка модуля | `bool` |
+| `getCurrentModuleCode()` | Код модуля | `string\|null` |
diff --git a/docs/EventManager.txt b/docs/EventManager.txt
deleted file mode 100644
index 52afa1b..0000000
--- a/docs/EventManager.txt
+++ /dev/null
@@ -1,64 +0,0 @@
-Примеры использования событийной системы
-
-Для создания интеграций между модулями, которые должны работать только при наличии активной подписки, используется метод moduleOn(). Ниже приведен пример инициализации событий в файле модуля, например в app/Modules/Crm/Config/Events.php:
-
-php
-
-forModule('crm');
-
-// При создании нового клиента отправляем приветственное письмо
-$em->moduleOn('clients.created', function($client) {
- $emailService = service('email');
- $emailService->sendWelcomeEmail($client->email, $client->name);
-});
-
-// При изменении статуса сделки обновляем метрики
-$em->moduleOn('deals.status_changed', function($deal, $oldStatus, $newStatus) {
- service('analytics')->trackDealStatusChange($deal->id, $oldStatus, $newStatus);
-});
-
-// Логируем все действия с клиентами
-$em->moduleOn('clients.*', function($event, $data) {
- service('audit')->log('crm_client_activity', $data);
-});
-
-
-Для системных событий, которые должны выполняться всегда независимо от статуса подписки, используется метод systemOn(). Такие события подходят для сквозной функциональности, например, логирования, аудита или сбора аналитики:
-
-php
-
-systemOn('DBQuery', function($query) {
- if (ENVIRONMENT === 'development') {
- log_message('debug', 'DB Query: ' . $query);
- }
-});
-
-// Системное событие для записи активности пользователя
-$em->systemOn('user.login', function($user) {
- service('activityLogger')->logLogin($user->id);
-});
-
-
-Архитектурные преимущества решения
-
-Разделение событий на два типа обеспечивает гибкость при проектировании интеграций между модулями. События типа moduleOn() гарантируют, что бизнес-логика модуля выполняется только для организаций, которые оплатили доступ к этому модулю, что защищает коммерческие интересы и предотвращает несанкционированное использование функциональности. События типа systemOn() позволяют реализовывать сквозную функциональность, которая должна присутствовать в системе независимо от того, какие модули оплачены организацией, например, общие уведомления, аудит безопасности или интеграция с внешними системами мониторинга.
-
-Кэширование результата проверки статуса модуля в рамках одного запроса обеспечивает высокую производительность событийной системы. При множественных подписках на события одного модуля проверка подписки выполняется только один раз, а затем результат кэшируется в свойстве $moduleActive.
\ No newline at end of file