Переглянути джерело

added two new app bg remover and title creator

VISHAL BHANUSHALI 1 місяць тому
батько
коміт
8f1d43bb63
71 змінених файлів з 24074 додано та 5 видалено
  1. 0 0
      bg_remover/__init__.py
  2. 3 0
      bg_remover/admin.py
  3. 6 0
      bg_remover/apps.py
  4. 0 0
      bg_remover/migrations/__init__.py
  5. 3 0
      bg_remover/models.py
  6. 41 0
      bg_remover/services.py
  7. 265 0
      bg_remover/templates/bg_remover_index.html
  8. 376 0
      bg_remover/templates/bg_remover_index_old.html
  9. 194 0
      bg_remover/templates/templ.html
  10. 3 0
      bg_remover/tests.py
  11. 11 0
      bg_remover/urls.py
  12. 106 0
      bg_remover/views.py
  13. 2 0
      content_quality_tool/settings.py
  14. 2 0
      content_quality_tool/urls.py
  15. 44 5
      content_quality_tool_public/templates/sidebar.html
  16. 0 0
      title_creator_app/__init__.py
  17. 3 0
      title_creator_app/admin.py
  18. 6 0
      title_creator_app/apps.py
  19. 10 0
      title_creator_app/decorators.py
  20. 30 0
      title_creator_app/migrations/0001_initial.py
  21. 24 0
      title_creator_app/migrations/0002_processingtask.py
  22. 0 0
      title_creator_app/migrations/__init__.py
  23. 29 0
      title_creator_app/models.py
  24. 15380 0
      title_creator_app/static/css/adminlte.css
  25. 134 0
      title_creator_app/static/css/custom.css
  26. 309 0
      title_creator_app/static/css/login.css
  27. 8 0
      title_creator_app/static/css/overlayscrollbars.min.css
  28. 0 0
      title_creator_app/static/css/select2-bootstrap4.min.css
  29. BIN
      title_creator_app/static/fonts/aptos-font/Aptos.eot
  30. BIN
      title_creator_app/static/fonts/aptos-font/Aptos.woff
  31. BIN
      title_creator_app/static/fonts/aptos-font/Aptos.woff2
  32. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-black-italic.ttf
  33. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-black.ttf
  34. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-bold.ttf
  35. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic 2.ttf
  36. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic.ttf
  37. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-extrabold.ttf
  38. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-italic.ttf
  39. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-light-italic.ttf
  40. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-light.ttf
  41. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-semibold.ttf
  42. BIN
      title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos.ttf
  43. BIN
      title_creator_app/static/fonts/aptos-font/aptos-black-italic.ttf
  44. BIN
      title_creator_app/static/fonts/aptos-font/aptos-black.ttf
  45. BIN
      title_creator_app/static/fonts/aptos-font/aptos-bold.ttf
  46. BIN
      title_creator_app/static/fonts/aptos-font/aptos-extrabold-italic 2.ttf
  47. BIN
      title_creator_app/static/fonts/aptos-font/aptos-extrabold-italic.ttf
  48. BIN
      title_creator_app/static/fonts/aptos-font/aptos-extrabold.ttf
  49. BIN
      title_creator_app/static/fonts/aptos-font/aptos-italic.ttf
  50. BIN
      title_creator_app/static/fonts/aptos-font/aptos-light-italic.ttf
  51. BIN
      title_creator_app/static/fonts/aptos-font/aptos-light.ttf
  52. BIN
      title_creator_app/static/fonts/aptos-font/aptos-semibold.ttf
  53. BIN
      title_creator_app/static/fonts/aptos-font/aptos.ttf
  54. BIN
      title_creator_app/static/images/45.jpg
  55. BIN
      title_creator_app/static/images/bg.png
  56. BIN
      title_creator_app/static/images/left.png
  57. BIN
      title_creator_app/static/images/logo-mini.png
  58. BIN
      title_creator_app/static/images/logo.png
  59. BIN
      title_creator_app/static/images/user2-160x160.jpg
  60. 715 0
      title_creator_app/static/js/adminlte.js
  61. 1214 0
      title_creator_app/static/js/attr-extraction-bkp.js
  62. 3471 0
      title_creator_app/static/js/attr-extraction.js
  63. 42 0
      title_creator_app/templates/footer.html
  64. 155 0
      title_creator_app/templates/header.html
  65. 79 0
      title_creator_app/templates/login.html
  66. 40 0
      title_creator_app/templates/sidebar.html
  67. 412 0
      title_creator_app/templates/title_creator_index.html
  68. 514 0
      title_creator_app/templates/title_creator_master.html
  69. 3 0
      title_creator_app/tests.py
  70. 15 0
      title_creator_app/urls.py
  71. 425 0
      title_creator_app/views.py

+ 0 - 0
bg_remover/__init__.py


+ 3 - 0
bg_remover/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 6 - 0
bg_remover/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class BgRemoverConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'bg_remover'

+ 0 - 0
bg_remover/migrations/__init__.py


+ 3 - 0
bg_remover/models.py

@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

+ 41 - 0
bg_remover/services.py

@@ -0,0 +1,41 @@
+# background_remover/services.py
+import torch
+from PIL import Image
+from torchvision import transforms
+from transformers import AutoModelForImageSegmentation
+
+class BiRefNetService:
+    _instance = None
+
+    def __new__(cls):
+        if cls._instance is None:
+            cls._instance = super(BiRefNetService, cls).__new__(cls)
+            cls._instance._init_model()
+        return cls._instance
+
+    def _init_model(self):
+        self.device = "cuda" if torch.cuda.is_available() else "cpu"
+        self.model = AutoModelForImageSegmentation.from_pretrained(
+            "ZhengPeng7/BiRefNet", trust_remote_code=True
+        )
+        self.model.to(self.device)
+        self.model.eval()
+        self.transform = transforms.Compose([
+            transforms.Resize((1024, 1024)),
+            transforms.ToTensor(),
+            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
+        ])
+
+    def remove_background(self, input_image: Image.Image) -> Image.Image:
+        image_size = input_image.size
+        input_tensor = self.transform(input_image.convert("RGB")).unsqueeze(0).to(self.device)
+
+        with torch.no_grad():
+            preds = self.model(input_tensor)[-1].sigmoid().cpu()
+
+        mask = transforms.ToPILImage()(preds[0].squeeze()).resize(image_size)
+        
+        # Create white background
+        white_bg = Image.new("RGB", image_size, (255, 255, 255))
+        white_bg.paste(input_image, (0, 0), mask)
+        return white_bg

+ 265 - 0
bg_remover/templates/bg_remover_index.html

@@ -0,0 +1,265 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Column Mapping</title>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
+<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
+        integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" crossorigin="anonymous">
+    <!--end::Fonts--><!--begin::Third Party Plugin(OverlayScrollbars)-->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/styles/overlayscrollbars.min.css"
+        integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" crossorigin="anonymous">
+    <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Third Party Plugin(Bootstrap Icons)-->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css"
+        integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" crossorigin="anonymous">
+    <!--end::Third Party Plugin(Bootstrap Icons)--><!--begin::Required Plugin(AdminLTE)-->
+    <link rel="stylesheet" href="{% static './css/adminlte.css' %}"><!--end::Required Plugin(AdminLTE)--><!-- apexcharts -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/apexcharts@3.37.1/dist/apexcharts.css"
+        integrity="sha256-4MX+61mt9NVvvuPjUWdUdyfZfxSB1/Rf9WtqRHgG5S0=" crossorigin="anonymous">
+    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
+    <link rel="stylesheet" href="{% static './css/select2-bootstrap4.min.css' %}">
+    <link rel="stylesheet" href="{% static './css/custom.css' %}">
+    <script src="https://cdn.tailwindcss.com"></script>
+    <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;800&display=swap" rel="stylesheet">
+    <style>
+        .bg-remover-container .nav-pills .nav-link { color: #64748b; font-weight: 600; border-radius: 12px; padding: 10px 25px; transition: all 0.3s; border: none; background: none; }
+        .bg-remover-container .nav-pills .nav-link.active { background-color: #3b82f6 !important; color: white !important; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); }
+        .bg-remover-container .upload-card { transition: all 0.3s ease; border: 2px dashed #cbd5e1; background: #ffffff; }
+        .upload-card:hover { border-color: #3b82f6; background-color: #f8fafc; transform: translateY(-2px); }
+        .preview-box { background: #ffffff; border-radius: 12px; padding: 15px; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); }
+        .checkerboard-bg {
+            background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(-45deg, #eee 25%, transparent 25%), 
+                              linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(-45deg, transparent 75%, #eee 75%);
+            background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
+        }
+    </style>
+</head>
+
+<!-- <body class="layout-fixed sidebar-expand-lg bg-body-tertiary"> -->
+<body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse">
+    <div class="app-wrapper">
+        {% include 'header.html' %}
+        {% include 'sidebar.html' %}
+
+        <main class="app-main">
+            <div class="app-content pt-10 bg-remover-container">
+                <div class="max-w-5xl mx-auto px-4">
+                    <div class="text-center mb-8">
+                        <h1 class="text-4xl font-extrabold text-gray-900 mb-2">AI Background Remover</h1>
+                        <p class="text-gray-500 font-medium">Remove image backgrounds instantly for single images, bulk uploads, or ZIP files.</p>
+                    </div>
+
+                    <ul class="nav nav-pills justify-content-center mb-8 gap-3" id="pills-tab" role="tablist">
+                        <li class="nav-item" role="presentation">
+                            <button class="nav-link active" id="pills-single-tab" data-bs-toggle="pill" data-bs-target="#pills-single" type="button" role="tab">
+                                <i class="bi bi-image mr-2"></i> Single Image
+                            </button>
+                        </li>
+                        <li class="nav-item" role="presentation">
+                            <button class="nav-link" id="pills-bulk-tab" data-bs-toggle="pill" data-bs-target="#pills-bulk" type="button" role="tab">
+                                <i class="bi bi-images mr-2"></i> Bulk & ZIP Process
+                            </button>
+                        </li>
+                    </ul>
+
+                    <div class="tab-content" id="pills-tabContent">
+                        <div class="tab-pane fade show active" id="pills-single" role="tabpanel">
+                            <div id="uploadContainer" class="bg-white p-8 rounded-3xl shadow-sm border border-gray-200 mb-8">
+                                <div id="drop-zone" class="upload-card flex flex-col items-center justify-center rounded-2xl p-12 cursor-pointer" onclick="document.getElementById('file-input').click()">
+                                    <input type="file" id="file-input" class="hidden" accept="image/*">
+                                    <div class="bg-blue-50 p-5 rounded-full mb-4 text-blue-600">
+                                        <i class="bi bi-cloud-arrow-up text-4xl"></i>
+                                    </div>
+                                    <p class="text-lg font-semibold text-gray-700">Click or Drag & Drop Image</p>
+                                    <p class="text-sm text-gray-400 mt-1">Supports PNG, JPG, WEBP</p>
+                                </div>
+                            </div>
+                            <div id="result-container" class="hidden space-y-8 pb-20">
+                                <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
+                                    <div class="space-y-3">
+                                        <span class="text-xs font-bold uppercase text-gray-400 tracking-wider">Original</span>
+                                        <div class="preview-box"><img id="original-preview" class="rounded-lg w-full h-auto object-contain max-h-[400px]"></div>
+                                    </div>
+                                    <div class="space-y-3">
+                                        <div class="flex justify-between items-center px-1">
+                                            <span class="text-xs font-bold uppercase text-gray-400 tracking-wider">Processed</span>
+                                            <span id="processing-badge" class="hidden animate-pulse bg-blue-100 text-blue-700 px-2 py-0.5 rounded text-[10px] font-bold">PROCESSING...</span>
+                                        </div>
+                                        <div class="preview-box relative min-h-[200px] flex items-center justify-center checkerboard-bg rounded-lg">
+                                            <div id="result-loader" class="absolute inset-0 bg-white/60 backdrop-blur-sm flex flex-col items-center justify-center rounded-lg z-10 hidden">
+                                                <div class="w-10 h-10 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
+                                            </div>
+                                            <img id="processed-preview" class="rounded-lg w-full h-auto object-contain max-h-[400px]">
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="flex gap-3 justify-center">
+                                    <button onclick="location.reload()" class="px-6 py-2 bg-gray-100 rounded-xl font-bold hover:bg-gray-200">New Upload</button>
+                                    <a id="download-btn" class="hidden px-8 py-2 bg-green-600 text-white rounded-xl font-bold shadow-lg hover:bg-green-700">Download PNG</a>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="tab-pane fade" id="pills-bulk" role="tabpanel">
+                             <div class="bg-white p-8 rounded-3xl shadow-sm border border-gray-200 mb-8">
+                                <div id="bulk-drop-zone" class="upload-card flex flex-col items-center justify-center rounded-2xl p-12 cursor-pointer transition-all border-indigo-200" onclick="document.getElementById('bulk-file-input').click()">
+                                    <input type="file" id="bulk-file-input" class="hidden" accept="image/*,.zip" multiple>
+                                    <div class="bg-indigo-50 p-5 rounded-full mb-4 text-indigo-600">
+                                        <i class="bi bi-file-earmark-zip text-4xl"></i>
+                                    </div>
+                                    <h3 class="text-xl font-bold text-gray-800">Bulk & ZIP Upload</h3>
+                                    <p class="text-gray-500 mt-1 text-center">Upload multiple images or a <b>single ZIP file</b></p>
+                                </div>
+                                <div id="bulk-list-container" class="hidden mt-8 border-t pt-8">
+                                    <div class="flex justify-between items-center mb-6">
+                                        <div>
+                                            <h3 class="font-extrabold text-gray-900 text-lg">Batch Queue</h3>
+                                            <p class="text-xs text-gray-400 uppercase font-bold tracking-widest"><span id="file-count">0</span> Items Ready</p>
+                                        </div>
+                                        <button id="start-bulk-btn" class="bg-indigo-600 text-white px-8 py-3 rounded-xl font-bold hover:bg-indigo-700 shadow-lg">Start Processing</button>
+                                    </div>
+                                    <div id="file-queue" class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-6 gap-4"></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div id="bulk-loader" class="hidden fixed inset-0 bg-black/60 backdrop-blur-md z-[9999] flex flex-col items-center justify-center text-white">
+                <div class="flex flex-col items-center p-8 bg-gray-900/80 rounded-3xl border border-white/10 shadow-2xl">
+                    <div class="w-16 h-16 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4"></div>
+                    <h2 class="text-2xl font-bold">Processing Batch...</h2>
+                    <p class="text-gray-400 mt-2">AI is removing backgrounds and preparing your ZIP.</p>
+                    <p class="text-xs text-indigo-400 mt-4 uppercase tracking-widest font-bold">Please do not refresh the page</p>
+                </div>
+            </div>
+        </main>
+        {% include 'footer.html' %}
+    </div>
+
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"></script>
+    <script src="{% static './js/adminlte.js' %}"></script>
+
+    <script>
+        
+
+        // --- PRESERVED SINGLE UPLOAD LOGIC ---
+        const fileInput = document.getElementById('file-input');
+        if (fileInput) {
+            fileInput.onchange = (e) => handleSingleUpload(e.target.files[0]);
+        }
+
+        async function handleSingleUpload(file) {
+            if (!file) return;
+            document.getElementById('uploadContainer').classList.add('hidden');
+            document.getElementById('result-container').classList.remove('hidden');
+            document.getElementById('result-loader').classList.remove('hidden');
+            document.getElementById('original-preview').src = URL.createObjectURL(file);
+
+            const formData = new FormData();
+            formData.append('image', file);
+
+            try {
+                const response = await fetch('/bg-remover/remove-bg/', {
+                    method: 'POST',
+                    body: formData,
+                    headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                });
+                if (!response.ok) throw new Error("Processing failed");
+                const blob = await response.blob();
+                const url = URL.createObjectURL(blob);
+                document.getElementById('processed-preview').src = url;
+                const btn = document.getElementById('download-btn');
+                btn.href = url;
+                btn.download = `cleared_${file.name.split('.')[0]}.png`;
+                btn.classList.remove('hidden');
+            } catch (err) { 
+                alert("Error processing image: " + err.message); 
+                location.reload();
+            } finally { 
+                document.getElementById('result-loader').classList.add('hidden'); 
+            }
+        }
+
+        // --- PRESERVED BULK LOGIC ---
+        const bulkInput = document.getElementById('bulk-file-input');
+        const fileQueue = document.getElementById('file-queue');
+        const bulkList = document.getElementById('bulk-list-container');
+        const startBulkBtn = document.getElementById('start-bulk-btn');
+        const bulkLoader = document.getElementById('bulk-loader');
+
+        if (bulkInput) {
+            bulkInput.onchange = (e) => {
+                const files = Array.from(e.target.files);
+                if (files.length === 0) return;
+                bulkList.classList.remove('hidden');
+                document.getElementById('file-count').innerText = files.length;
+                fileQueue.innerHTML = '';
+                files.forEach(file => {
+                    const div = document.createElement('div');
+                    div.className = "relative bg-gray-50 rounded-xl border border-gray-100 p-2 text-center";
+                    if (file.name.toLowerCase().endsWith('.zip')) {
+                        div.innerHTML = `<div class="aspect-square flex flex-col items-center justify-center text-indigo-500"><i class="bi bi-file-earmark-zip-fill text-4xl"></i><span class="text-[10px] font-bold mt-1 truncate w-full px-2">${file.name}</span></div>`;
+                    } else {
+                        const reader = new FileReader();
+                        reader.onload = (event) => {
+                            div.innerHTML = `<div class="aspect-square rounded-lg overflow-hidden bg-white mb-1"><img src="${event.target.result}" class="w-full h-full object-cover"></div><span class="text-[9px] font-bold text-gray-400 truncate block">${file.name}</span>`;
+                        };
+                        reader.readAsDataURL(file);
+                    }
+                    fileQueue.appendChild(div);
+                });
+            };
+        }
+
+        if (startBulkBtn) {
+            startBulkBtn.onclick = async () => {
+                const files = bulkInput.files;
+                if (!files || files.length === 0) return;
+                const formData = new FormData();
+                for (let i = 0; i < files.length; i++) formData.append('images', files[i]);
+                if (bulkLoader) bulkLoader.classList.remove('hidden');
+                try {
+                    const response = await fetch('/bg-remover/remove-bg/bulk/', {
+                        method: 'POST', body: formData, headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                    });
+                    if (response.ok) {
+                        const blob = await response.blob();
+                        const url = window.URL.createObjectURL(blob);
+                        const bulkCardInner = document.querySelector('#pills-bulk .bg-white');
+                        bulkCardInner.innerHTML = `<div class="text-center py-12"><div class="bg-green-100 text-green-600 p-6 rounded-full inline-block mb-6"><i class="bi bi-check-all text-6xl"></i></div><h2 class="text-3xl font-bold">Complete!</h2><a href="${url}" download="processed.zip" class="bg-indigo-600 text-white px-10 py-4 rounded-2xl font-bold inline-block mt-4">Download ZIP</a></div>`;
+                    }
+                } finally { if (bulkLoader) bulkLoader.classList.add('hidden'); }
+            };
+        }
+    </script>
+    <script>
+        const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
+        const Default = {
+            scrollbarTheme: "os-theme-light",
+            scrollbarAutoHide: "leave",
+            scrollbarClickScroll: true,
+        };
+        document.addEventListener("DOMContentLoaded", function () {
+            const sidebarWrapper = document.querySelector(SELECTOR_SIDEBAR_WRAPPER);
+            if (
+                sidebarWrapper &&
+                typeof OverlayScrollbarsGlobal?.OverlayScrollbars !== "undefined"
+            ) {
+                OverlayScrollbarsGlobal.OverlayScrollbars(sidebarWrapper, {
+                    scrollbars: {
+                        theme: Default.scrollbarTheme,
+                        autoHide: Default.scrollbarAutoHide,
+                        clickScroll: Default.scrollbarClickScroll,
+                    },
+                });
+            }
+        });
+    </script>
+</body>
+</html>

+ 376 - 0
bg_remover/templates/bg_remover_index_old.html

@@ -0,0 +1,376 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>AI Background Remover | Dashboard</title>
+    
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css">
+    <link rel="stylesheet" href="{% static './css/adminlte.css' %}">
+    <script src="https://cdn.tailwindcss.com"></script>
+    <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;800&display=swap" rel="stylesheet">
+
+    <style>
+        /* body { font-family: 'Plus Jakarta Sans', sans-serif; background-color: #f4f6f9; } */
+        .nav-pills .nav-link { color: #64748b; font-weight: 600; border-radius: 12px; padding: 10px 25px; transition: all 0.3s; border: none; background: none; }
+        .nav-pills .nav-link.active { background-color: #3b82f6 !important; color: white !important; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); }
+        .upload-card { transition: all 0.3s ease; border: 2px dashed #cbd5e1; background: #ffffff; }
+        .upload-card:hover { border-color: #3b82f6; background-color: #f8fafc; transform: translateY(-2px); }
+        .preview-box { background: #ffffff; border-radius: 12px; padding: 15px; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); }
+        .checkerboard-bg {
+            background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(-45deg, #eee 25%, transparent 25%), 
+                              linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(-45deg, transparent 75%, #eee 75%);
+            background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
+        }
+    </style>
+</head>
+
+<body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse">
+    <div id="bulk-loader" class="hidden fixed inset-0 bg-black/60 backdrop-blur-md z-[9999] flex flex-col items-center justify-center text-white">
+        <div class="flex flex-col items-center p-8 bg-gray-900/80 rounded-3xl border border-white/10 shadow-2xl">
+            <div class="w-16 h-16 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4"></div>
+            <h2 class="text-2xl font-bold">Processing Batch...</h2>
+            <p class="text-gray-400 mt-2">AI is removing backgrounds and preparing your ZIP.</p>
+            <p class="text-xs text-indigo-400 mt-4 uppercase tracking-widest font-bold">Please do not refresh the page</p>
+        </div>
+    </div>
+
+    <div class="app-wrapper">
+        {% include 'header.html' %}
+        {% include 'sidebar.html' %}
+
+        <main class="app-main">
+            <div class="app-content pt-10">
+                <div class="max-w-5xl mx-auto px-4">
+                    
+                    <div class="text-center mb-8">
+                        <h1 class="text-4xl font-extrabold text-gray-900 mb-2">AI Background Remover</h1>
+                        <p class="text-gray-500 font-medium">Remove image backgrounds instantly for single images, bulk uploads, or ZIP files.</p>
+                    </div>
+
+                    <ul class="nav nav-pills justify-content-center mb-8 gap-3" id="pills-tab" role="tablist">
+                        <li class="nav-item" role="presentation">
+                            <button class="nav-link active" id="pills-single-tab" data-bs-toggle="pill" data-bs-target="#pills-single" type="button" role="tab">
+                                <i class="bi bi-image mr-2"></i> Single Image
+                            </button>
+                        </li>
+                        <li class="nav-item" role="presentation">
+                            <button class="nav-link" id="pills-bulk-tab" data-bs-toggle="pill" data-bs-target="#pills-bulk" type="button" role="tab">
+                                <i class="bi bi-images mr-2"></i> Bulk & ZIP Process
+                            </button>
+                        </li>
+                    </ul>
+
+                    <div class="tab-content" id="pills-tabContent">
+                        <div class="tab-pane fade show active" id="pills-single" role="tabpanel">
+                            <div id="uploadContainer" class="bg-white p-8 rounded-3xl shadow-sm border border-gray-200 mb-8">
+                                <div id="drop-zone" class="upload-card flex flex-col items-center justify-center rounded-2xl p-12 cursor-pointer" onclick="document.getElementById('file-input').click()">
+                                    <input type="file" id="file-input" class="hidden" accept="image/*">
+                                    <div class="bg-blue-50 p-5 rounded-full mb-4 text-blue-600">
+                                        <i class="bi bi-cloud-arrow-up text-4xl"></i>
+                                    </div>
+                                    <p class="text-lg font-semibold text-gray-700">Click or Drag & Drop Image</p>
+                                    <p class="text-sm text-gray-400 mt-1">Supports PNG, JPG, WEBP</p>
+                                </div>
+                            </div>
+
+                            <div id="result-container" class="hidden space-y-8 pb-20">
+                                <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
+                                    <div class="space-y-3">
+                                        <span class="text-xs font-bold uppercase text-gray-400 tracking-wider">Original</span>
+                                        <div class="preview-box"><img id="original-preview" class="rounded-lg w-full h-auto object-contain max-h-[400px]"></div>
+                                    </div>
+                                    <div class="space-y-3">
+                                        <div class="flex justify-between items-center px-1">
+                                            <span class="text-xs font-bold uppercase text-gray-400 tracking-wider">Processed</span>
+                                            <span id="processing-badge" class="hidden animate-pulse bg-blue-100 text-blue-700 px-2 py-0.5 rounded text-[10px] font-bold">PROCESSING...</span>
+                                        </div>
+                                        <div class="preview-box relative min-h-[200px] flex items-center justify-center checkerboard-bg rounded-lg">
+                                            <div id="result-loader" class="absolute inset-0 bg-white/60 backdrop-blur-sm flex flex-col items-center justify-center rounded-lg z-10 hidden">
+                                                <div class="w-10 h-10 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
+                                            </div>
+                                            <img id="processed-preview" class="rounded-lg w-full h-auto object-contain max-h-[400px]">
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="flex gap-3 justify-center">
+                                    <button onclick="location.reload()" class="px-6 py-2 bg-gray-100 rounded-xl font-bold hover:bg-gray-200">New Upload</button>
+                                    <a id="download-btn" class="hidden px-8 py-2 bg-green-600 text-white rounded-xl font-bold shadow-lg hover:bg-green-700">Download PNG</a>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="tab-pane fade" id="pills-bulk" role="tabpanel">
+                            <div class="bg-white p-8 rounded-3xl shadow-sm border border-gray-200 mb-8">
+                                <div id="bulk-drop-zone" class="upload-card flex flex-col items-center justify-center rounded-2xl p-12 cursor-pointer transition-all border-indigo-200" onclick="document.getElementById('bulk-file-input').click()">
+                                    <input type="file" id="bulk-file-input" class="hidden" accept="image/*,.zip" multiple>
+                                    <div class="bg-indigo-50 p-5 rounded-full mb-4 text-indigo-600">
+                                        <i class="bi bi-file-earmark-zip text-4xl"></i>
+                                    </div>
+                                    <h3 class="text-xl font-bold text-gray-800">Bulk & ZIP Upload</h3>
+                                    <p class="text-gray-500 mt-1 text-center">Upload multiple images or a <b>single ZIP file</b> containing images</p>
+                                </div>
+
+                                <div id="bulk-list-container" class="hidden mt-8 border-t pt-8">
+                                    <div class="flex justify-between items-center mb-6">
+                                        <div>
+                                            <h3 class="font-extrabold text-gray-900 text-lg">Batch Queue</h3>
+                                            <p class="text-xs text-gray-400 uppercase font-bold tracking-widest"><span id="file-count">0</span> Items Ready</p>
+                                        </div>
+                                        <button id="start-bulk-btn" class="bg-indigo-600 text-white px-8 py-3 rounded-xl font-bold hover:bg-indigo-700 shadow-lg">
+                                            Start Processing
+                                        </button>
+                                    </div>
+                                    <div id="file-queue" class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-6 gap-4"></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </main>
+        {% include 'footer.html' %}
+    </div>
+
+    <!-- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> -->
+
+       <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"
+        integrity="sha256-H2VM7BKda+v2Z4+DRy69uknwxjyDRhszjXFhsL4gD3w=" crossorigin="anonymous"></script>
+    <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Required Plugin(popperjs for Bootstrap 5)-->
+    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
+        integrity="sha256-whL0tQWoY1Ku1iskqPFvmZ+CHsvmRWx/PIoEvIeWh4I=" crossorigin="anonymous"></script>
+    <!--end::Required Plugin(popperjs for Bootstrap 5)--><!--begin::Required Plugin(Bootstrap 5)-->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
+        integrity="sha256-YMa+wAM6QkVyz999odX7lPRxkoYAan8suedu4k2Zur8=" crossorigin="anonymous"></script>
+    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+    <!--end::Required Plugin(Bootstrap 5)--><!--begin::Required Plugin(AdminLTE)-->
+    <script src="{% static './js/adminlte.js' %}"></script>
+    <!--end::Required Plugin(AdminLTE)--><!--begin::OverlayScrollbars Configure-->
+    <script>
+        const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
+        const Default = {
+            scrollbarTheme: "os-theme-light",
+            scrollbarAutoHide: "leave",
+            scrollbarClickScroll: true,
+        };
+        document.addEventListener("DOMContentLoaded", function () {
+            const sidebarWrapper = document.querySelector(SELECTOR_SIDEBAR_WRAPPER);
+            if (
+                sidebarWrapper &&
+                typeof OverlayScrollbarsGlobal?.OverlayScrollbars !== "undefined"
+            ) {
+                OverlayScrollbarsGlobal.OverlayScrollbars(sidebarWrapper, {
+                    scrollbars: {
+                        theme: Default.scrollbarTheme,
+                        autoHide: Default.scrollbarAutoHide,
+                        clickScroll: Default.scrollbarClickScroll,
+                    },
+                });
+            }
+        });
+    </script>
+    <script>
+        // --- SINGLE UPLOAD LOGIC ---
+        const fileInput = document.getElementById('file-input');
+        if (fileInput) {
+            fileInput.onchange = (e) => handleSingleUpload(e.target.files[0]);
+        }
+
+        async function handleSingleUpload(file) {
+            if (!file) return;
+            document.getElementById('uploadContainer').classList.add('hidden');
+            document.getElementById('result-container').classList.remove('hidden');
+            document.getElementById('result-loader').classList.remove('hidden');
+            document.getElementById('original-preview').src = URL.createObjectURL(file);
+
+            const formData = new FormData();
+            formData.append('image', file);
+
+            try {
+                const response = await fetch('/bg-remover/remove-bg/', {
+                    method: 'POST',
+                    body: formData,
+                    headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                });
+                if (!response.ok) throw new Error("Processing failed");
+                const blob = await response.blob();
+                const url = URL.createObjectURL(blob);
+                document.getElementById('processed-preview').src = url;
+                const btn = document.getElementById('download-btn');
+                btn.href = url;
+                btn.download = `cleared_${file.name.split('.')[0]}.png`;
+                btn.classList.remove('hidden');
+            } catch (err) { 
+                alert("Error processing image: " + err.message); 
+                location.reload();
+            } finally { 
+                document.getElementById('result-loader').classList.add('hidden'); 
+            }
+        }
+
+        // --- BULK & ZIP UPLOAD LOGIC ---
+        const bulkInput = document.getElementById('bulk-file-input');
+        const fileQueue = document.getElementById('file-queue');
+        const bulkList = document.getElementById('bulk-list-container');
+        const startBulkBtn = document.getElementById('start-bulk-btn');
+        const bulkLoader = document.getElementById('bulk-loader');
+
+        if (bulkInput) {
+            bulkInput.onchange = (e) => {
+                const files = Array.from(e.target.files);
+                if (files.length === 0) return;
+
+                bulkList.classList.remove('hidden');
+                document.getElementById('file-count').innerText = files.length;
+                fileQueue.innerHTML = '';
+
+                files.forEach(file => {
+                    const div = document.createElement('div');
+                    div.className = "relative bg-gray-50 rounded-xl border border-gray-100 p-2 text-center";
+
+                    if (file.name.toLowerCase().endsWith('.zip')) {
+                        div.innerHTML = `
+                            <div class="aspect-square flex flex-col items-center justify-center text-indigo-500">
+                                <i class="bi bi-file-earmark-zip-fill text-4xl"></i>
+                                <span class="text-[10px] font-bold mt-1 truncate w-full px-2">${file.name}</span>
+                            </div>`;
+                        fileQueue.appendChild(div);
+                    } else {
+                        const reader = new FileReader();
+                        reader.onload = (event) => {
+                            div.innerHTML = `
+                                <div class="aspect-square rounded-lg overflow-hidden bg-white mb-1">
+                                    <img src="${event.target.result}" class="w-full h-full object-cover">
+                                </div>
+                                <span class="text-[9px] font-bold text-gray-400 truncate block">${file.name}</span>`;
+                            fileQueue.appendChild(div);
+                        };
+                        reader.readAsDataURL(file);
+                    }
+                });
+            };
+        }
+
+        if (startBulkBtn) {
+            startBulkBtn.onclick = async () => {
+                const files = bulkInput.files;
+                if (!files || files.length === 0) {
+                    alert("Please select files first.");
+                    return;
+                }
+
+                const formData = new FormData();
+                for (let i = 0; i < files.length; i++) {
+                    formData.append('images', files[i]);
+                }
+
+                // Show global loader overlay
+                if (bulkLoader) bulkLoader.classList.remove('hidden');
+
+                try {
+                    const response = await fetch('/bg-remover/remove-bg/bulk/', {
+                        method: 'POST',
+                        body: formData,
+                        headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                    });
+
+                    if (response.ok) {
+                        const blob = await response.blob();
+                        const url = window.URL.createObjectURL(blob);
+                        
+                        // 1. Clear and Hide the Selection/Queue UI
+                        const bulkCardInner = document.querySelector('#pills-bulk .bg-white');
+                        bulkCardInner.innerHTML = `
+                            <div class="flex flex-col items-center justify-center py-12 text-center animate-in fade-in zoom-in duration-300">
+                                <div class="bg-green-100 text-green-600 p-6 rounded-full mb-6">
+                                    <i class="bi bi-check-all text-6xl"></i>
+                                </div>
+                                <h2 class="text-3xl font-extrabold text-gray-900 mb-2">Processing Complete!</h2>
+                                <p class="text-gray-500 mb-8 max-w-sm mx-auto">Your images have been processed and are ready for download in a single ZIP archive.</p>
+                                
+                                <div class="flex flex-col sm:flex-row gap-4">
+                                    <a href="${url}" download="processed_images.zip" 
+                                    class="flex items-center gap-3 bg-indigo-600 text-white px-10 py-4 rounded-2xl font-bold hover:bg-indigo-700 shadow-xl shadow-indigo-200 transition-all transform hover:scale-105">
+                                        <i class="bi bi-cloud-arrow-down-fill text-xl"></i>
+                                        Download All Images (ZIP)
+                                    </a>
+                                    <button onclick="location.reload()" 
+                                            class="px-10 py-4 bg-gray-100 text-gray-700 rounded-2xl font-bold hover:bg-gray-200 transition-all">
+                                        Process More
+                                    </button>
+                                </div>
+                            </div>
+                        `;
+
+                        // 2. Trigger automatic download as a convenience
+                        const a = document.createElement('a');
+                        a.href = url;
+                        a.download = "processed_images.zip";
+                        document.body.appendChild(a);
+                        a.click();
+                        a.remove();
+
+                    } else {
+                        alert("Bulk processing failed on the server.");
+                    }
+                } catch (err) {
+                    console.error(err);
+                    alert("An error occurred during upload.");
+                } finally {
+                    if (bulkLoader) bulkLoader.classList.add('hidden');
+                }
+            };
+        }
+
+        // if (startBulkBtn) {
+        //     startBulkBtn.onclick = async () => {
+        //         const files = bulkInput.files;
+        //         if (!files || files.length === 0) {
+        //             alert("Please select files first.");
+        //             return;
+        //         }
+
+        //         const formData = new FormData();
+        //         for (let i = 0; i < files.length; i++) {
+        //             formData.append('images', files[i]);
+        //         }
+
+        //         // Show loader
+        //         if (bulkLoader) bulkLoader.classList.remove('hidden');
+
+        //         try {
+        //             const response = await fetch('/bg-remover/remove-bg/bulk/', {
+        //                 method: 'POST',
+        //                 body: formData,
+        //                 headers: {'X-CSRFToken': '{{ csrf_token }}'}
+        //             });
+
+        //             if (response.ok) {
+        //                 const blob = await response.blob();
+        //                 const url = window.URL.createObjectURL(blob);
+        //                 const a = document.createElement('a');
+        //                 a.href = url;
+        //                 a.download = "processed_images.zip";
+        //                 document.body.appendChild(a);
+        //                 a.click();
+        //                 a.remove();
+        //             } else {
+        //                 alert("Bulk processing failed on the server.");
+        //             }
+        //         } catch (err) {
+        //             console.error(err);
+        //             alert("An error occurred during upload.");
+        //         } finally {
+        //             if (bulkLoader) bulkLoader.classList.add('hidden');
+        //         }
+        //     };
+        // }
+
+        
+    </script>
+</body>
+</html>

+ 194 - 0
bg_remover/templates/templ.html

@@ -0,0 +1,194 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>AI Background Remover</title>
+    
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/styles/overlayscrollbars.min.css">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css">
+    <link rel="stylesheet" href="{% static './css/adminlte.css' %}">
+    <script src="https://cdn.tailwindcss.com"></script>
+
+    <style>
+        .nav-pills .nav-link.active { background-color: #3b82f6 !important; }
+        .checkerboard-bg {
+            background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(-45deg, #eee 25%, transparent 25%), 
+                              linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(-45deg, transparent 75%, #eee 75%);
+            background-size: 20px 20px;
+            background-color: white;
+        }
+    </style>
+</head>
+
+<body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse">
+    
+    <div id="bulk-loader" class="hidden fixed inset-0 bg-black/60 backdrop-blur-md z-[9999] flex flex-col items-center justify-center text-white">
+        <div class="w-16 h-16 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4"></div>
+        <h2 class="text-2xl font-bold">Processing Batch...</h2>
+    </div>
+
+    <div class="app-wrapper">
+        {% include 'header.html' %}
+        {% include 'sidebar.html' %}
+
+        <main class="app-main">
+            <div class="app-content pt-10">
+                <div class="max-w-5xl mx-auto px-4">
+                    
+                    <ul class="nav nav-pills justify-content-center mb-8 gap-3" id="pills-tab" role="tablist">
+                        <li class="nav-item">
+                            <button class="nav-link active" id="pills-single-tab" data-bs-toggle="pill" data-bs-target="#pills-single" type="button">Single Image</button>
+                        </li>
+                        <li class="nav-item">
+                            <button class="nav-link" id="pills-bulk-tab" data-bs-toggle="pill" data-bs-target="#pills-bulk" type="button">Bulk Process</button>
+                        </li>
+                    </ul>
+
+                    <div class="tab-content">
+                        <div class="tab-pane fade show active" id="pills-single">
+                            <div id="single-upload-card" class="bg-white p-8 rounded-3xl shadow-sm border border-gray-200">
+                                <div class="border-2 border-dashed border-gray-300 flex flex-col items-center justify-center rounded-2xl p-12 cursor-pointer hover:border-blue-500 transition-all" onclick="document.getElementById('single-file-input').click()">
+                                    <input type="file" id="single-file-input" class="hidden" accept="image/*">
+                                    <i class="bi bi-image text-4xl text-blue-600 mb-4"></i>
+                                    <p class="text-lg font-semibold text-gray-700">Upload a single image</p>
+                                </div>
+                            </div>
+
+                            <div id="single-result-area" class="hidden mt-8 space-y-6">
+                                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
+                                    <div>
+                                        <p class="text-xs font-bold text-gray-400 uppercase mb-2">Original</p>
+                                        <img id="single-orig-prev" class="rounded-xl w-full border bg-white p-1">
+                                    </div>
+                                    <div>
+                                        <p class="text-xs font-bold text-gray-400 uppercase mb-2">Result</p>
+                                        <div class="checkerboard-bg rounded-xl border p-1 relative min-h-[200px] flex items-center justify-center">
+                                            <div id="single-item-loader" class="hidden animate-spin w-10 h-10 border-4 border-blue-500 border-t-transparent rounded-full"></div>
+                                            <img id="single-proc-prev" class="rounded-xl w-full">
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="flex justify-center gap-4">
+                                    <button onclick="location.reload()" class="px-6 py-2 bg-gray-100 rounded-xl font-bold">Try Another</button>
+                                    <a id="single-download-btn" class="hidden px-10 py-2 bg-green-600 text-white rounded-xl font-bold shadow-lg">Download PNG</a>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="tab-pane fade" id="pills-bulk">
+                            <div id="bulk-container" class="bg-white p-8 rounded-3xl shadow-sm border border-gray-200">
+                                <div class="border-2 border-dashed border-gray-300 flex flex-col items-center justify-center rounded-2xl p-12 cursor-pointer hover:border-indigo-500" onclick="document.getElementById('bulk-file-input').click()">
+                                    <input type="file" id="bulk-file-input" class="hidden" accept="image/*" multiple>
+                                    <i class="bi bi-layers text-4xl text-indigo-600 mb-4"></i>
+                                    <p class="text-lg font-semibold">Select Multiple Images</p>
+                                </div>
+                                <div id="bulk-queue-section" class="hidden mt-6">
+                                    <div class="flex justify-between items-center mb-4">
+                                        <span class="font-bold text-gray-700"><span id="bulk-count">0</span> files selected</span>
+                                        <button id="start-bulk-btn" class="bg-indigo-600 text-white px-6 py-2 rounded-lg font-bold">Process & Zip</button>
+                                    </div>
+                                    <div id="bulk-preview-grid" class="grid grid-cols-4 md:grid-cols-6 gap-2"></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </main>
+    </div>
+
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"></script>
+    <script src="{% static './js/adminlte.js' %}"></script>
+
+    <script>
+        // --- SINGLE IMAGE LOGIC ---
+        const singleInput = document.getElementById('single-file-input');
+        singleInput.onchange = async (e) => {
+            const file = e.target.files[0];
+            if (!file) return;
+
+            // Show UI
+            document.getElementById('single-upload-card').classList.add('hidden');
+            document.getElementById('single-result-area').classList.remove('hidden');
+            document.getElementById('single-item-loader').classList.remove('hidden');
+            document.getElementById('single-orig-prev').src = URL.createObjectURL(file);
+
+            const formData = new FormData();
+            formData.append('image', file);
+
+            try {
+                const response = await fetch('/bg-remover/remove-bg/single/', {
+                    method: 'POST',
+                    body: formData,
+                    headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                });
+                
+                if (response.ok) {
+                    const blob = await response.blob();
+                    const url = URL.createObjectURL(blob);
+                    document.getElementById('single-proc-prev').src = url;
+                    const dlBtn = document.getElementById('single-download-btn');
+                    dlBtn.href = url;
+                    dlBtn.download = "bg-removed.png";
+                    dlBtn.classList.remove('hidden');
+                }
+            } catch (err) { alert("Error processing image."); }
+            finally { document.getElementById('single-item-loader').classList.add('hidden'); }
+        };
+
+        // --- BULK IMAGE LOGIC ---
+        const bulkInput = document.getElementById('bulk-file-input');
+        bulkInput.onchange = (e) => {
+            const files = Array.from(e.target.files);
+            document.getElementById('bulk-queue-section').classList.remove('hidden');
+            document.getElementById('bulk-count').innerText = files.length;
+            const grid = document.getElementById('bulk-preview-grid');
+            grid.innerHTML = '';
+            files.slice(0, 12).forEach(f => {
+                const img = document.createElement('div');
+                img.className = "h-12 bg-gray-100 rounded border text-[8px] overflow-hidden p-1";
+                img.innerText = f.name;
+                grid.appendChild(img);
+            });
+        };
+
+        document.getElementById('start-bulk-btn').onclick = async () => {
+            const formData = new FormData();
+            for(let f of bulkInput.files) { formData.append('images', f); }
+            document.getElementById('bulk-loader').classList.remove('hidden');
+
+            try {
+                const res = await fetch('/bg-remover/remove-bg/bulk/', {
+                    method: 'POST', body: formData, headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                });
+                if (res.ok) {
+                    const blob = await res.blob();
+                    const url = URL.createObjectURL(blob);
+                    document.getElementById('bulk-container').innerHTML = `
+                        <div class="text-center py-10">
+                            <i class="bi bi-check-circle-fill text-6xl text-green-500 mb-4"></i>
+                            <h2 class="text-2xl font-bold mb-6">ZIP File Ready</h2>
+                            <a href="${url}" download="processed.zip" class="bg-indigo-600 text-white px-10 py-4 rounded-xl font-bold">Download All Images</a>
+                            <button onclick="location.reload()" class="block mx-auto mt-6 text-gray-400 underline">Upload More</button>
+                        </div>`;
+                }
+            } finally { document.getElementById('bulk-loader').classList.add('hidden'); }
+        };
+
+        // AdminLTE Sidebar Scrollbar
+        document.addEventListener("DOMContentLoaded", function () {
+            const sidebarWrapper = document.querySelector(".sidebar-wrapper");
+            if (sidebarWrapper && typeof OverlayScrollbarsGlobal?.OverlayScrollbars !== "undefined") {
+                OverlayScrollbarsGlobal.OverlayScrollbars(sidebarWrapper, {
+                    scrollbars: { theme: "os-theme-light", autoHide: "leave", clickScroll: true }
+                });
+            }
+        });
+    </script>
+</body>
+</html>

+ 3 - 0
bg_remover/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 11 - 0
bg_remover/urls.py

@@ -0,0 +1,11 @@
+from django.contrib import admin
+from django.urls import path
+from . import views
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns = [
+    path('remove-bg/', views.BackgroundRemovalView.as_view(), name='remove_bg'),
+    path("remove-bg/bulk/", views.BulkBackgroundRemovalAPI.as_view(), name="remove_bg_bulk"),
+
+]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

+ 106 - 0
bg_remover/views.py

@@ -0,0 +1,106 @@
+# background_remover/views.py
+import io
+from PIL import Image
+from django.http import HttpResponse
+from rest_framework.views import APIView
+from rest_framework.parsers import MultiPartParser
+from .services import BiRefNetService
+from django.shortcuts import render
+import zipfile
+import time
+
+class BackgroundRemovalView(APIView):
+    parser_classes = [MultiPartParser]
+
+    def get(self, request):
+        return render(request, "bg_remover_index.html")
+
+    def post(self, request, *args, **kwargs):
+        if 'image' not in request.FILES:
+            return HttpResponse("No image provided", status=400)
+
+        # 1. Load image from request
+        input_file = request.FILES['image']
+        input_image = Image.open(input_file)
+
+        # 2. Process via Service
+        service = BiRefNetService()
+        output_image = service.remove_background(input_image)
+
+        # 3. Prepare response
+        buffer = io.BytesIO()
+        output_image.save(buffer, format="PNG")
+        
+        return HttpResponse(buffer.getvalue(), content_type="image/png")
+    
+class BulkBackgroundRemovalAPI(APIView):
+    parser_classes = [MultiPartParser]
+
+    def post(self, request):
+        files = request.FILES.getlist("images")
+
+        if not files:
+            return HttpResponse({"error": "No images provided"}, status=400)
+
+        service = BiRefNetService()
+        start_time = time.time()
+
+        zip_buffer = io.BytesIO()
+
+        # with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
+            # for file in files:
+            #     try:
+            #         image = Image.open(file).convert("RGB")
+            #         output = service.remove_background(image)
+
+            #         img_buffer = io.BytesIO()
+            #         output.save(img_buffer, format="PNG")
+
+            #         name = file.name.rsplit(".", 1)[0] + "_whitebg.png"
+            #         zip_file.writestr(name, img_buffer.getvalue())
+
+            #     except Exception as e:
+            #         print(f"Error processing {file.name}: {e}")
+
+        with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as master_zip:
+            for file in files:
+                # CHECK IF FILE IS A ZIP
+                if file.name.lower().endswith('.zip'):
+                    with zipfile.ZipFile(file, 'r') as user_zip:
+                        for inner_file_name in user_zip.namelist():
+                            # Skip directories and non-image files
+                            if inner_file_name.endswith(('.png', '.jpg', '.jpeg', '.webp')):
+                                with user_zip.open(inner_file_name) as inner_file:
+                                    img_data = inner_file.read()
+                                    image = Image.open(io.BytesIO(img_data)).convert("RGB")
+                                    output = service.remove_background(image)
+                                    
+                                    img_io = io.BytesIO()
+                                    output.save(img_io, format="PNG")
+                                    
+                                    # Create clean name for the output zip
+                                    clean_name = inner_file_name.rsplit(".", 1)[0].split('/')[-1] + "_no_bg.png"
+                                    master_zip.writestr(clean_name, img_io.getvalue())
+                
+                # PROCESS INDIVIDUAL IMAGES (Existing logic)
+                else:
+                    try:
+                        image = Image.open(file).convert("RGB")
+                        output = service.remove_background(image)
+                        img_io = io.BytesIO()
+                        output.save(img_io, format="PNG")
+                        name = file.name.rsplit(".", 1)[0] + "_no_bg.png"
+                        master_zip.writestr(name, img_io.getvalue())
+                    except Exception as e:
+                        print(f"Error processing {file.name}: {e}")
+        zip_buffer.seek(0)
+        total_time = round(time.time() - start_time, 2)
+
+        response = HttpResponse(
+            zip_buffer.getvalue(),
+            content_type="application/zip"
+        )
+        response["Content-Disposition"] = "attachment; filename=processed_images.zip"
+        response["X-Processing-Time"] = f"{total_time}s"
+
+        return response    

+ 2 - 0
content_quality_tool/settings.py

@@ -35,7 +35,9 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'core',
+    'title_creator_app',
     'rest_framework',
+    'bg_remover',
     # 'attr_extraction',
     'attr_extraction.apps.AttrExtractionConfig',
     # 'attr_extraction.apps.ProductAttributesConfig',  # Full path

+ 2 - 0
content_quality_tool/urls.py

@@ -33,6 +33,8 @@ urlpatterns = [
     path("attr/", include("attr_extraction.urls")),
     path('ca/', include('ca.urls')),
     path('api/', include('api.urls')),
+    path('title-creator/', include('title_creator_app.urls')),
+    path('bg-remover/', include('bg_remover.urls')),
     # path("", views.login_view, name="login_view"),
 ]
 

+ 44 - 5
content_quality_tool_public/templates/sidebar.html

@@ -30,11 +30,11 @@
                     </a> </li> 
                     
                 
-                <li class="nav-item"> <a href="{% url 'product-attributes' %}" class="nav-link {% if request.path == '/product-attributes/' %}active{% endif %}"> <i
+                <li class="nav-item"> <a href="{% url 'product-attributes' %}"  title="Product Attributes" class="nav-link {% if request.path == '/product-attributes/' %}active{% endif %}"> <i
                             class="nav-icon bi bi-tag"></i>
                         <p>Product Attributes</p>
                     </a> </li>
-                <li class="nav-item"> <a href="{% url 'cia' %}" target="_blank" class="nav-link {% if request.path == '/ca/cia/' %}active{% endif %}"> <i
+                <li class="nav-item"> <a href="{% url 'cia' %}"  title="Competitor Intelligence Report" target="_blank" class="nav-link {% if request.path == '/ca/cia/' %}active{% endif %}"> <i
                             class="nav-icon bi bi-graph-up"></i>
                         <p>Competitor Intelligence Report</p>
                     </a> </li> 
@@ -57,11 +57,50 @@
                             
                             </ul>
                     </li> -->
-                 
-                <li class="nav-item"> <a href="{% url 'attribure-column-mapping-setting' %}"  class="nav-link {% if request.path == '/attribure-column-mapping-setting/' %}active{% endif %}"> <i
+                
+                <li class="nav-item"> <a href="{% url 'attribure-column-mapping-setting' %}" title="Product Attribute Column Mapping" class="nav-link {% if request.path == '/attribure-column-mapping-setting/' %}active{% endif %}"> <i
                             class="nav-icon bi bi-gear"></i>
                         <p>Product Attribute Column Mapping</p>
-                    </a> </li>    
+                    </a> 
+                </li>    
+                <li class="nav-item"> <a href="{% url 'remove_bg' %}" title="Image Background Remover" class="nav-link {% if request.path == '/bg-remover/remove-bg/' %}active{% endif %}"> <i
+                            class="nav-icon bi bi-eraser-fill"></i>
+                        <p>Image Background Remover</p>
+                    </a> 
+                </li> 
+                <!-- {{request.resolver_match.url_name}} {{request.path}} -->
+                <li class="nav-item has-treeview  {% if request.path == '/title-creator/upload/' or request.path == '/title-creator/master/' %}active{% endif %}">
+                    <a href="#" class="nav-link {% if request.path == '/title-creator/upload/' or request.path == '/title-creator/master/' %}active{% endif %}">
+                        <i class="nav-icon bi bi-pencil-square"></i>
+                        <p>
+                            Title Creator
+                            <i class="right bi bi-chevron-left"></i>
+                        </p>
+                    </a>
+
+                    <ul class="nav nav-treeview">
+                        <!-- Title Generator -->
+                        <li class="nav-item">
+                            <a href="{% url 'title_creator_home' %}"
+                            class="nav-link {% if request.resolver_match.url_name == 'title_creator_home' %}active{% endif %}">
+                                <i class="nav-icon bi bi-magic"></i>
+                                <p>Title Generator</p>
+                            </a>
+                        </li>
+
+                        <!-- Master -->
+                        <li class="nav-item">
+                            <a href="{% url 'title_creator_master' %}"
+                            class="nav-link {% if request.resolver_match.url_name == 'title_creator_master' %}active{% endif %}">
+                                <i class="nav-icon bi bi-gear-fill"></i>
+                                <p>Title Master</p>
+                            </a>
+                        </li>
+                    </ul>
+                </li>
+
+
+
                 
 
             </ul> <!--end::Sidebar Menu-->

+ 0 - 0
title_creator_app/__init__.py


+ 3 - 0
title_creator_app/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 6 - 0
title_creator_app/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class TitleCreatorAppConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'title_creator_app'

+ 10 - 0
title_creator_app/decorators.py

@@ -0,0 +1,10 @@
+from functools import wraps
+from django.shortcuts import redirect
+
+def login_required(view_func):
+    @wraps(view_func)
+    def _wrapped_view(request, *args, **kwargs):
+        if 'user_email' not in request.session:
+            return redirect('login_view')  # Redirect to your login URL name
+        return view_func(request, *args, **kwargs)
+    return _wrapped_view

+ 30 - 0
title_creator_app/migrations/0001_initial.py

@@ -0,0 +1,30 @@
+# Generated by Django 5.2.9 on 2025-12-18 13:40
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='AttributeMaster',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('is_mandatory', models.BooleanField(default=False)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='TitleMapping',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('product_type', models.CharField(max_length=255, unique=True)),
+                ('format_sequence', models.TextField()),
+            ],
+        ),
+    ]

+ 24 - 0
title_creator_app/migrations/0002_processingtask.py

@@ -0,0 +1,24 @@
+# Generated by Django 5.2.9 on 2025-12-19 13:25
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('title_creator_app', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ProcessingTask',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('task_id', models.CharField(max_length=100, unique=True)),
+                ('original_filename', models.CharField(blank=True, max_length=255, null=True)),
+                ('status', models.CharField(default='PENDING', max_length=20)),
+                ('download_url', models.TextField(blank=True, null=True)),
+                ('created_at', models.DateTimeField(auto_now_add=True)),
+            ],
+        ),
+    ]

+ 0 - 0
title_creator_app/migrations/__init__.py


+ 29 - 0
title_creator_app/models.py

@@ -0,0 +1,29 @@
+from django.db import models
+
+class AttributeMaster(models.Model):
+    name = models.CharField(max_length=100, unique=True) # e.g., "Brand", "Capacity"
+    is_mandatory = models.BooleanField(default=False)
+
+    def __str__(self):
+        return self.name
+
+class TitleMapping(models.Model):
+    product_type = models.CharField(max_length=255, unique=True) # e.g., "Flammable Safety Cabinet-Links"
+    # Store sequence as a comma-separated string: "Brand,Product Type,Door Type,Capacity,Dimensions,Color"
+    format_sequence = models.TextField() 
+
+    def get_sequence_list(self):
+        return [item.strip() for item in self.format_sequence.split(',') if item.strip()]
+
+    def __str__(self):
+        return self.product_type
+    
+class ProcessingTask(models.Model):
+    task_id = models.CharField(max_length=100, unique=True)
+    original_filename = models.CharField(max_length=255, null=True, blank=True) # New field
+    status = models.CharField(max_length=20, default='PENDING') 
+    download_url = models.TextField(null=True, blank=True)
+    created_at = models.DateTimeField(auto_now_add=True)
+
+    def __str__(self):
+        return f"{self.original_filename} - {self.status}"    

+ 15380 - 0
title_creator_app/static/css/adminlte.css

@@ -0,0 +1,15380 @@
+@charset "UTF-8";
+/*!
+ *   AdminLTE v4.0.0-beta2
+ *   Author: Colorlib
+ *   Website: AdminLTE.io <https://adminlte.io>
+ *   License: Open source - MIT <https://opensource.org/licenses/MIT>
+ */
+:root,
+[data-bs-theme=light] {
+  --bs-blue: #01445E;
+  --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: #01445E;
+  --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: "aptos", "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: 14px;
+  --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: #01445E;
+  --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;
+}
+
+*,
+*::before,
+*::after {
+  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: rgba(0, 0, 0, 0);
+}
+
+hr {
+  margin: 1rem 0;
+  color: inherit;
+  border: 0;
+  border-top: var(--bs-border-width) solid;
+  opacity: 0.25;
+}
+
+h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 {
+  margin-top: 0;
+  margin-bottom: 0.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 + 0.9vw);
+}
+@media (min-width: 1200px) {
+  h2, .h2 {
+    font-size: 2rem;
+  }
+}
+
+h3, .h3 {
+  font-size: calc(1rem + 0.6vw);
+}
+@media (min-width: 1200px) {
+  h3, .h3 {
+    font-size: 1.55rem;
+  }
+}
+
+h4, .h4 {
+  font-size: calc(1.275rem + 0.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;
+}
+
+ol,
+ul,
+dl {
+  margin-top: 0;
+  margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+  margin-bottom: 0;
+}
+
+dt {
+  font-weight: 700;
+}
+
+dd {
+  margin-bottom: 0.5rem;
+  margin-left: 0;
+}
+
+blockquote {
+  margin: 0 0 1rem;
+}
+
+b,
+strong {
+  font-weight: bolder;
+}
+
+small, .small {
+  font-size: 0.875em;
+}
+
+mark, .mark {
+  padding: 0.1875em;
+  color: var(--bs-highlight-color);
+  background-color: var(--bs-highlight-bg);
+}
+
+sub,
+sup {
+  position: relative;
+  font-size: 0.75em;
+  line-height: 0;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.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;
+}
+
+pre,
+code,
+kbd,
+samp {
+  font-family: var(--bs-font-monospace);
+  font-size: 1em;
+}
+
+pre {
+  display: block;
+  margin-top: 0;
+  margin-bottom: 1rem;
+  overflow: auto;
+  font-size: 0.875em;
+}
+pre code {
+  font-size: inherit;
+  color: inherit;
+  word-break: normal;
+}
+
+code {
+  font-size: 0.875em;
+  color: var(--bs-code-color);
+  word-wrap: break-word;
+}
+a > code {
+  color: inherit;
+}
+
+kbd {
+  padding: 0.1875rem 0.375rem;
+  font-size: 0.875em;
+  color: var(--bs-body-bg);
+  background-color: var(--bs-body-color);
+  border-radius: 0.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: 0.5rem;
+  padding-bottom: 0.5rem;
+  color: var(--bs-secondary-color);
+  text-align: left;
+}
+
+th {
+  text-align: inherit;
+  text-align: -webkit-match-parent;
+}
+
+thead,
+tbody,
+tfoot,
+tr,
+td,
+th {
+  border-color: inherit;
+  border-style: solid;
+  border-width: 0;
+}
+
+label {
+  display: inline-block;
+}
+
+button {
+  border-radius: 0;
+}
+
+button:focus:not(:focus-visible) {
+  outline: 0;
+}
+
+input,
+button,
+select,
+optgroup,
+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;
+}
+
+button,
+[type=button],
+[type=reset],
+[type=submit] {
+  -webkit-appearance: button;
+}
+button:not(:disabled),
+[type=button]:not(:disabled),
+[type=reset]:not(:disabled),
+[type=submit]: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: 0.5rem;
+  font-size: calc(1.275rem + 0.3vw);
+  line-height: inherit;
+}
+@media (min-width: 1200px) {
+  legend {
+    font-size: 1.5rem;
+  }
+}
+legend + * {
+  clear: left;
+}
+
+::-webkit-datetime-edit-fields-wrapper,
+::-webkit-datetime-edit-text,
+::-webkit-datetime-edit-minute,
+::-webkit-datetime-edit-hour-field,
+::-webkit-datetime-edit-day-field,
+::-webkit-datetime-edit-month-field,
+::-webkit-datetime-edit-year-field {
+  padding: 0;
+}
+
+::-webkit-inner-spin-button {
+  height: auto;
+}
+
+[type=search] {
+  -webkit-appearance: textfield;
+  outline-offset: -2px;
+}
+
+/* rtl:raw:
+[type="tel"],
+[type="url"],
+[type="email"],
+[type="number"] {
+  direction: ltr;
+}
+*/
+::-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-size: calc(1.625rem + 4.5vw);
+  font-weight: 300;
+  line-height: 1.2;
+}
+@media (min-width: 1200px) {
+  .display-1 {
+    font-size: 5rem;
+  }
+}
+
+.display-2 {
+  font-size: calc(1.575rem + 3.9vw);
+  font-weight: 300;
+  line-height: 1.2;
+}
+@media (min-width: 1200px) {
+  .display-2 {
+    font-size: 4.5rem;
+  }
+}
+
+.display-3 {
+  font-size: calc(1.525rem + 3.3vw);
+  font-weight: 300;
+  line-height: 1.2;
+}
+@media (min-width: 1200px) {
+  .display-3 {
+    font-size: 4rem;
+  }
+}
+
+.display-4 {
+  font-size: calc(1.475rem + 2.7vw);
+  font-weight: 300;
+  line-height: 1.2;
+}
+@media (min-width: 1200px) {
+  .display-4 {
+    font-size: 3.5rem;
+  }
+}
+
+.display-5 {
+  font-size: calc(1.425rem + 2.1vw);
+  font-weight: 300;
+  line-height: 1.2;
+}
+@media (min-width: 1200px) {
+  .display-5 {
+    font-size: 3rem;
+  }
+}
+
+.display-6 {
+  font-size: calc(1.375rem + 1.5vw);
+  font-weight: 300;
+  line-height: 1.2;
+}
+@media (min-width: 1200px) {
+  .display-6 {
+    font-size: 2.5rem;
+  }
+}
+
+.list-unstyled {
+  padding-left: 0;
+  list-style: none;
+}
+
+.list-inline {
+  padding-left: 0;
+  list-style: none;
+}
+
+.list-inline-item {
+  display: inline-block;
+}
+.list-inline-item:not(:last-child) {
+  margin-right: 0.5rem;
+}
+
+.initialism {
+  font-size: 0.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: 0.875em;
+  color: #6c757d;
+}
+.blockquote-footer::before {
+  content: "— ";
+}
+
+.img-fluid {
+  max-width: 100%;
+  height: auto;
+}
+
+.img-thumbnail {
+  padding: 0.25rem;
+  background-color: var(--bs-body-bg);
+  border: var(--bs-border-width) solid var(--bs-border-color);
+  border-radius: var(--bs-border-radius);
+  box-shadow: var(--bs-box-shadow-sm);
+  max-width: 100%;
+  height: auto;
+}
+
+.figure {
+  display: inline-block;
+}
+
+.figure-img {
+  margin-bottom: 0.5rem;
+  line-height: 1;
+}
+
+.figure-caption {
+  font-size: 0.875em;
+  color: var(--bs-secondary-color);
+}
+
+.container,
+.container-fluid,
+.container-xxl,
+.container-xl,
+.container-lg,
+.container-md,
+.container-sm {
+  --bs-gutter-x: 1.5rem;
+  --bs-gutter-y: 0;
+  width: 100%;
+  padding-right: calc(var(--bs-gutter-x) * 0.5);
+  padding-left: calc(var(--bs-gutter-x) * 0.5);
+  margin-right: auto;
+  margin-left: auto;
+}
+
+@media (min-width: 576px) {
+  .container-sm, .container {
+    max-width: 540px;
+  }
+}
+@media (min-width: 768px) {
+  .container-md, .container-sm, .container {
+    max-width: 720px;
+  }
+}
+@media (min-width: 992px) {
+  .container-lg, .container-md, .container-sm, .container {
+    max-width: 960px;
+  }
+}
+@media (min-width: 1200px) {
+  .container-xl, .container-lg, .container-md, .container-sm, .container {
+    max-width: 1140px;
+  }
+}
+@media (min-width: 1400px) {
+  .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {
+    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(-0.5 * var(--bs-gutter-x));
+  margin-left: calc(-0.5 * var(--bs-gutter-x));
+}
+.row > * {
+  flex-shrink: 0;
+  width: 100%;
+  max-width: 100%;
+  padding-right: calc(var(--bs-gutter-x) * 0.5);
+  padding-left: calc(var(--bs-gutter-x) * 0.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: 0.5rem 0.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: 0.25rem 0.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(even) {
+  --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: 0.5rem;
+}
+
+.col-form-label {
+  padding-top: calc(0.375rem + var(--bs-border-width));
+  padding-bottom: calc(0.375rem + var(--bs-border-width));
+  margin-bottom: 0;
+  font-size: inherit;
+  line-height: 1.5;
+}
+
+.col-form-label-lg {
+  padding-top: calc(0.5rem + var(--bs-border-width));
+  padding-bottom: calc(0.5rem + var(--bs-border-width));
+  font-size: 1.25rem;
+}
+
+.col-form-label-sm {
+  padding-top: calc(0.25rem + var(--bs-border-width));
+  padding-bottom: calc(0.25rem + var(--bs-border-width));
+  font-size: 0.875rem;
+}
+
+.form-text {
+  margin-top: 0.25rem;
+  font-size: 0.875em;
+  color: var(--bs-secondary-color);
+}
+
+.form-control {
+  display: block;
+  width: 100%;
+  padding: 0.375rem 0.75rem;
+  font-size: 14px;
+  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);
+  box-shadow: var(--bs-box-shadow-inset);
+  transition: border-color 0.15s ease-in-out, box-shadow 0.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: var(--bs-box-shadow-inset), 0 0 0 0.25rem rgba(13, 110, 253, 0.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::-moz-placeholder {
+  color: var(--bs-secondary-color);
+  opacity: 1;
+}
+.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: 0.375rem 0.75rem;
+  margin: -0.375rem -0.75rem;
+  -webkit-margin-end: 0.75rem;
+  margin-inline-end: 0.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 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;
+  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;
+}
+.form-control::file-selector-button {
+  padding: 0.375rem 0.75rem;
+  margin: -0.375rem -0.75rem;
+  -webkit-margin-end: 0.75rem;
+  margin-inline-end: 0.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 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;
+}
+@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: 0.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-sm, .form-control-plaintext.form-control-lg {
+  padding-right: 0;
+  padding-left: 0;
+}
+
+.form-control-sm {
+  min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));
+  padding: 0.25rem 0.5rem;
+  font-size: 0.875rem;
+  border-radius: var(--bs-border-radius-sm);
+}
+.form-control-sm::-webkit-file-upload-button {
+  padding: 0.25rem 0.5rem;
+  margin: -0.25rem -0.5rem;
+  -webkit-margin-end: 0.5rem;
+  margin-inline-end: 0.5rem;
+}
+.form-control-sm::file-selector-button {
+  padding: 0.25rem 0.5rem;
+  margin: -0.25rem -0.5rem;
+  -webkit-margin-end: 0.5rem;
+  margin-inline-end: 0.5rem;
+}
+
+.form-control-lg {
+  min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));
+  padding: 0.5rem 1rem;
+  font-size: 1.25rem;
+  border-radius: var(--bs-border-radius-lg);
+}
+.form-control-lg::-webkit-file-upload-button {
+  padding: 0.5rem 1rem;
+  margin: -0.5rem -1rem;
+  -webkit-margin-end: 1rem;
+  margin-inline-end: 1rem;
+}
+.form-control-lg::file-selector-button {
+  padding: 0.5rem 1rem;
+  margin: -0.5rem -1rem;
+  -webkit-margin-end: 1rem;
+  margin-inline-end: 1rem;
+}
+
+textarea.form-control {
+  min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2));
+}
+textarea.form-control-sm {
+  min-height: calc(1.5em + 0.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 + 0.75rem + calc(var(--bs-border-width) * 2));
+  padding: 0.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 + 0.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: 0.375rem 2.25rem 0.375rem 0.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 0.75rem center;
+  background-size: 16px 12px;
+  border: var(--bs-border-width) solid var(--bs-border-color);
+  border-radius: var(--bs-border-radius);
+  box-shadow: var(--bs-box-shadow-inset);
+  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .form-select {
+    transition: none;
+  }
+}
+.form-select:focus {
+  border-color: #86b7fe;
+  outline: 0;
+  box-shadow: var(--bs-box-shadow-inset), 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
+}
+.form-select[multiple], .form-select[size]:not([size="1"]) {
+  padding-right: 0.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: 0.25rem;
+  padding-bottom: 0.25rem;
+  padding-left: 0.5rem;
+  font-size: 0.875rem;
+  border-radius: var(--bs-border-radius-sm);
+}
+
+.form-select-lg {
+  padding-top: 0.5rem;
+  padding-bottom: 0.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: 0.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: 0.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: 0.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 0.25rem rgba(13, 110, 253, 0.25);
+}
+.form-check-input:checked {
+  background-color: #01445E;
+  border-color: #01445E;
+}
+.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: #01445E;
+  border-color: #01445E;
+  --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: 0.5;
+}
+.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label {
+  cursor: default;
+  opacity: 0.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 0.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: 0.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 0.25rem rgba(13, 110, 253, 0.25);
+}
+.form-range:focus::-moz-range-thumb {
+  box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
+}
+.form-range::-moz-focus-outer {
+  border: 0;
+}
+.form-range::-webkit-slider-thumb {
+  width: 1rem;
+  height: 1rem;
+  margin-top: -0.25rem;
+  -webkit-appearance: none;
+  appearance: none;
+  background-color: #01445E;
+  border: 0;
+  border-radius: 1rem;
+  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);
+  -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.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: 0.5rem;
+  color: transparent;
+  cursor: pointer;
+  background-color: var(--bs-secondary-bg);
+  border-color: transparent;
+  border-radius: 1rem;
+  box-shadow: var(--bs-box-shadow-inset);
+}
+.form-range::-moz-range-thumb {
+  width: 1rem;
+  height: 1rem;
+  -moz-appearance: none;
+  appearance: none;
+  background-color: #01445E;
+  border: 0;
+  border-radius: 1rem;
+  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);
+  -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.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: 0.5rem;
+  color: transparent;
+  cursor: pointer;
+  background-color: var(--bs-secondary-bg);
+  border-color: transparent;
+  border-radius: 1rem;
+  box-shadow: var(--bs-box-shadow-inset);
+}
+.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;
+  height: 100%;
+  padding: 1rem 0.75rem;
+  overflow: hidden;
+  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 0.1s ease-in-out, transform 0.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 0.75rem;
+}
+.form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder {
+  color: transparent;
+}
+.form-floating > .form-control::placeholder,
+.form-floating > .form-control-plaintext::placeholder {
+  color: transparent;
+}
+.form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) {
+  padding-top: 1.625rem;
+  padding-bottom: 0.625rem;
+}
+.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown),
+.form-floating > .form-control-plaintext:focus,
+.form-floating > .form-control-plaintext:not(:placeholder-shown) {
+  padding-top: 1.625rem;
+  padding-bottom: 0.625rem;
+}
+.form-floating > .form-control:-webkit-autofill,
+.form-floating > .form-control-plaintext:-webkit-autofill {
+  padding-top: 1.625rem;
+  padding-bottom: 0.625rem;
+}
+.form-floating > .form-select {
+  padding-top: 1.625rem;
+  padding-bottom: 0.625rem;
+}
+.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label {
+  color: rgba(var(--bs-body-color-rgb), 0.65);
+  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
+}
+.form-floating > .form-control:focus ~ label,
+.form-floating > .form-control:not(:placeholder-shown) ~ label,
+.form-floating > .form-control-plaintext ~ label,
+.form-floating > .form-select ~ label {
+  color: rgba(var(--bs-body-color-rgb), 0.65);
+  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
+}
+.form-floating > .form-control:not(:-moz-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 > .form-control:focus ~ label::after,
+.form-floating > .form-control:not(:placeholder-shown) ~ label::after,
+.form-floating > .form-control-plaintext ~ label::after,
+.form-floating > .form-select ~ 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 > .form-control:-webkit-autofill ~ label {
+  color: rgba(var(--bs-body-color-rgb), 0.65);
+  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
+}
+.form-floating > .form-control-plaintext ~ label {
+  border-width: var(--bs-border-width) 0;
+}
+.form-floating > :disabled ~ label,
+.form-floating > .form-control:disabled ~ label {
+  color: #6c757d;
+}
+.form-floating > :disabled ~ label::after,
+.form-floating > .form-control:disabled ~ label::after {
+  background-color: var(--bs-secondary-bg);
+}
+
+.input-group {
+  position: relative;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: stretch;
+  width: 100%;
+}
+.input-group > .form-control,
+.input-group > .form-select,
+.input-group > .form-floating {
+  position: relative;
+  flex: 1 1 auto;
+  width: 1%;
+  min-width: 0;
+}
+.input-group > .form-control:focus,
+.input-group > .form-select:focus,
+.input-group > .form-floating:focus-within {
+  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: 0.375rem 0.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 > .form-control,
+.input-group-lg > .form-select,
+.input-group-lg > .input-group-text,
+.input-group-lg > .btn {
+  padding: 0.5rem 1rem;
+  font-size: 1.25rem;
+  border-radius: var(--bs-border-radius-lg);
+}
+
+.input-group-sm > .form-control,
+.input-group-sm > .form-select,
+.input-group-sm > .input-group-text,
+.input-group-sm > .btn {
+  padding: 0.25rem 0.5rem;
+  font-size: 0.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) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),
+.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 {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),
+.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 {
+  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(var(--bs-border-width) * -1);
+  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: 0.25rem;
+  font-size: 0.875em;
+  color: var(--bs-form-valid-color);
+}
+
+.valid-tooltip {
+  position: absolute;
+  top: 100%;
+  z-index: 5;
+  display: none;
+  max-width: 100%;
+  padding: 0.25rem 0.5rem;
+  margin-top: 0.1rem;
+  font-size: 0.875rem;
+  color: #fff;
+  background-color: var(--bs-success);
+  border-radius: var(--bs-border-radius);
+}
+
+.was-validated :valid ~ .valid-feedback,
+.was-validated :valid ~ .valid-tooltip,
+.is-valid ~ .valid-feedback,
+.is-valid ~ .valid-tooltip {
+  display: block;
+}
+
+.was-validated .form-control:valid, .form-control.is-valid {
+  border-color: var(--bs-form-valid-border-color);
+  padding-right: calc(1.5em + 0.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.1z'/%3e%3c/svg%3e");
+  background-repeat: no-repeat;
+  background-position: right calc(0.375em + 0.1875rem) center;
+  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+.was-validated .form-control:valid:focus, .form-control.is-valid:focus {
+  border-color: var(--bs-form-valid-border-color);
+  box-shadow: var(--bs-box-shadow-inset), 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);
+}
+
+.was-validated textarea.form-control:valid, textarea.form-control.is-valid {
+  padding-right: calc(1.5em + 0.75rem);
+  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
+}
+
+.was-validated .form-select:valid, .form-select.is-valid {
+  border-color: var(--bs-form-valid-border-color);
+}
+.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-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.1z'/%3e%3c/svg%3e");
+  padding-right: 4.125rem;
+  background-position: right 0.75rem center, center right 2.25rem;
+  background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+.was-validated .form-select:valid:focus, .form-select.is-valid:focus {
+  border-color: var(--bs-form-valid-border-color);
+  box-shadow: var(--bs-box-shadow-inset), 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);
+}
+
+.was-validated .form-control-color:valid, .form-control-color.is-valid {
+  width: calc(3rem + calc(1.5em + 0.75rem));
+}
+
+.was-validated .form-check-input:valid, .form-check-input.is-valid {
+  border-color: var(--bs-form-valid-border-color);
+}
+.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked {
+  background-color: var(--bs-form-valid-color);
+}
+.was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus {
+  box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);
+}
+.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {
+  color: var(--bs-form-valid-color);
+}
+
+.form-check-inline .form-check-input ~ .valid-feedback {
+  margin-left: 0.5em;
+}
+
+.was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid,
+.was-validated .input-group > .form-select:not(:focus):valid,
+.input-group > .form-select:not(:focus).is-valid,
+.was-validated .input-group > .form-floating:not(:focus-within):valid,
+.input-group > .form-floating:not(:focus-within).is-valid {
+  z-index: 3;
+}
+
+.invalid-feedback {
+  display: none;
+  width: 100%;
+  margin-top: 0.25rem;
+  font-size: 0.875em;
+  color: var(--bs-form-invalid-color);
+}
+
+.invalid-tooltip {
+  position: absolute;
+  top: 100%;
+  z-index: 5;
+  display: none;
+  max-width: 100%;
+  padding: 0.25rem 0.5rem;
+  margin-top: 0.1rem;
+  font-size: 0.875rem;
+  color: #fff;
+  background-color: var(--bs-danger);
+  border-radius: var(--bs-border-radius);
+}
+
+.was-validated :invalid ~ .invalid-feedback,
+.was-validated :invalid ~ .invalid-tooltip,
+.is-invalid ~ .invalid-feedback,
+.is-invalid ~ .invalid-tooltip {
+  display: block;
+}
+
+.was-validated .form-control:invalid, .form-control.is-invalid {
+  border-color: var(--bs-form-invalid-border-color);
+  padding-right: calc(1.5em + 0.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(0.375em + 0.1875rem) center;
+  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {
+  border-color: var(--bs-form-invalid-border-color);
+  box-shadow: var(--bs-box-shadow-inset), 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
+}
+
+.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {
+  padding-right: calc(1.5em + 0.75rem);
+  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
+}
+
+.was-validated .form-select:invalid, .form-select.is-invalid {
+  border-color: var(--bs-form-invalid-border-color);
+}
+.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-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 0.75rem center, center right 2.25rem;
+  background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus {
+  border-color: var(--bs-form-invalid-border-color);
+  box-shadow: var(--bs-box-shadow-inset), 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
+}
+
+.was-validated .form-control-color:invalid, .form-control-color.is-invalid {
+  width: calc(3rem + calc(1.5em + 0.75rem));
+}
+
+.was-validated .form-check-input:invalid, .form-check-input.is-invalid {
+  border-color: var(--bs-form-invalid-border-color);
+}
+.was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked {
+  background-color: var(--bs-form-invalid-color);
+}
+.was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus {
+  box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
+}
+.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {
+  color: var(--bs-form-invalid-color);
+}
+
+.form-check-inline .form-check-input ~ .invalid-feedback {
+  margin-left: 0.5em;
+}
+
+.was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid,
+.was-validated .input-group > .form-select:not(:focus):invalid,
+.input-group > .form-select:not(:focus).is-invalid,
+.was-validated .input-group > .form-floating:not(:focus-within):invalid,
+.input-group > .form-floating:not(:focus-within).is-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);
+  box-shadow: var(--bs-btn-box-shadow);
+  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;
+}
+@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-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-box-shadow), var(--bs-btn-focus-box-shadow);
+}
+.btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show {
+  color: var(--bs-btn-active-color);
+  background-color: var(--bs-btn-active-bg);
+  border-color: var(--bs-btn-active-border-color);
+  box-shadow: var(--bs-btn-active-shadow);
+}
+.btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible {
+  box-shadow: var(--bs-btn-active-shadow), var(--bs-btn-focus-box-shadow);
+}
+.btn-check:checked:focus-visible + .btn {
+  box-shadow: var(--bs-btn-active-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);
+  box-shadow: none;
+}
+
+.btn-primary {
+  --bs-btn-color: #fff;
+  --bs-btn-bg: #01445E;
+  --bs-btn-border-color: #01445E;
+  --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: #01445E;
+  --bs-btn-disabled-border-color: #01445E;
+}
+
+.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: #01445E;
+  --bs-btn-border-color: #01445E;
+  --bs-btn-hover-color: #fff;
+  --bs-btn-hover-bg: #01445E;
+  --bs-btn-hover-border-color: #01445E;
+  --bs-btn-focus-shadow-rgb: 13, 110, 253;
+  --bs-btn-active-color: #fff;
+  --bs-btn-active-bg: #01445E;
+  --bs-btn-active-border-color: #01445E;
+  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+  --bs-btn-disabled-color: #01445E;
+  --bs-btn-disabled-bg: transparent;
+  --bs-btn-disabled-border-color: #01445E;
+  --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-lg, .btn-group-lg > .btn {
+  --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-sm, .btn-group-sm > .btn {
+  --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 0.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 0.35s ease;
+}
+@media (prefers-reduced-motion: reduce) {
+  .collapsing {
+    transition: none;
+  }
+}
+.collapsing.collapse-horizontal {
+  width: 0;
+  height: auto;
+  transition: width 0.35s ease;
+}
+@media (prefers-reduced-motion: reduce) {
+  .collapsing.collapse-horizontal {
+    transition: none;
+  }
+}
+
+.dropup,
+.dropend,
+.dropdown,
+.dropstart,
+.dropup-center,
+.dropdown-center {
+  position: relative;
+}
+
+.dropdown-toggle {
+  white-space: nowrap;
+}
+.dropdown-toggle::after {
+  display: inline-block;
+  margin-left: 0.255em;
+  vertical-align: 0.255em;
+  content: "";
+  border-top: 0.3em solid;
+  border-right: 0.3em solid transparent;
+  border-bottom: 0;
+  border-left: 0.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: #01445E;
+  --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);
+  box-shadow: var(--bs-dropdown-box-shadow);
+}
+.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: 0.255em;
+  vertical-align: 0.255em;
+  content: "";
+  border-top: 0;
+  border-right: 0.3em solid transparent;
+  border-bottom: 0.3em solid;
+  border-left: 0.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: 0.255em;
+  vertical-align: 0.255em;
+  content: "";
+  border-top: 0.3em solid transparent;
+  border-right: 0;
+  border-bottom: 0.3em solid transparent;
+  border-left: 0.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: 0.255em;
+  vertical-align: 0.255em;
+  content: "";
+}
+.dropstart .dropdown-toggle::after {
+  display: none;
+}
+.dropstart .dropdown-toggle::before {
+  display: inline-block;
+  margin-right: 0.255em;
+  vertical-align: 0.255em;
+  content: "";
+  border-top: 0.3em solid transparent;
+  border-right: 0.3em solid;
+  border-bottom: 0.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:hover, .dropdown-item:focus {
+  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: 0.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: #01445E;
+  --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 > .btn,
+.btn-group-vertical > .btn {
+  position: relative;
+  flex: 1 1 auto;
+}
+.btn-group > .btn-check:checked + .btn,
+.btn-group > .btn-check:focus + .btn,
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn-check:checked + .btn,
+.btn-group-vertical > .btn-check:focus + .btn,
+.btn-group-vertical > .btn:hover,
+.btn-group-vertical > .btn:focus,
+.btn-group-vertical > .btn:active,
+.btn-group-vertical > .btn.active {
+  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 > :not(.btn-check:first-child) + .btn,
+.btn-group > .btn-group:not(:first-child) {
+  margin-left: calc(var(--bs-border-width) * -1);
+}
+.btn-group > .btn:not(:last-child):not(.dropdown-toggle),
+.btn-group > .btn.dropdown-toggle-split:first-child,
+.btn-group > .btn-group:not(:last-child) > .btn {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn:nth-child(n+3),
+.btn-group > :not(.btn-check) + .btn,
+.btn-group > .btn-group:not(:first-child) > .btn {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.dropdown-toggle-split {
+  padding-right: 0.5625rem;
+  padding-left: 0.5625rem;
+}
+.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after {
+  margin-left: 0;
+}
+.dropstart .dropdown-toggle-split::before {
+  margin-right: 0;
+}
+
+.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {
+  padding-right: 0.375rem;
+  padding-left: 0.375rem;
+}
+
+.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {
+  padding-right: 0.75rem;
+  padding-left: 0.75rem;
+}
+
+.btn-group.show .dropdown-toggle {
+  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+.btn-group.show .dropdown-toggle.btn-link {
+  box-shadow: none;
+}
+
+.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:not(:first-child),
+.btn-group-vertical > .btn-group:not(:first-child) {
+  margin-top: calc(var(--bs-border-width) * -1);
+}
+.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),
+.btn-group-vertical > .btn-group:not(:last-child) > .btn {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn ~ .btn,
+.btn-group-vertical > .btn-group:not(:first-child) > .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: none;
+  border: 0;
+  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .nav-link {
+    transition: none;
+  }
+}
+.nav-link:hover, .nav-link:focus {
+  color: var(--bs-nav-link-hover-color);
+}
+.nav-link:focus-visible {
+  outline: 0;
+  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.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:hover, .nav-tabs .nav-link:focus {
+  isolation: isolate;
+  border-color: var(--bs-nav-tabs-link-hover-border-color);
+}
+.nav-tabs .nav-link.active,
+.nav-tabs .nav-item.show .nav-link {
+  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: #01445E;
+}
+.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:hover, .nav-underline .nav-link:focus {
+  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-link,
+.nav-fill .nav-item {
+  flex: 1 1 auto;
+  text-align: center;
+}
+
+.nav-justified > .nav-link,
+.nav-justified .nav-item {
+  flex-basis: 0;
+  flex-grow: 1;
+  text-align: center;
+}
+
+.nav-fill .nav-item .nav-link,
+.nav-justified .nav-item .nav-link {
+  width: 100%;
+}
+
+.tab-content > .tab-pane {
+  display: none;
+}
+.tab-content > .active {
+  display: block;
+}
+
+.navbar {
+  --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: 1rem;
+  --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-sm,
+.navbar > .container-md,
+.navbar > .container-lg,
+.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:hover, .navbar-brand:focus {
+  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: 0.5rem;
+  padding-bottom: 0.5rem;
+  color: var(--bs-navbar-color);
+}
+.navbar-text a,
+.navbar-text a:hover,
+.navbar-text a:focus {
+  color: var(--bs-navbar-active-color);
+}
+
+.navbar-collapse {
+  flex-basis: 100%;
+  flex-grow: 1;
+  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;
+    box-shadow: none;
+    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;
+    box-shadow: none;
+    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;
+    box-shadow: none;
+    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;
+    box-shadow: none;
+    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;
+    box-shadow: none;
+    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;
+  box-shadow: none;
+  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);
+  box-shadow: var(--bs-card-box-shadow);
+}
+.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(-0.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) !important;
+  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(-0.5 * var(--bs-card-cap-padding-x));
+  margin-bottom: calc(-1 * var(--bs-card-cap-padding-y));
+  margin-left: calc(-0.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(-0.5 * var(--bs-card-cap-padding-x));
+  margin-left: calc(-0.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-top,
+.card-img-bottom {
+  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-img-top,
+  .card-group > .card:not(:last-child) .card-header {
+    border-top-right-radius: 0;
+  }
+  .card-group > .card:not(:last-child) .card-img-bottom,
+  .card-group > .card:not(:last-child) .card-footer {
+    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-img-top,
+  .card-group > .card:not(:first-child) .card-header {
+    border-top-left-radius: 0;
+  }
+  .card-group > .card:not(:first-child) .card-img-bottom,
+  .card-group > .card:not(:first-child) .card-footer {
+    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='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+  --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='%23052c65'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+  --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-header .accordion-button, .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed {
+  border-radius: 0;
+}
+.accordion-flush > .accordion-item > .accordion-collapse {
+  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-.708z'/%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-.708z'/%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, "/") /* rtl: 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: #01445E;
+  --bs-pagination-active-border-color: #01445E;
+  --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 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;
+}
+@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);
+}
+.page-link.active, .active > .page-link {
+  z-index: 3;
+  color: var(--bs-pagination-active-color);
+  background-color: var(--bs-pagination-active-bg);
+  border-color: var(--bs-pagination-active-border-color);
+}
+.page-link.disabled, .disabled > .page-link {
+  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(var(--bs-border-width) * -1);
+}
+.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: 1rem;
+  }
+}
+.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: #01445E;
+  --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);
+  box-shadow: var(--bs-progress-box-shadow);
+}
+
+.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, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.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: #01445E;
+  --bs-list-group-active-border-color: #01445E;
+  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-action {
+  width: 100%;
+  color: var(--bs-list-group-action-color);
+  text-align: inherit;
+}
+.list-group-item-action:hover, .list-group-item-action:focus {
+  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:active {
+  color: var(--bs-list-group-action-active-color);
+  background-color: var(--bs-list-group-action-active-bg);
+}
+
+.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-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.414z'/%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;
+  --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);
+  box-sizing: content-box;
+  width: 1em;
+  height: 1em;
+  padding: 0.25em 0.25em;
+  color: var(--bs-btn-close-color);
+  background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat;
+  border: 0;
+  border-radius: 0.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 {
+  filter: var(--bs-btn-close-white-filter);
+}
+
+[data-bs-theme=dark] .btn-close {
+  filter: var(--bs-btn-close-white-filter);
+}
+
+.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(-0.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: ;
+  --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 {
+  transition: transform 0.3s ease-out;
+  transform: translate(0, -50px);
+}
+@media (prefers-reduced-motion: reduce) {
+  .modal.fade .modal-dialog {
+    transition: none;
+  }
+}
+.modal.show .modal-dialog {
+  transform: none;
+}
+.modal.modal-static .modal-dialog {
+  transform: scale(1.02);
+}
+
+.modal-dialog-scrollable {
+  height: calc(100% - 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);
+  box-shadow: var(--bs-modal-box-shadow);
+  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) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5);
+  margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) 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) * 0.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) * 0.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-header,
+.modal-fullscreen .modal-footer {
+  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-header,
+  .modal-fullscreen-sm-down .modal-footer {
+    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-header,
+  .modal-fullscreen-md-down .modal-footer {
+    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-header,
+  .modal-fullscreen-lg-down .modal-footer {
+    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-header,
+  .modal-fullscreen-xl-down .modal-footer {
+    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-header,
+  .modal-fullscreen-xxl-down .modal-footer {
+    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-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {
+  bottom: calc(-1 * var(--bs-tooltip-arrow-height));
+}
+.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {
+  top: -1px;
+  border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;
+  border-top-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {
+  left: calc(-1 * var(--bs-tooltip-arrow-height));
+  width: var(--bs-tooltip-arrow-height);
+  height: var(--bs-tooltip-arrow-width);
+}
+.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {
+  right: -1px;
+  border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;
+  border-right-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:end:ignore */
+.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {
+  top: calc(-1 * var(--bs-tooltip-arrow-height));
+}
+.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {
+  bottom: -1px;
+  border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);
+  border-bottom-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {
+  right: calc(-1 * var(--bs-tooltip-arrow-height));
+  width: var(--bs-tooltip-arrow-height);
+  height: var(--bs-tooltip-arrow-width);
+}
+.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {
+  left: -1px;
+  border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);
+  border-left-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:end:ignore */
+.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);
+  box-shadow: var(--bs-popover-box-shadow);
+}
+.popover .popover-arrow {
+  display: block;
+  width: var(--bs-popover-arrow-width);
+  height: var(--bs-popover-arrow-height);
+}
+.popover .popover-arrow::before, .popover .popover-arrow::after {
+  position: absolute;
+  display: block;
+  content: "";
+  border-color: transparent;
+  border-style: solid;
+  border-width: 0;
+}
+
+.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow {
+  bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));
+}
+.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {
+  border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;
+}
+.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before {
+  bottom: 0;
+  border-top-color: var(--bs-popover-arrow-border);
+}
+.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {
+  bottom: var(--bs-popover-border-width);
+  border-top-color: var(--bs-popover-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .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-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {
+  border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;
+}
+.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before {
+  left: 0;
+  border-right-color: var(--bs-popover-arrow-border);
+}
+.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {
+  left: var(--bs-popover-border-width);
+  border-right-color: var(--bs-popover-bg);
+}
+
+/* rtl:end:ignore */
+.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow {
+  top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));
+}
+.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {
+  border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);
+}
+.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before {
+  top: 0;
+  border-bottom-color: var(--bs-popover-arrow-border);
+}
+.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {
+  top: var(--bs-popover-border-width);
+  border-bottom-color: var(--bs-popover-bg);
+}
+.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before {
+  position: absolute;
+  top: 0;
+  left: 50%;
+  display: block;
+  width: var(--bs-popover-arrow-width);
+  margin-left: calc(-0.5 * var(--bs-popover-arrow-width));
+  content: "";
+  border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .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-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {
+  border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);
+}
+.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before {
+  right: 0;
+  border-left-color: var(--bs-popover-arrow-border);
+}
+.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {
+  right: var(--bs-popover-border-width);
+  border-left-color: var(--bs-popover-bg);
+}
+
+/* rtl:end:ignore */
+.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 0.6s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .carousel-item {
+    transition: none;
+  }
+}
+
+.carousel-item.active,
+.carousel-item-next,
+.carousel-item-prev {
+  display: block;
+}
+
+.carousel-item-next:not(.carousel-item-start),
+.active.carousel-item-end {
+  transform: translateX(100%);
+}
+
+.carousel-item-prev:not(.carousel-item-end),
+.active.carousel-item-start {
+  transform: translateX(-100%);
+}
+
+.carousel-fade .carousel-item {
+  opacity: 0;
+  transition-property: opacity;
+  transform: none;
+}
+.carousel-fade .carousel-item.active,
+.carousel-fade .carousel-item-next.carousel-item-start,
+.carousel-fade .carousel-item-prev.carousel-item-end {
+  z-index: 1;
+  opacity: 1;
+}
+.carousel-fade .active.carousel-item-start,
+.carousel-fade .active.carousel-item-end {
+  z-index: 0;
+  opacity: 0;
+  transition: opacity 0s 0.6s;
+}
+@media (prefers-reduced-motion: reduce) {
+  .carousel-fade .active.carousel-item-start,
+  .carousel-fade .active.carousel-item-end {
+    transition: none;
+  }
+}
+
+.carousel-control-prev,
+.carousel-control-next {
+  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: none;
+  border: 0;
+  opacity: 0.5;
+  transition: opacity 0.15s ease;
+}
+@media (prefers-reduced-motion: reduce) {
+  .carousel-control-prev,
+  .carousel-control-next {
+    transition: none;
+  }
+}
+.carousel-control-prev:hover, .carousel-control-prev:focus,
+.carousel-control-next:hover,
+.carousel-control-next:focus {
+  color: #fff;
+  text-decoration: none;
+  outline: 0;
+  opacity: 0.9;
+}
+
+.carousel-control-prev {
+  left: 0;
+}
+
+.carousel-control-next {
+  right: 0;
+}
+
+.carousel-control-prev-icon,
+.carousel-control-next-icon {
+  display: inline-block;
+  width: 2rem;
+  height: 2rem;
+  background-repeat: no-repeat;
+  background-position: 50%;
+  background-size: 100% 100%;
+}
+
+.carousel-control-prev-icon {
+  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")*/;
+}
+
+.carousel-control-next-icon {
+  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")*/;
+}
+
+.carousel-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: #fff;
+  background-clip: padding-box;
+  border: 0;
+  border-top: 10px solid transparent;
+  border-bottom: 10px solid transparent;
+  opacity: 0.5;
+  transition: opacity 0.6s ease;
+}
+@media (prefers-reduced-motion: reduce) {
+  .carousel-indicators [data-bs-target] {
+    transition: none;
+  }
+}
+.carousel-indicators .active {
+  opacity: 1;
+}
+
+.carousel-caption {
+  position: absolute;
+  right: 15%;
+  bottom: 1.25rem;
+  left: 15%;
+  padding-top: 1.25rem;
+  padding-bottom: 1.25rem;
+  color: #fff;
+  text-align: center;
+}
+
+.carousel-dark .carousel-control-prev-icon,
+.carousel-dark .carousel-control-next-icon {
+  filter: invert(1) grayscale(100);
+}
+.carousel-dark .carousel-indicators [data-bs-target] {
+  background-color: #000;
+}
+.carousel-dark .carousel-caption {
+  color: #000;
+}
+
+[data-bs-theme=dark] .carousel .carousel-control-prev-icon,
+[data-bs-theme=dark] .carousel .carousel-control-next-icon, [data-bs-theme=dark].carousel .carousel-control-prev-icon,
+[data-bs-theme=dark].carousel .carousel-control-next-icon {
+  filter: invert(1) grayscale(100);
+}
+[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target], [data-bs-theme=dark].carousel .carousel-indicators [data-bs-target] {
+  background-color: #000;
+}
+[data-bs-theme=dark] .carousel .carousel-caption, [data-bs-theme=dark].carousel .carousel-caption {
+  color: #000;
+}
+
+.spinner-grow,
+.spinner-border {
+  display: inline-block;
+  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) /* rtl:ignore */;
+  }
+}
+.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-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm {
+  --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;
+    box-shadow: var(--bs-offcanvas-box-shadow);
+    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.showing, .offcanvas-sm.show:not(.hiding) {
+    transform: none;
+  }
+  .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show {
+    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;
+    box-shadow: var(--bs-offcanvas-box-shadow);
+    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.showing, .offcanvas-md.show:not(.hiding) {
+    transform: none;
+  }
+  .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show {
+    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;
+    box-shadow: var(--bs-offcanvas-box-shadow);
+    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.showing, .offcanvas-lg.show:not(.hiding) {
+    transform: none;
+  }
+  .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show {
+    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;
+    box-shadow: var(--bs-offcanvas-box-shadow);
+    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.showing, .offcanvas-xl.show:not(.hiding) {
+    transform: none;
+  }
+  .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show {
+    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;
+    box-shadow: var(--bs-offcanvas-box-shadow);
+    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.showing, .offcanvas-xxl.show:not(.hiding) {
+    transform: none;
+  }
+  .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show {
+    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;
+  box-shadow: var(--bs-offcanvas-box-shadow);
+  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.showing, .offcanvas.show:not(.hiding) {
+  transform: none;
+}
+.offcanvas.showing, .offcanvas.hiding, .offcanvas.show {
+  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: 0.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) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5);
+  margin: calc(-0.5 * var(--bs-offcanvas-padding-y)) calc(-0.5 * var(--bs-offcanvas-padding-x)) calc(-0.5 * var(--bs-offcanvas-padding-y)) 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: 0.5;
+}
+.placeholder.btn::before {
+  display: inline-block;
+  content: "";
+}
+
+.placeholder-xs {
+  min-height: 0.6em;
+}
+
+.placeholder-sm {
+  min-height: 0.8em;
+}
+
+.placeholder-lg {
+  min-height: 1.2em;
+}
+
+.placeholder-glow .placeholder {
+  animation: placeholder-glow 2s ease-in-out infinite;
+}
+
+@keyframes placeholder-glow {
+  50% {
+    opacity: 0.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:hover, .link-primary:focus {
+  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:hover, .link-secondary:focus {
+  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:hover, .link-success:focus {
+  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:hover, .link-info:focus {
+  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:hover, .link-warning:focus {
+  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:hover, .link-danger:focus {
+  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:hover, .link-light:focus {
+  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:hover, .link-dark:focus {
+  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:hover, .link-body-emphasis:focus {
+  color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.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: 0.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: 0.2s ease-in-out transform;
+}
+@media (prefers-reduced-motion: reduce) {
+  .icon-link > .bi {
+    transition: none;
+  }
+}
+
+.icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi {
+  transform: var(--bs-icon-link-transform, translate3d(0.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:not(caption),
+.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) {
+  position: absolute !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: 0.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: 0.25 !important;
+}
+
+.opacity-50 {
+  opacity: 0.5 !important;
+}
+
+.opacity-75 {
+  opacity: 0.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: 0.25rem !important;
+}
+
+.m-2 {
+  margin: 0.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: 0.25rem !important;
+  margin-left: 0.25rem !important;
+}
+
+.mx-2 {
+  margin-right: 0.5rem !important;
+  margin-left: 0.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: 0.25rem !important;
+  margin-bottom: 0.25rem !important;
+}
+
+.my-2 {
+  margin-top: 0.5rem !important;
+  margin-bottom: 0.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: 0.25rem !important;
+}
+
+.mt-2 {
+  margin-top: 0.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: 0.25rem !important;
+}
+
+.me-2 {
+  margin-right: 0.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: 0.25rem !important;
+}
+
+.mb-2 {
+  margin-bottom: 0.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: 0.25rem !important;
+}
+
+.ms-2 {
+  margin-left: 0.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;
+}
+
+.m-n1 {
+  margin: -0.25rem !important;
+}
+
+.m-n2 {
+  margin: -0.5rem !important;
+}
+
+.m-n3 {
+  margin: -1rem !important;
+}
+
+.m-n4 {
+  margin: -1.5rem !important;
+}
+
+.m-n5 {
+  margin: -3rem !important;
+}
+
+.mx-n1 {
+  margin-right: -0.25rem !important;
+  margin-left: -0.25rem !important;
+}
+
+.mx-n2 {
+  margin-right: -0.5rem !important;
+  margin-left: -0.5rem !important;
+}
+
+.mx-n3 {
+  margin-right: -1rem !important;
+  margin-left: -1rem !important;
+}
+
+.mx-n4 {
+  margin-right: -1.5rem !important;
+  margin-left: -1.5rem !important;
+}
+
+.mx-n5 {
+  margin-right: -3rem !important;
+  margin-left: -3rem !important;
+}
+
+.my-n1 {
+  margin-top: -0.25rem !important;
+  margin-bottom: -0.25rem !important;
+}
+
+.my-n2 {
+  margin-top: -0.5rem !important;
+  margin-bottom: -0.5rem !important;
+}
+
+.my-n3 {
+  margin-top: -1rem !important;
+  margin-bottom: -1rem !important;
+}
+
+.my-n4 {
+  margin-top: -1.5rem !important;
+  margin-bottom: -1.5rem !important;
+}
+
+.my-n5 {
+  margin-top: -3rem !important;
+  margin-bottom: -3rem !important;
+}
+
+.mt-n1 {
+  margin-top: -0.25rem !important;
+}
+
+.mt-n2 {
+  margin-top: -0.5rem !important;
+}
+
+.mt-n3 {
+  margin-top: -1rem !important;
+}
+
+.mt-n4 {
+  margin-top: -1.5rem !important;
+}
+
+.mt-n5 {
+  margin-top: -3rem !important;
+}
+
+.me-n1 {
+  margin-right: -0.25rem !important;
+}
+
+.me-n2 {
+  margin-right: -0.5rem !important;
+}
+
+.me-n3 {
+  margin-right: -1rem !important;
+}
+
+.me-n4 {
+  margin-right: -1.5rem !important;
+}
+
+.me-n5 {
+  margin-right: -3rem !important;
+}
+
+.mb-n1 {
+  margin-bottom: -0.25rem !important;
+}
+
+.mb-n2 {
+  margin-bottom: -0.5rem !important;
+}
+
+.mb-n3 {
+  margin-bottom: -1rem !important;
+}
+
+.mb-n4 {
+  margin-bottom: -1.5rem !important;
+}
+
+.mb-n5 {
+  margin-bottom: -3rem !important;
+}
+
+.ms-n1 {
+  margin-left: -0.25rem !important;
+}
+
+.ms-n2 {
+  margin-left: -0.5rem !important;
+}
+
+.ms-n3 {
+  margin-left: -1rem !important;
+}
+
+.ms-n4 {
+  margin-left: -1.5rem !important;
+}
+
+.ms-n5 {
+  margin-left: -3rem !important;
+}
+
+.p-0 {
+  padding: 0 !important;
+}
+
+.p-1 {
+  padding: 0.25rem !important;
+}
+
+.p-2 {
+  padding: 0.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: 0.25rem !important;
+  padding-left: 0.25rem !important;
+}
+
+.px-2 {
+  padding-right: 0.5rem !important;
+  padding-left: 0.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: 0.25rem !important;
+  padding-bottom: 0.25rem !important;
+}
+
+.py-2 {
+  padding-top: 0.5rem !important;
+  padding-bottom: 0.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: 0.25rem !important;
+}
+
+.pt-2 {
+  padding-top: 0.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: 0.25rem !important;
+}
+
+.pe-2 {
+  padding-right: 0.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: 0.25rem !important;
+}
+
+.pb-2 {
+  padding-bottom: 0.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: 0.25rem !important;
+}
+
+.ps-2 {
+  padding-left: 0.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: 0.25rem !important;
+}
+
+.gap-2 {
+  gap: 0.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: 0.25rem !important;
+}
+
+.row-gap-2 {
+  row-gap: 0.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: 0.25rem !important;
+}
+
+.column-gap-2 {
+  -moz-column-gap: 0.5rem !important;
+  column-gap: 0.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 + 0.9vw) !important;
+}
+
+.fs-3 {
+  font-size: calc(1.3rem + 0.6vw) !important;
+}
+
+.fs-4 {
+  font-size: calc(1.275rem + 0.3vw) !important;
+}
+
+.fs-5 {
+  font-size: 1.25rem !important;
+}
+
+.fs-6 {
+  font-size: 1rem !important;
+}
+
+.fs-7 {
+  font-size: 0.875rem !important;
+}
+
+.fs-8 {
+  font-size: 0.75rem !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;
+}
+
+/* rtl:begin:remove */
+.text-break {
+  word-wrap: break-word !important;
+  word-break: break-word !important;
+}
+
+/* rtl:end:remove */
+.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, 0.5) !important;
+}
+
+.text-white-50 {
+  --bs-text-opacity: 1;
+  color: rgba(255, 255, 255, 0.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: 0.25rem !important;
+  }
+  .m-sm-2 {
+    margin: 0.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: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .mx-sm-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.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: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .my-sm-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .mt-sm-2 {
+    margin-top: 0.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: 0.25rem !important;
+  }
+  .me-sm-2 {
+    margin-right: 0.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: 0.25rem !important;
+  }
+  .mb-sm-2 {
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .ms-sm-2 {
+    margin-left: 0.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;
+  }
+  .m-sm-n1 {
+    margin: -0.25rem !important;
+  }
+  .m-sm-n2 {
+    margin: -0.5rem !important;
+  }
+  .m-sm-n3 {
+    margin: -1rem !important;
+  }
+  .m-sm-n4 {
+    margin: -1.5rem !important;
+  }
+  .m-sm-n5 {
+    margin: -3rem !important;
+  }
+  .mx-sm-n1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+  .mx-sm-n2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+  .mx-sm-n3 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+  .mx-sm-n4 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+  .mx-sm-n5 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+  .my-sm-n1 {
+    margin-top: -0.25rem !important;
+    margin-bottom: -0.25rem !important;
+  }
+  .my-sm-n2 {
+    margin-top: -0.5rem !important;
+    margin-bottom: -0.5rem !important;
+  }
+  .my-sm-n3 {
+    margin-top: -1rem !important;
+    margin-bottom: -1rem !important;
+  }
+  .my-sm-n4 {
+    margin-top: -1.5rem !important;
+    margin-bottom: -1.5rem !important;
+  }
+  .my-sm-n5 {
+    margin-top: -3rem !important;
+    margin-bottom: -3rem !important;
+  }
+  .mt-sm-n1 {
+    margin-top: -0.25rem !important;
+  }
+  .mt-sm-n2 {
+    margin-top: -0.5rem !important;
+  }
+  .mt-sm-n3 {
+    margin-top: -1rem !important;
+  }
+  .mt-sm-n4 {
+    margin-top: -1.5rem !important;
+  }
+  .mt-sm-n5 {
+    margin-top: -3rem !important;
+  }
+  .me-sm-n1 {
+    margin-right: -0.25rem !important;
+  }
+  .me-sm-n2 {
+    margin-right: -0.5rem !important;
+  }
+  .me-sm-n3 {
+    margin-right: -1rem !important;
+  }
+  .me-sm-n4 {
+    margin-right: -1.5rem !important;
+  }
+  .me-sm-n5 {
+    margin-right: -3rem !important;
+  }
+  .mb-sm-n1 {
+    margin-bottom: -0.25rem !important;
+  }
+  .mb-sm-n2 {
+    margin-bottom: -0.5rem !important;
+  }
+  .mb-sm-n3 {
+    margin-bottom: -1rem !important;
+  }
+  .mb-sm-n4 {
+    margin-bottom: -1.5rem !important;
+  }
+  .mb-sm-n5 {
+    margin-bottom: -3rem !important;
+  }
+  .ms-sm-n1 {
+    margin-left: -0.25rem !important;
+  }
+  .ms-sm-n2 {
+    margin-left: -0.5rem !important;
+  }
+  .ms-sm-n3 {
+    margin-left: -1rem !important;
+  }
+  .ms-sm-n4 {
+    margin-left: -1.5rem !important;
+  }
+  .ms-sm-n5 {
+    margin-left: -3rem !important;
+  }
+  .p-sm-0 {
+    padding: 0 !important;
+  }
+  .p-sm-1 {
+    padding: 0.25rem !important;
+  }
+  .p-sm-2 {
+    padding: 0.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: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .px-sm-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.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: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .py-sm-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .pt-sm-2 {
+    padding-top: 0.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: 0.25rem !important;
+  }
+  .pe-sm-2 {
+    padding-right: 0.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: 0.25rem !important;
+  }
+  .pb-sm-2 {
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .ps-sm-2 {
+    padding-left: 0.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: 0.25rem !important;
+  }
+  .gap-sm-2 {
+    gap: 0.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: 0.25rem !important;
+  }
+  .row-gap-sm-2 {
+    row-gap: 0.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: 0.25rem !important;
+  }
+  .column-gap-sm-2 {
+    -moz-column-gap: 0.5rem !important;
+    column-gap: 0.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: 0.25rem !important;
+  }
+  .m-md-2 {
+    margin: 0.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: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .mx-md-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.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: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .my-md-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .mt-md-2 {
+    margin-top: 0.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: 0.25rem !important;
+  }
+  .me-md-2 {
+    margin-right: 0.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: 0.25rem !important;
+  }
+  .mb-md-2 {
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .ms-md-2 {
+    margin-left: 0.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;
+  }
+  .m-md-n1 {
+    margin: -0.25rem !important;
+  }
+  .m-md-n2 {
+    margin: -0.5rem !important;
+  }
+  .m-md-n3 {
+    margin: -1rem !important;
+  }
+  .m-md-n4 {
+    margin: -1.5rem !important;
+  }
+  .m-md-n5 {
+    margin: -3rem !important;
+  }
+  .mx-md-n1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+  .mx-md-n2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+  .mx-md-n3 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+  .mx-md-n4 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+  .mx-md-n5 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+  .my-md-n1 {
+    margin-top: -0.25rem !important;
+    margin-bottom: -0.25rem !important;
+  }
+  .my-md-n2 {
+    margin-top: -0.5rem !important;
+    margin-bottom: -0.5rem !important;
+  }
+  .my-md-n3 {
+    margin-top: -1rem !important;
+    margin-bottom: -1rem !important;
+  }
+  .my-md-n4 {
+    margin-top: -1.5rem !important;
+    margin-bottom: -1.5rem !important;
+  }
+  .my-md-n5 {
+    margin-top: -3rem !important;
+    margin-bottom: -3rem !important;
+  }
+  .mt-md-n1 {
+    margin-top: -0.25rem !important;
+  }
+  .mt-md-n2 {
+    margin-top: -0.5rem !important;
+  }
+  .mt-md-n3 {
+    margin-top: -1rem !important;
+  }
+  .mt-md-n4 {
+    margin-top: -1.5rem !important;
+  }
+  .mt-md-n5 {
+    margin-top: -3rem !important;
+  }
+  .me-md-n1 {
+    margin-right: -0.25rem !important;
+  }
+  .me-md-n2 {
+    margin-right: -0.5rem !important;
+  }
+  .me-md-n3 {
+    margin-right: -1rem !important;
+  }
+  .me-md-n4 {
+    margin-right: -1.5rem !important;
+  }
+  .me-md-n5 {
+    margin-right: -3rem !important;
+  }
+  .mb-md-n1 {
+    margin-bottom: -0.25rem !important;
+  }
+  .mb-md-n2 {
+    margin-bottom: -0.5rem !important;
+  }
+  .mb-md-n3 {
+    margin-bottom: -1rem !important;
+  }
+  .mb-md-n4 {
+    margin-bottom: -1.5rem !important;
+  }
+  .mb-md-n5 {
+    margin-bottom: -3rem !important;
+  }
+  .ms-md-n1 {
+    margin-left: -0.25rem !important;
+  }
+  .ms-md-n2 {
+    margin-left: -0.5rem !important;
+  }
+  .ms-md-n3 {
+    margin-left: -1rem !important;
+  }
+  .ms-md-n4 {
+    margin-left: -1.5rem !important;
+  }
+  .ms-md-n5 {
+    margin-left: -3rem !important;
+  }
+  .p-md-0 {
+    padding: 0 !important;
+  }
+  .p-md-1 {
+    padding: 0.25rem !important;
+  }
+  .p-md-2 {
+    padding: 0.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: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .px-md-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.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: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .py-md-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .pt-md-2 {
+    padding-top: 0.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: 0.25rem !important;
+  }
+  .pe-md-2 {
+    padding-right: 0.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: 0.25rem !important;
+  }
+  .pb-md-2 {
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .ps-md-2 {
+    padding-left: 0.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: 0.25rem !important;
+  }
+  .gap-md-2 {
+    gap: 0.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: 0.25rem !important;
+  }
+  .row-gap-md-2 {
+    row-gap: 0.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: 0.25rem !important;
+  }
+  .column-gap-md-2 {
+    -moz-column-gap: 0.5rem !important;
+    column-gap: 0.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: 0.25rem !important;
+  }
+  .m-lg-2 {
+    margin: 0.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: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .mx-lg-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.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: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .my-lg-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .mt-lg-2 {
+    margin-top: 0.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: 0.25rem !important;
+  }
+  .me-lg-2 {
+    margin-right: 0.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: 0.25rem !important;
+  }
+  .mb-lg-2 {
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .ms-lg-2 {
+    margin-left: 0.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;
+  }
+  .m-lg-n1 {
+    margin: -0.25rem !important;
+  }
+  .m-lg-n2 {
+    margin: -0.5rem !important;
+  }
+  .m-lg-n3 {
+    margin: -1rem !important;
+  }
+  .m-lg-n4 {
+    margin: -1.5rem !important;
+  }
+  .m-lg-n5 {
+    margin: -3rem !important;
+  }
+  .mx-lg-n1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+  .mx-lg-n2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+  .mx-lg-n3 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+  .mx-lg-n4 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+  .mx-lg-n5 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+  .my-lg-n1 {
+    margin-top: -0.25rem !important;
+    margin-bottom: -0.25rem !important;
+  }
+  .my-lg-n2 {
+    margin-top: -0.5rem !important;
+    margin-bottom: -0.5rem !important;
+  }
+  .my-lg-n3 {
+    margin-top: -1rem !important;
+    margin-bottom: -1rem !important;
+  }
+  .my-lg-n4 {
+    margin-top: -1.5rem !important;
+    margin-bottom: -1.5rem !important;
+  }
+  .my-lg-n5 {
+    margin-top: -3rem !important;
+    margin-bottom: -3rem !important;
+  }
+  .mt-lg-n1 {
+    margin-top: -0.25rem !important;
+  }
+  .mt-lg-n2 {
+    margin-top: -0.5rem !important;
+  }
+  .mt-lg-n3 {
+    margin-top: -1rem !important;
+  }
+  .mt-lg-n4 {
+    margin-top: -1.5rem !important;
+  }
+  .mt-lg-n5 {
+    margin-top: -3rem !important;
+  }
+  .me-lg-n1 {
+    margin-right: -0.25rem !important;
+  }
+  .me-lg-n2 {
+    margin-right: -0.5rem !important;
+  }
+  .me-lg-n3 {
+    margin-right: -1rem !important;
+  }
+  .me-lg-n4 {
+    margin-right: -1.5rem !important;
+  }
+  .me-lg-n5 {
+    margin-right: -3rem !important;
+  }
+  .mb-lg-n1 {
+    margin-bottom: -0.25rem !important;
+  }
+  .mb-lg-n2 {
+    margin-bottom: -0.5rem !important;
+  }
+  .mb-lg-n3 {
+    margin-bottom: -1rem !important;
+  }
+  .mb-lg-n4 {
+    margin-bottom: -1.5rem !important;
+  }
+  .mb-lg-n5 {
+    margin-bottom: -3rem !important;
+  }
+  .ms-lg-n1 {
+    margin-left: -0.25rem !important;
+  }
+  .ms-lg-n2 {
+    margin-left: -0.5rem !important;
+  }
+  .ms-lg-n3 {
+    margin-left: -1rem !important;
+  }
+  .ms-lg-n4 {
+    margin-left: -1.5rem !important;
+  }
+  .ms-lg-n5 {
+    margin-left: -3rem !important;
+  }
+  .p-lg-0 {
+    padding: 0 !important;
+  }
+  .p-lg-1 {
+    padding: 0.25rem !important;
+  }
+  .p-lg-2 {
+    padding: 0.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: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .px-lg-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.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: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .py-lg-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .pt-lg-2 {
+    padding-top: 0.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: 0.25rem !important;
+  }
+  .pe-lg-2 {
+    padding-right: 0.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: 0.25rem !important;
+  }
+  .pb-lg-2 {
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .ps-lg-2 {
+    padding-left: 0.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: 0.25rem !important;
+  }
+  .gap-lg-2 {
+    gap: 0.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: 0.25rem !important;
+  }
+  .row-gap-lg-2 {
+    row-gap: 0.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: 0.25rem !important;
+  }
+  .column-gap-lg-2 {
+    -moz-column-gap: 0.5rem !important;
+    column-gap: 0.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: 0.25rem !important;
+  }
+  .m-xl-2 {
+    margin: 0.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: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .mx-xl-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.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: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .my-xl-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .mt-xl-2 {
+    margin-top: 0.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: 0.25rem !important;
+  }
+  .me-xl-2 {
+    margin-right: 0.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: 0.25rem !important;
+  }
+  .mb-xl-2 {
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .ms-xl-2 {
+    margin-left: 0.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;
+  }
+  .m-xl-n1 {
+    margin: -0.25rem !important;
+  }
+  .m-xl-n2 {
+    margin: -0.5rem !important;
+  }
+  .m-xl-n3 {
+    margin: -1rem !important;
+  }
+  .m-xl-n4 {
+    margin: -1.5rem !important;
+  }
+  .m-xl-n5 {
+    margin: -3rem !important;
+  }
+  .mx-xl-n1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+  .mx-xl-n2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+  .mx-xl-n3 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+  .mx-xl-n4 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+  .mx-xl-n5 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+  .my-xl-n1 {
+    margin-top: -0.25rem !important;
+    margin-bottom: -0.25rem !important;
+  }
+  .my-xl-n2 {
+    margin-top: -0.5rem !important;
+    margin-bottom: -0.5rem !important;
+  }
+  .my-xl-n3 {
+    margin-top: -1rem !important;
+    margin-bottom: -1rem !important;
+  }
+  .my-xl-n4 {
+    margin-top: -1.5rem !important;
+    margin-bottom: -1.5rem !important;
+  }
+  .my-xl-n5 {
+    margin-top: -3rem !important;
+    margin-bottom: -3rem !important;
+  }
+  .mt-xl-n1 {
+    margin-top: -0.25rem !important;
+  }
+  .mt-xl-n2 {
+    margin-top: -0.5rem !important;
+  }
+  .mt-xl-n3 {
+    margin-top: -1rem !important;
+  }
+  .mt-xl-n4 {
+    margin-top: -1.5rem !important;
+  }
+  .mt-xl-n5 {
+    margin-top: -3rem !important;
+  }
+  .me-xl-n1 {
+    margin-right: -0.25rem !important;
+  }
+  .me-xl-n2 {
+    margin-right: -0.5rem !important;
+  }
+  .me-xl-n3 {
+    margin-right: -1rem !important;
+  }
+  .me-xl-n4 {
+    margin-right: -1.5rem !important;
+  }
+  .me-xl-n5 {
+    margin-right: -3rem !important;
+  }
+  .mb-xl-n1 {
+    margin-bottom: -0.25rem !important;
+  }
+  .mb-xl-n2 {
+    margin-bottom: -0.5rem !important;
+  }
+  .mb-xl-n3 {
+    margin-bottom: -1rem !important;
+  }
+  .mb-xl-n4 {
+    margin-bottom: -1.5rem !important;
+  }
+  .mb-xl-n5 {
+    margin-bottom: -3rem !important;
+  }
+  .ms-xl-n1 {
+    margin-left: -0.25rem !important;
+  }
+  .ms-xl-n2 {
+    margin-left: -0.5rem !important;
+  }
+  .ms-xl-n3 {
+    margin-left: -1rem !important;
+  }
+  .ms-xl-n4 {
+    margin-left: -1.5rem !important;
+  }
+  .ms-xl-n5 {
+    margin-left: -3rem !important;
+  }
+  .p-xl-0 {
+    padding: 0 !important;
+  }
+  .p-xl-1 {
+    padding: 0.25rem !important;
+  }
+  .p-xl-2 {
+    padding: 0.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: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .px-xl-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.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: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .py-xl-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .pt-xl-2 {
+    padding-top: 0.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: 0.25rem !important;
+  }
+  .pe-xl-2 {
+    padding-right: 0.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: 0.25rem !important;
+  }
+  .pb-xl-2 {
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .ps-xl-2 {
+    padding-left: 0.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: 0.25rem !important;
+  }
+  .gap-xl-2 {
+    gap: 0.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: 0.25rem !important;
+  }
+  .row-gap-xl-2 {
+    row-gap: 0.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: 0.25rem !important;
+  }
+  .column-gap-xl-2 {
+    -moz-column-gap: 0.5rem !important;
+    column-gap: 0.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: 0.25rem !important;
+  }
+  .m-xxl-2 {
+    margin: 0.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: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .mx-xxl-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.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: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .my-xxl-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .mt-xxl-2 {
+    margin-top: 0.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: 0.25rem !important;
+  }
+  .me-xxl-2 {
+    margin-right: 0.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: 0.25rem !important;
+  }
+  .mb-xxl-2 {
+    margin-bottom: 0.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: 0.25rem !important;
+  }
+  .ms-xxl-2 {
+    margin-left: 0.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;
+  }
+  .m-xxl-n1 {
+    margin: -0.25rem !important;
+  }
+  .m-xxl-n2 {
+    margin: -0.5rem !important;
+  }
+  .m-xxl-n3 {
+    margin: -1rem !important;
+  }
+  .m-xxl-n4 {
+    margin: -1.5rem !important;
+  }
+  .m-xxl-n5 {
+    margin: -3rem !important;
+  }
+  .mx-xxl-n1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+  .mx-xxl-n2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+  .mx-xxl-n3 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+  .mx-xxl-n4 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+  .mx-xxl-n5 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+  .my-xxl-n1 {
+    margin-top: -0.25rem !important;
+    margin-bottom: -0.25rem !important;
+  }
+  .my-xxl-n2 {
+    margin-top: -0.5rem !important;
+    margin-bottom: -0.5rem !important;
+  }
+  .my-xxl-n3 {
+    margin-top: -1rem !important;
+    margin-bottom: -1rem !important;
+  }
+  .my-xxl-n4 {
+    margin-top: -1.5rem !important;
+    margin-bottom: -1.5rem !important;
+  }
+  .my-xxl-n5 {
+    margin-top: -3rem !important;
+    margin-bottom: -3rem !important;
+  }
+  .mt-xxl-n1 {
+    margin-top: -0.25rem !important;
+  }
+  .mt-xxl-n2 {
+    margin-top: -0.5rem !important;
+  }
+  .mt-xxl-n3 {
+    margin-top: -1rem !important;
+  }
+  .mt-xxl-n4 {
+    margin-top: -1.5rem !important;
+  }
+  .mt-xxl-n5 {
+    margin-top: -3rem !important;
+  }
+  .me-xxl-n1 {
+    margin-right: -0.25rem !important;
+  }
+  .me-xxl-n2 {
+    margin-right: -0.5rem !important;
+  }
+  .me-xxl-n3 {
+    margin-right: -1rem !important;
+  }
+  .me-xxl-n4 {
+    margin-right: -1.5rem !important;
+  }
+  .me-xxl-n5 {
+    margin-right: -3rem !important;
+  }
+  .mb-xxl-n1 {
+    margin-bottom: -0.25rem !important;
+  }
+  .mb-xxl-n2 {
+    margin-bottom: -0.5rem !important;
+  }
+  .mb-xxl-n3 {
+    margin-bottom: -1rem !important;
+  }
+  .mb-xxl-n4 {
+    margin-bottom: -1.5rem !important;
+  }
+  .mb-xxl-n5 {
+    margin-bottom: -3rem !important;
+  }
+  .ms-xxl-n1 {
+    margin-left: -0.25rem !important;
+  }
+  .ms-xxl-n2 {
+    margin-left: -0.5rem !important;
+  }
+  .ms-xxl-n3 {
+    margin-left: -1rem !important;
+  }
+  .ms-xxl-n4 {
+    margin-left: -1.5rem !important;
+  }
+  .ms-xxl-n5 {
+    margin-left: -3rem !important;
+  }
+  .p-xxl-0 {
+    padding: 0 !important;
+  }
+  .p-xxl-1 {
+    padding: 0.25rem !important;
+  }
+  .p-xxl-2 {
+    padding: 0.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: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .px-xxl-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.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: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .py-xxl-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .pt-xxl-2 {
+    padding-top: 0.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: 0.25rem !important;
+  }
+  .pe-xxl-2 {
+    padding-right: 0.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: 0.25rem !important;
+  }
+  .pb-xxl-2 {
+    padding-bottom: 0.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: 0.25rem !important;
+  }
+  .ps-xxl-2 {
+    padding-left: 0.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: 0.25rem !important;
+  }
+  .gap-xxl-2 {
+    gap: 0.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: 0.25rem !important;
+  }
+  .row-gap-xxl-2 {
+    row-gap: 0.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: 0.25rem !important;
+  }
+  .column-gap-xxl-2 {
+    -moz-column-gap: 0.5rem !important;
+    column-gap: 0.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;
+  }
+}
+@keyframes flipInX {
+  0% {
+    opacity: 0;
+    transition-timing-function: ease-in;
+    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+  }
+  40% {
+    transition-timing-function: ease-in;
+    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+  }
+  60% {
+    opacity: 1;
+    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
+  }
+  80% {
+    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
+  }
+  100% {
+    transform: perspective(400px);
+  }
+}
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+}
+@keyframes fadeOut {
+  from {
+    opacity: 1;
+  }
+  to {
+    opacity: 0;
+  }
+}
+@keyframes shake {
+  0% {
+    transform: translate(2px, 1px) rotate(0deg);
+  }
+  10% {
+    transform: translate(-1px, -2px) rotate(-2deg);
+  }
+  20% {
+    transform: translate(-3px, 0) rotate(3deg);
+  }
+  30% {
+    transform: translate(0, 2px) rotate(0deg);
+  }
+  40% {
+    transform: translate(1px, -1px) rotate(1deg);
+  }
+  50% {
+    transform: translate(-1px, 2px) rotate(-1deg);
+  }
+  60% {
+    transform: translate(-3px, 1px) rotate(0deg);
+  }
+  70% {
+    transform: translate(2px, 1px) rotate(-2deg);
+  }
+  80% {
+    transform: translate(-1px, -1px) rotate(4deg);
+  }
+  90% {
+    transform: translate(2px, 2px) rotate(0deg);
+  }
+  100% {
+    transform: translate(1px, -2px) rotate(-1deg);
+  }
+}
+@keyframes wobble {
+  0% {
+    transform: none;
+  }
+  15% {
+    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
+  }
+  30% {
+    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
+  }
+  45% {
+    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
+  }
+  60% {
+    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
+  }
+  75% {
+    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
+  }
+  100% {
+    transform: none;
+  }
+}
+:root,
+[data-bs-theme=light] {
+  --lte-sidebar-width: 250px;
+}
+
+.app-wrapper {
+  position: relative;
+  display: grid;
+  grid-template-areas: "lte-app-sidebar lte-app-header" "lte-app-sidebar lte-app-main" "lte-app-sidebar lte-app-footer";
+  grid-template-rows: -webkit-min-content 1fr -webkit-min-content;
+  grid-template-rows: min-content 1fr min-content;
+  grid-template-columns: auto 1fr;
+  grid-gap: 0;
+  align-content: stretch;
+  align-items: stretch;
+  max-width: 100vw;
+  min-height: 100vh;
+}
+.app-wrapper > * {
+  min-width: 0;
+}
+
+.app-content {
+  padding: 0 0.5rem;
+}
+
+.app-header {
+  z-index: 1034;
+  grid-area: lte-app-header;
+  max-width: 100vw;
+  border-bottom: 1px solid var(--bs-border-color);
+  transition: 0.3s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .app-header {
+    transition: none;
+  }
+}
+.app-header .nav-link {
+  position: relative;
+  height: 2.5rem;
+}
+
+.navbar-badge {
+  position: absolute;
+  top: 9px;
+  right: 5px;
+  padding: 2px 4px;
+  font-size: 0.6rem;
+  font-weight: 400;
+}
+
+.app-sidebar {
+  --lte-sidebar-hover-bg: rgba(0, 0, 0, 0.1);
+  --lte-sidebar-color: #343a40;
+  --lte-sidebar-hover-color: #212529;
+  --lte-sidebar-active-color: #000;
+  --lte-sidebar-menu-active-bg: rgba(0, 0, 0, 0.1);
+  --lte-sidebar-menu-active-color: #000;
+  --lte-sidebar-submenu-bg: transparent;
+  --lte-sidebar-submenu-color: #777;
+  --lte-sidebar-submenu-hover-color: #000;
+  --lte-sidebar-submenu-hover-bg: rgba(0, 0, 0, 0.1);
+  --lte-sidebar-submenu-active-color: #212529;
+  --lte-sidebar-submenu-active-bg: rgba(0, 0, 0, 0.1);
+  --lte-sidebar-header-color: #31373d;
+  z-index: 1038;
+  grid-area: lte-app-sidebar;
+  min-width: var(--lte-sidebar-width);
+  max-width: var(--lte-sidebar-width);
+  transition: min-width 0.3s ease-in-out, max-width 0.3s ease-in-out, margin-left 0.3s ease-in-out, margin-right 0.3s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .app-sidebar {
+    transition: none;
+  }
+}
+
+.sidebar-brand {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 3.55rem;
+  padding: 0.8125rem 0.5rem;
+  overflow: hidden;
+  font-size: 1.25rem;
+  white-space: nowrap;
+  border-bottom: 1px solid var(--bs-border-color);
+  transition: width 0.3s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .sidebar-brand {
+    transition: none;
+  }
+}
+.sidebar-brand .brand-link {
+  display: flex;
+  align-items: center;
+  text-decoration: none;
+}
+.sidebar-brand .brand-link .brand-image {
+  float: left;
+  width: auto;
+  max-height: 33px;
+  line-height: 0.8;
+}
+.sidebar-brand .brand-link .brand-image-xs {
+  float: left;
+  width: auto;
+  max-height: 33px;
+  margin-top: -0.1rem;
+  line-height: 0.8;
+}
+.sidebar-brand .brand-link .brand-image-xl {
+  width: auto;
+  max-height: 40px;
+  line-height: 0.8;
+}
+.sidebar-brand .brand-link .brand-image-xl.single {
+  margin-top: -0.3rem;
+}
+.sidebar-brand .brand-text {
+  margin-left: 0.5rem;
+  color: rgba(var(--bs-emphasis-color-rgb), 0.8);
+  transition: flex 0.3s ease-in-out, width 0.3s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .sidebar-brand .brand-text {
+    transition: none;
+  }
+}
+.sidebar-brand .brand-text:hover {
+  color: var(--bs-emphasis-color);
+}
+
+.sidebar-wrapper {
+  padding-top: 0.5rem;
+  padding-right: 0.5rem;
+  padding-bottom: 0.5rem;
+  padding-left: 0.5rem;
+  scrollbar-color: var(--bs-secondary-bg) transparent;
+  scrollbar-width: thin;
+}
+.sidebar-wrapper::-webkit-scrollbar-thumb {
+  background-color: var(--bs-secondary-bg);
+}
+.sidebar-wrapper::-webkit-scrollbar-track {
+  background-color: transparent;
+}
+.sidebar-wrapper::-webkit-scrollbar-corner {
+  background-color: transparent;
+}
+.sidebar-wrapper::-webkit-scrollbar {
+  width: 0.5rem;
+  height: 0.5rem;
+}
+.sidebar-wrapper .nav-item {
+  max-width: 100%;
+}
+.sidebar-wrapper .nav-link {
+  display: flex;
+  justify-content: flex-start;
+}
+.sidebar-wrapper .nav-link p {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.sidebar-wrapper .nav-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  min-width: 1.5rem;
+  max-width: 1.5rem;
+}
+.sidebar-wrapper .sidebar-menu > .nav-item.menu-open .nav-link.active:not(:hover) {
+  --lte-sidebar-menu-active-bg: transparent;
+}
+.sidebar-wrapper .sidebar-menu > .nav-item > .nav-link:active, .sidebar-wrapper .sidebar-menu > .nav-item > .nav-link:focus {
+  color: var(--lte-sidebar-color);
+}
+.sidebar-wrapper .sidebar-menu > .nav-item > .nav-link.active:not(:hover) {
+  color: var(--lte-sidebar-menu-active-color);
+  background-color: var(--lte-sidebar-menu-active-bg);
+}
+.sidebar-wrapper .sidebar-menu > .nav-item.menu-open > .nav-link, .sidebar-wrapper .sidebar-menu > .nav-item:hover > .nav-link,
+.sidebar-wrapper .sidebar-menu > .nav-item > .nav-link:focus {
+  color: var(--lte-sidebar-hover-color);
+  background-color: var(--lte-sidebar-hover-bg);
+}
+.sidebar-wrapper .sidebar-menu > .nav-item > .nav-treeview {
+  background-color: var(--lte-sidebar-submenu-bg);
+}
+.sidebar-wrapper .nav-header {
+  color: var(--lte-sidebar-header-color);
+  background-color: inherit;
+}
+.sidebar-wrapper a {
+  color: var(--lte-sidebar-color);
+}
+.sidebar-wrapper .nav-treeview > .nav-item > .nav-link {
+  color: var(--lte-sidebar-submenu-color);
+}
+.sidebar-wrapper .nav-treeview > .nav-item > .nav-link:hover, .sidebar-wrapper .nav-treeview > .nav-item > .nav-link:focus {
+  color: var(--lte-sidebar-submenu-hover-color);
+}
+.sidebar-wrapper .nav-treeview > .nav-item > .nav-link.active, .sidebar-wrapper .nav-treeview > .nav-item > .nav-link.active:hover, .sidebar-wrapper .nav-treeview > .nav-item > .nav-link.active:focus {
+  color: var(--lte-sidebar-submenu-active-color);
+  background-color: var(--lte-sidebar-submenu-active-bg);
+}
+.sidebar-wrapper .nav-treeview > .nav-item > .nav-link:hover {
+  background-color: var(--lte-sidebar-submenu-hover-bg);
+}
+
+.sidebar-menu .nav-item > .nav-link {
+  margin-bottom: 0.2rem;
+}
+.sidebar-menu .nav-item > .nav-link .nav-arrow {
+  transition: transform ease-in-out 0.3s;
+  transform: translateY(-50%) /*rtl:append:rotate(180deg)*/;
+  animation-name: fadeIn;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+}
+@media (prefers-reduced-motion: reduce) {
+  .sidebar-menu .nav-item > .nav-link .nav-arrow {
+    transition: none;
+  }
+}
+.sidebar-menu .nav-link > .nav-badge,
+.sidebar-menu .nav-link > p > .nav-badge {
+  position: absolute;
+  right: 1rem;
+}
+.sidebar-menu .nav-link > .nav-arrow,
+.sidebar-menu .nav-link > p > .nav-arrow {
+  position: absolute;
+  top: 50%;
+  right: 1rem;
+}
+.sidebar-menu .nav-link {
+  position: relative;
+  transition: width ease-in-out 0.3s;
+  border-radius: 0.375rem;
+}
+@media (prefers-reduced-motion: reduce) {
+  .sidebar-menu .nav-link {
+    transition: none;
+  }
+}
+.sidebar-menu .nav-link p {
+  display: inline;
+  padding-left: 0.5rem;
+  margin: 0;
+}
+.sidebar-menu .nav-header {
+  padding: 0.5rem 0.75rem;
+  font-size: 0.9rem;
+}
+.sidebar-menu .nav-treeview {
+  display: none;
+  padding: 0;
+  list-style: none;
+}
+.nav-indent .sidebar-menu .nav-treeview {
+  padding-left: 0.5rem;
+}
+.sidebar-menu .menu-open > .nav-treeview {
+  display: block;
+}
+.sidebar-menu .menu-open > .nav-link .nav-arrow {
+  transform: translateY(-50%) rotate(90deg) /*rtl:ignore*/;
+}
+
+.nav-compact.nav-indent .nav-treeview {
+  padding-left: 0;
+}
+.nav-compact.nav-indent .nav-treeview .nav-item {
+  padding-left: 0.5rem;
+}
+
+.sidebar-mini.sidebar-collapse.nav-indent .app-sidebar:hover .nav-treeview {
+  padding-left: 0;
+}
+.sidebar-mini.sidebar-collapse.nav-indent .app-sidebar:hover .nav-treeview .nav-item {
+  padding-left: 0.5rem;
+}
+
+.sidebar-collapse.nav-compact.nav-indent .nav-treeview .nav-item {
+  padding-left: 0;
+}
+
+.nav-compact .nav-link {
+  border-radius: 0;
+  margin-bottom: 0 !important;
+}
+
+.sidebar-menu,
+.sidebar-menu > .nav-header,
+.sidebar-menu .nav-link {
+  white-space: nowrap;
+}
+
+.logo-xs,
+.logo-xl {
+  position: absolute;
+  visibility: visible;
+  opacity: 1;
+}
+.logo-xs.brand-image-xs,
+.logo-xl.brand-image-xs {
+  top: 12px;
+  left: 18px;
+}
+.logo-xs.brand-image-xl,
+.logo-xl.brand-image-xl {
+  top: 6px;
+  left: 12px;
+}
+
+.logo-xs {
+  visibility: hidden;
+  opacity: 0;
+}
+.logo-xs.brand-image-xl {
+  top: 8px;
+  left: 16px;
+}
+
+.brand-link.logo-switch::before {
+  content: " ";
+}
+
+.sidebar-mini.sidebar-collapse .app-sidebar {
+  min-width: 4.6rem;
+  max-width: 4.6rem;
+}
+.sidebar-mini.sidebar-collapse .sidebar-menu .nav-header {
+  display: none;
+}
+.sidebar-mini.sidebar-collapse .sidebar-menu .nav-link {
+  width: 3.6rem;
+}
+.sidebar-mini.sidebar-collapse .sidebar-menu .nav-link p {
+  display: inline-block;
+  width: 0;
+  white-space: nowrap;
+}
+.sidebar-mini.sidebar-collapse .sidebar-menu .nav-badge,
+.sidebar-mini.sidebar-collapse .sidebar-menu .nav-arrow {
+  display: none;
+  animation-name: fadeOut;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+}
+.sidebar-mini.sidebar-collapse .brand-text {
+  display: inline-block;
+  max-width: 0;
+  overflow: hidden;
+}
+.sidebar-mini.sidebar-collapse .sidebar-menu .nav-link p,
+.sidebar-mini.sidebar-collapse .brand-text,
+.sidebar-mini.sidebar-collapse .logo-xl,
+.sidebar-mini.sidebar-collapse .nav-arrow {
+  visibility: hidden;
+  animation-name: fadeOut;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+}
+.sidebar-mini.sidebar-collapse .logo-xs {
+  display: inline-block;
+  visibility: visible;
+  animation-name: fadeIn;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover {
+  min-width: var(--lte-sidebar-width);
+  max-width: var(--lte-sidebar-width);
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .sidebar-menu .nav-header {
+  display: inline-block;
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .sidebar-menu .nav-link {
+  width: auto;
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .sidebar-menu .nav-link p,
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .brand-text,
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .logo-xl {
+  width: auto;
+  margin-left: 0;
+  visibility: visible;
+  animation-name: fadeIn;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .brand-text {
+  display: inline;
+  max-width: inherit;
+  margin-left: 0.5rem;
+  animation-name: fadeIn;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .nav-badge,
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .nav-arrow {
+  display: inline-block;
+  visibility: visible;
+  animation-name: fadeIn;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+  animation-delay: 0.3s;
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .nav-link p {
+  padding-left: 0.5rem;
+}
+.sidebar-mini.sidebar-collapse .app-sidebar:hover .logo-xs {
+  visibility: hidden;
+  animation-name: fadeOut;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+}
+
+.sidebar-collapse:not(.sidebar-mini) .app-sidebar {
+  margin-left: calc(var(--lte-sidebar-width) * -1);
+}
+
+.sidebar-expand {
+  /* stylelint-disable-next-line scss/selector-no-union-class-name */
+  /* stylelint-disable-next-line scss/selector-no-union-class-name */
+  /* stylelint-disable-next-line scss/selector-no-union-class-name */
+  /* stylelint-disable-next-line scss/selector-no-union-class-name */
+  /* stylelint-disable-next-line scss/selector-no-union-class-name */
+  /* stylelint-disable-next-line scss/selector-no-union-class-name */
+}
+@media (min-width: 576px) {
+  .sidebar-expand-sm.layout-fixed .app-main-wrapper {
+    display: flex;
+    flex-direction: column;
+    min-height: 100vh;
+  }
+  .sidebar-expand-sm.layout-fixed .app-sidebar-wrapper {
+    position: relative;
+  }
+  .sidebar-expand-sm.layout-fixed .app-main {
+    flex: 1 1 auto;
+    overflow: auto;
+  }
+  .sidebar-expand-sm.layout-fixed .app-sidebar {
+    position: -webkit-sticky;
+    position: sticky;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+  }
+  .sidebar-expand-sm.layout-fixed .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-sm.sidebar-open .nav-link > .nav-badge,
+  .sidebar-expand-sm.sidebar-open .nav-link > p > .nav-badge {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+  .sidebar-expand-sm.sidebar-open .nav-link > .nav-arrow,
+  .sidebar-expand-sm.sidebar-open .nav-link > p > .nav-arrow {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+}
+@media (max-width: 575.98px) {
+  .sidebar-expand-sm::before {
+    display: none;
+    content: "575.98px";
+  }
+  .sidebar-expand-sm .app-sidebar {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+    margin-left: calc(var(--lte-sidebar-width) * -1);
+  }
+  .sidebar-expand-sm .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-sm.sidebar-open .app-sidebar {
+    margin-left: 0;
+  }
+  .sidebar-expand-sm.sidebar-open .sidebar-overlay {
+    position: absolute;
+    inset: 0;
+    z-index: 1037;
+    width: 100%;
+    height: 100%;
+    cursor: pointer;
+    visibility: visible;
+    background-color: rgba(0, 0, 0, 0.2);
+    animation-name: fadeIn;
+    animation-fill-mode: both;
+  }
+}
+@media (min-width: 768px) {
+  .sidebar-expand-md.layout-fixed .app-main-wrapper {
+    display: flex;
+    flex-direction: column;
+    min-height: 100vh;
+  }
+  .sidebar-expand-md.layout-fixed .app-sidebar-wrapper {
+    position: relative;
+  }
+  .sidebar-expand-md.layout-fixed .app-main {
+    flex: 1 1 auto;
+    overflow: auto;
+  }
+  .sidebar-expand-md.layout-fixed .app-sidebar {
+    position: -webkit-sticky;
+    position: sticky;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+  }
+  .sidebar-expand-md.layout-fixed .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-md.sidebar-open .nav-link > .nav-badge,
+  .sidebar-expand-md.sidebar-open .nav-link > p > .nav-badge {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+  .sidebar-expand-md.sidebar-open .nav-link > .nav-arrow,
+  .sidebar-expand-md.sidebar-open .nav-link > p > .nav-arrow {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+}
+@media (max-width: 767.98px) {
+  .sidebar-expand-md::before {
+    display: none;
+    content: "767.98px";
+  }
+  .sidebar-expand-md .app-sidebar {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+    margin-left: calc(var(--lte-sidebar-width) * -1);
+  }
+  .sidebar-expand-md .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-md.sidebar-open .app-sidebar {
+    margin-left: 0;
+  }
+  .sidebar-expand-md.sidebar-open .sidebar-overlay {
+    position: absolute;
+    inset: 0;
+    z-index: 1037;
+    width: 100%;
+    height: 100%;
+    cursor: pointer;
+    visibility: visible;
+    background-color: rgba(0, 0, 0, 0.2);
+    animation-name: fadeIn;
+    animation-fill-mode: both;
+  }
+}
+@media (min-width: 992px) {
+  .sidebar-expand-lg.layout-fixed .app-main-wrapper {
+    display: flex;
+    flex-direction: column;
+    min-height: 100vh;
+  }
+  .sidebar-expand-lg.layout-fixed .app-sidebar-wrapper {
+    position: relative;
+  }
+  .sidebar-expand-lg.layout-fixed .app-main {
+    flex: 1 1 auto;
+    overflow: auto;
+  }
+  .sidebar-expand-lg.layout-fixed .app-sidebar {
+    position: -webkit-sticky;
+    position: sticky;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+  }
+  .sidebar-expand-lg.layout-fixed .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-lg.sidebar-open .nav-link > .nav-badge,
+  .sidebar-expand-lg.sidebar-open .nav-link > p > .nav-badge {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+  .sidebar-expand-lg.sidebar-open .nav-link > .nav-arrow,
+  .sidebar-expand-lg.sidebar-open .nav-link > p > .nav-arrow {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+}
+@media (max-width: 991.98px) {
+  .sidebar-expand-lg::before {
+    display: none;
+    content: "991.98px";
+  }
+  .sidebar-expand-lg .app-sidebar {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+    margin-left: calc(var(--lte-sidebar-width) * -1);
+  }
+  .sidebar-expand-lg .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-lg.sidebar-open .app-sidebar {
+    margin-left: 0;
+  }
+  .sidebar-expand-lg.sidebar-open .sidebar-overlay {
+    position: absolute;
+    inset: 0;
+    z-index: 1037;
+    width: 100%;
+    height: 100%;
+    cursor: pointer;
+    visibility: visible;
+    background-color: rgba(0, 0, 0, 0.2);
+    animation-name: fadeIn;
+    animation-fill-mode: both;
+  }
+}
+@media (min-width: 1200px) {
+  .sidebar-expand-xl.layout-fixed .app-main-wrapper {
+    display: flex;
+    flex-direction: column;
+    min-height: 100vh;
+  }
+  .sidebar-expand-xl.layout-fixed .app-sidebar-wrapper {
+    position: relative;
+  }
+  .sidebar-expand-xl.layout-fixed .app-main {
+    flex: 1 1 auto;
+    overflow: auto;
+  }
+  .sidebar-expand-xl.layout-fixed .app-sidebar {
+    position: -webkit-sticky;
+    position: sticky;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+  }
+  .sidebar-expand-xl.layout-fixed .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-xl.sidebar-open .nav-link > .nav-badge,
+  .sidebar-expand-xl.sidebar-open .nav-link > p > .nav-badge {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+  .sidebar-expand-xl.sidebar-open .nav-link > .nav-arrow,
+  .sidebar-expand-xl.sidebar-open .nav-link > p > .nav-arrow {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+}
+@media (max-width: 1199.98px) {
+  .sidebar-expand-xl::before {
+    display: none;
+    content: "1199.98px";
+  }
+  .sidebar-expand-xl .app-sidebar {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+    margin-left: calc(var(--lte-sidebar-width) * -1);
+  }
+  .sidebar-expand-xl .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-xl.sidebar-open .app-sidebar {
+    margin-left: 0;
+  }
+  .sidebar-expand-xl.sidebar-open .sidebar-overlay {
+    position: absolute;
+    inset: 0;
+    z-index: 1037;
+    width: 100%;
+    height: 100%;
+    cursor: pointer;
+    visibility: visible;
+    background-color: rgba(0, 0, 0, 0.2);
+    animation-name: fadeIn;
+    animation-fill-mode: both;
+  }
+}
+@media (min-width: 1400px) {
+  .sidebar-expand-xxl.layout-fixed .app-main-wrapper {
+    display: flex;
+    flex-direction: column;
+    min-height: 100vh;
+  }
+  .sidebar-expand-xxl.layout-fixed .app-sidebar-wrapper {
+    position: relative;
+  }
+  .sidebar-expand-xxl.layout-fixed .app-main {
+    flex: 1 1 auto;
+    overflow: auto;
+  }
+  .sidebar-expand-xxl.layout-fixed .app-sidebar {
+    position: -webkit-sticky;
+    position: sticky;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+  }
+  .sidebar-expand-xxl.layout-fixed .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-xxl.sidebar-open .nav-link > .nav-badge,
+  .sidebar-expand-xxl.sidebar-open .nav-link > p > .nav-badge {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+  .sidebar-expand-xxl.sidebar-open .nav-link > .nav-arrow,
+  .sidebar-expand-xxl.sidebar-open .nav-link > p > .nav-arrow {
+    animation-name: fadeIn;
+    animation-duration: 0.3s;
+    animation-fill-mode: both;
+    animation-delay: 0.3s;
+  }
+}
+@media (max-width: 1399.98px) {
+  .sidebar-expand-xxl::before {
+    display: none;
+    content: "1399.98px";
+  }
+  .sidebar-expand-xxl .app-sidebar {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    max-height: 100vh;
+    margin-left: calc(var(--lte-sidebar-width) * -1);
+  }
+  .sidebar-expand-xxl .app-sidebar .sidebar-wrapper {
+    height: calc(100vh - (calc(3.5rem + 1px)));
+  }
+  .sidebar-expand-xxl.sidebar-open .app-sidebar {
+    margin-left: 0;
+  }
+  .sidebar-expand-xxl.sidebar-open .sidebar-overlay {
+    position: absolute;
+    inset: 0;
+    z-index: 1037;
+    width: 100%;
+    height: 100%;
+    cursor: pointer;
+    visibility: visible;
+    background-color: rgba(0, 0, 0, 0.2);
+    animation-name: fadeIn;
+    animation-fill-mode: both;
+  }
+}
+.sidebar-expand.layout-fixed .app-main-wrapper {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+}
+.sidebar-expand.layout-fixed .app-sidebar-wrapper {
+  position: relative;
+}
+.sidebar-expand.layout-fixed .app-main {
+  flex: 1 1 auto;
+  overflow: auto;
+}
+.sidebar-expand.layout-fixed .app-sidebar {
+  position: -webkit-sticky;
+  position: sticky;
+  top: 0;
+  bottom: 0;
+  max-height: 100vh;
+}
+.sidebar-expand.layout-fixed .app-sidebar .sidebar-wrapper {
+  height: calc(100vh - (calc(3.5rem + 1px)));
+}
+.sidebar-expand.sidebar-open .nav-link > .nav-badge,
+.sidebar-expand.sidebar-open .nav-link > p > .nav-badge {
+  animation-name: fadeIn;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+  animation-delay: 0.3s;
+}
+.sidebar-expand.sidebar-open .nav-link > .nav-arrow,
+.sidebar-expand.sidebar-open .nav-link > p > .nav-arrow {
+  animation-name: fadeIn;
+  animation-duration: 0.3s;
+  animation-fill-mode: both;
+  animation-delay: 0.3s;
+}
+.sidebar-expand::before {
+  display: none;
+  content: "";
+}
+.sidebar-expand .app-sidebar {
+  position: fixed;
+  top: 0;
+  bottom: 0;
+  max-height: 100vh;
+  margin-left: calc(var(--lte-sidebar-width) * -1);
+}
+.sidebar-expand .app-sidebar .sidebar-wrapper {
+  height: calc(100vh - (calc(3.5rem + 1px)));
+}
+.sidebar-expand.sidebar-open .app-sidebar {
+  margin-left: 0;
+}
+.sidebar-expand.sidebar-open .sidebar-overlay {
+  position: absolute;
+  inset: 0;
+  z-index: 1037;
+  width: 100%;
+  height: 100%;
+  cursor: pointer;
+  visibility: visible;
+  background-color: rgba(0, 0, 0, 0.2);
+  animation-name: fadeIn;
+  animation-fill-mode: both;
+}
+
+.sidebar-menu .nav-link p,
+.app-sidebar .brand-text,
+.app-sidebar .logo-xs,
+.app-sidebar .logo-xl {
+  transition: margin-left 0.3s linear, opacity 0.3s ease, visibility 0.3s ease;
+}
+@media (prefers-reduced-motion: reduce) {
+  .sidebar-menu .nav-link p,
+  .app-sidebar .brand-text,
+  .app-sidebar .logo-xs,
+  .app-sidebar .logo-xl {
+    transition: none;
+  }
+}
+
+.app-loaded.sidebar-mini.sidebar-collapse .sidebar-menu .nav-link p,
+.app-loaded.sidebar-mini.sidebar-collapse .brand-text {
+  animation-duration: 0.3s;
+}
+
+body:not(.app-loaded) .app-header,
+body:not(.app-loaded) .app-sidebar,
+body:not(.app-loaded) .app-main,
+body:not(.app-loaded) .app-footer {
+  transition: none !important;
+  animation-duration: 0s !important;
+}
+@media (prefers-reduced-motion: reduce) {
+  body:not(.app-loaded) .app-header,
+  body:not(.app-loaded) .app-sidebar,
+  body:not(.app-loaded) .app-main,
+  body:not(.app-loaded) .app-footer {
+    transition: none;
+  }
+}
+
+.hold-transition .app-header,
+.hold-transition .app-sidebar,
+.hold-transition .app-main,
+.hold-transition .app-footer,
+.hold-transition .nav-arrow,
+.hold-transition .nav-badge {
+  transition: none !important;
+  animation-duration: 0s !important;
+}
+@media (prefers-reduced-motion: reduce) {
+  .hold-transition .app-header,
+  .hold-transition .app-sidebar,
+  .hold-transition .app-main,
+  .hold-transition .app-footer,
+  .hold-transition .nav-arrow,
+  .hold-transition .nav-badge {
+    transition: none;
+  }
+}
+
+[data-bs-theme=dark].app-sidebar,
+[data-bs-theme=dark] .app-sidebar {
+  --lte-sidebar-hover-bg: rgba(255, 255, 255, 0.1);
+  --lte-sidebar-color: #c2c7d0;
+  --lte-sidebar-hover-color: #fff;
+  --lte-sidebar-active-color: #fff;
+  --lte-sidebar-menu-active-bg: rgba(255, 255, 255, 0.1);
+  --lte-sidebar-menu-active-color: #fff;
+  --lte-sidebar-submenu-bg: transparent;
+  --lte-sidebar-submenu-color: #c2c7d0;
+  --lte-sidebar-submenu-hover-color: #fff;
+  --lte-sidebar-submenu-hover-bg: rgba(255, 255, 255, 0.1);
+  --lte-sidebar-submenu-active-color: #fff;
+  --lte-sidebar-submenu-active-bg: rgba(255, 255, 255, 0.1);
+  --lte-sidebar-header-color: #c5cad2;
+}
+
+.app-main {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  grid-area: lte-app-main;
+  max-width: 100vw;
+  padding-bottom: 0.75rem;
+  transition: 0.3s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .app-main {
+    transition: none;
+  }
+}
+.app-main .app-content-header {
+  padding: 1rem 0.5rem;
+}
+.app-main .app-content-header .breadcrumb {
+  padding: 0;
+  margin-bottom: 0;
+  line-height: 2rem;
+}
+.app-main .app-content-header .breadcrumb a {
+  text-decoration: none;
+}
+.app-main .app-content-top-area,
+.app-main .app-content-bottom-area {
+  color: var(--bs-secondary-color);
+  background-color: var(--bs-body-bg);
+}
+.app-main .app-content-top-area {
+  padding: 1rem 0;
+  border-bottom: 1px solid var(--bs-border-color);
+}
+.app-main .app-content-bottom-area {
+  padding: 1rem 0;
+  margin-top: auto;
+  margin-bottom: -0.75rem;
+  border-top: 1px solid var(--bs-border-color);
+}
+
+.app-footer {
+  grid-area: lte-app-footer;
+  width: inherit;
+  max-width: 100vw;
+  min-height: 3rem;
+  padding: 1rem;
+  color: var(--bs-secondary-color);
+  background-color: var(--bs-body-bg);
+  border-top: 1px solid var(--bs-border-color);
+  transition: 0.3s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .app-footer {
+    transition: none;
+  }
+}
+
+.fs-7 .dropdown-menu {
+  font-size: 0.875rem !important;
+}
+.fs-7 .dropdown-toggle::after {
+  vertical-align: 0.2rem;
+}
+
+.dropdown-item-title {
+  margin: 0;
+  font-size: 1rem;
+}
+
+.dropdown-icon::after {
+  margin-left: 0;
+}
+
+.dropdown-menu-lg {
+  min-width: 280px;
+  max-width: 300px;
+  padding: 0;
+}
+.dropdown-menu-lg .dropdown-divider {
+  margin: 0;
+}
+.dropdown-menu-lg .dropdown-item {
+  padding: 0.5rem 1rem;
+}
+.dropdown-menu-lg p {
+  margin: 0;
+  word-wrap: break-word;
+  white-space: normal;
+}
+
+.dropdown-submenu {
+  position: relative;
+}
+.dropdown-submenu > a::after {
+  border-top: 0.3em solid transparent;
+  border-right: 0;
+  border-bottom: 0.3em solid transparent;
+  border-left: 0.3em solid;
+  float: right;
+  margin-top: 0.5rem;
+  margin-left: 0.5rem;
+}
+.dropdown-submenu > .dropdown-menu {
+  top: 0;
+  left: 100%;
+  margin-top: 0;
+  margin-left: 0;
+}
+
+.dropdown-hover:hover > .dropdown-menu, .dropdown-hover.nav-item.dropdown:hover > .dropdown-menu,
+.dropdown-hover .dropdown-submenu:hover > .dropdown-menu, .dropdown-hover.dropdown-submenu:hover > .dropdown-menu {
+  display: block;
+}
+
+.dropdown-menu-xl {
+  min-width: 360px;
+  max-width: 420px;
+  padding: 0;
+}
+.dropdown-menu-xl .dropdown-divider {
+  margin: 0;
+}
+.dropdown-menu-xl .dropdown-item {
+  padding: 0.5rem 1rem;
+}
+.dropdown-menu-xl p {
+  margin: 0;
+  word-wrap: break-word;
+  white-space: normal;
+}
+
+.dropdown-footer,
+.dropdown-header {
+  display: block;
+  padding: 0.5rem 1rem;
+  font-size: 0.875rem;
+  text-align: center;
+}
+
+.open:not(.dropup) > .animated-dropdown-menu {
+  animation: flipInX 0.7s both;
+  -webkit-backface-visibility: visible !important;
+  backface-visibility: visible !important;
+}
+
+.navbar-custom-menu > .navbar-nav > li {
+  position: relative;
+}
+.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
+  position: absolute;
+  right: 0;
+  left: auto;
+}
+
+@media (max-width: 575.98px) {
+  .navbar-custom-menu > .navbar-nav {
+    float: right;
+  }
+  .navbar-custom-menu > .navbar-nav > li {
+    position: static;
+  }
+  .navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
+    position: absolute;
+    right: 5%;
+    left: auto;
+    background-color: var(--bs-body-bg);
+    border: 1px solid var(--bs-border-color);
+  }
+}
+.navbar-nav > .user-menu > .nav-link::after {
+  content: none;
+}
+.navbar-nav > .user-menu > .dropdown-menu {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+  width: 280px;
+  padding: 0;
+}
+.navbar-nav > .user-menu > .dropdown-menu,
+.navbar-nav > .user-menu > .dropdown-menu > .user-body {
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header {
+  min-height: 175px;
+  padding: 10px;
+  text-align: center;
+}
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header > img {
+  z-index: 5;
+  width: 90px;
+  height: 90px;
+  border: 3px solid;
+  border-color: transparent;
+  border-color: var(--bs-border-color-translucent);
+}
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p {
+  z-index: 5;
+  margin-top: 10px;
+  font-size: 17px;
+  word-wrap: break-word;
+}
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p > small, .navbar-nav > .user-menu > .dropdown-menu > li.user-header > p > .small {
+  display: block;
+  font-size: 12px;
+}
+.navbar-nav > .user-menu > .dropdown-menu > .user-body {
+  padding: 15px;
+  border-top: 1px solid var(--bs-border-color);
+  border-bottom: 1px solid var(--bs-border-color-translucent);
+}
+.navbar-nav > .user-menu > .dropdown-menu > .user-body::after {
+  display: block;
+  clear: both;
+  content: "";
+}
+.navbar-nav > .user-menu > .dropdown-menu > .user-body a {
+  text-decoration: none;
+}
+@media (min-width: 576px) {
+  .navbar-nav > .user-menu > .dropdown-menu > .user-body a {
+    color: var(--bs-body-color) !important;
+    background-color: var(--bs-body-bg) !important;
+  }
+}
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer {
+  padding: 10px;
+  background-color: var(--bs-light-bg);
+}
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer::after {
+  display: block;
+  clear: both;
+  content: "";
+}
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default {
+  color: var(--bs-body-color);
+}
+@media (min-width: 576px) {
+  .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:hover {
+    background-color: var(--bs-body-bg);
+  }
+}
+.navbar-nav > .user-menu .user-image {
+  float: left;
+  width: 2rem;
+  height: 2rem;
+  margin-top: -2px;
+  border-radius: 50%;
+}
+@media (min-width: 576px) {
+  .navbar-nav > .user-menu .user-image {
+    float: none;
+    margin-top: -8px;
+    margin-right: 0.4rem;
+    line-height: 10px;
+  }
+}
+
+.callout {
+  --bs-link-color-rgb: var(--lte-callout-link);
+  --bs-code-color: var(--lte-callout-code-color);
+  padding: 1.25rem;
+  color: var(--lte-callout-color, inherit);
+  background-color: var(--lte-callout-bg, var(--bs-gray-100));
+  border-left: 0.25rem solid var(--lte-callout-border, var(--bs-gray-300));
+}
+.callout .callout-link {
+  font-weight: 700;
+  color: var(--bs-callout-link-color);
+}
+.callout h4, .callout .h4 {
+  margin-bottom: 0.25rem;
+}
+.callout > :last-child {
+  margin-bottom: 0;
+}
+.callout + .callout {
+  margin-top: -0.25rem;
+}
+
+.callout-primary {
+  --lte-callout-color: var(--bs-primary-text-emphasis);
+  --lte-callout-bg: var(--bs-primary-bg-subtle);
+  --lte-callout-border: var(--bs-primary-border-subtle);
+  --bs-callout-link-color: var(--bs-primary-text-emphasis);
+}
+
+.callout-secondary {
+  --lte-callout-color: var(--bs-secondary-text-emphasis);
+  --lte-callout-bg: var(--bs-secondary-bg-subtle);
+  --lte-callout-border: var(--bs-secondary-border-subtle);
+  --bs-callout-link-color: var(--bs-secondary-text-emphasis);
+}
+
+.callout-success {
+  --lte-callout-color: var(--bs-success-text-emphasis);
+  --lte-callout-bg: var(--bs-success-bg-subtle);
+  --lte-callout-border: var(--bs-success-border-subtle);
+  --bs-callout-link-color: var(--bs-success-text-emphasis);
+}
+
+.callout-info {
+  --lte-callout-color: var(--bs-info-text-emphasis);
+  --lte-callout-bg: var(--bs-info-bg-subtle);
+  --lte-callout-border: var(--bs-info-border-subtle);
+  --bs-callout-link-color: var(--bs-info-text-emphasis);
+}
+
+.callout-warning {
+  --lte-callout-color: var(--bs-warning-text-emphasis);
+  --lte-callout-bg: var(--bs-warning-bg-subtle);
+  --lte-callout-border: var(--bs-warning-border-subtle);
+  --bs-callout-link-color: var(--bs-warning-text-emphasis);
+}
+
+.callout-danger {
+  --lte-callout-color: var(--bs-danger-text-emphasis);
+  --lte-callout-bg: var(--bs-danger-bg-subtle);
+  --lte-callout-border: var(--bs-danger-border-subtle);
+  --bs-callout-link-color: var(--bs-danger-text-emphasis);
+}
+
+.callout-light {
+  --lte-callout-color: var(--bs-light-text-emphasis);
+  --lte-callout-bg: var(--bs-light-bg-subtle);
+  --lte-callout-border: var(--bs-light-border-subtle);
+  --bs-callout-link-color: var(--bs-light-text-emphasis);
+}
+
+.callout-dark {
+  --lte-callout-color: var(--bs-dark-text-emphasis);
+  --lte-callout-bg: var(--bs-dark-bg-subtle);
+  --lte-callout-border: var(--bs-dark-border-subtle);
+  --bs-callout-link-color: var(--bs-dark-text-emphasis);
+}
+
+.compact-mode .app-header {
+  max-height: 2.75rem;
+}
+.compact-mode .app-header .nav-link {
+  max-height: 1.75rem;
+}
+.compact-mode .nav-link {
+  --bs-nav-link-padding-y: .25rem;
+  --bs-nav-link-padding-x: .5rem;
+}
+.compact-mode.sidebar-mini.sidebar-collapse .app-sidebar:not(:hover) {
+  min-width: 3.1rem;
+  max-width: 3.1rem;
+}
+.compact-mode.sidebar-mini.sidebar-collapse .app-sidebar:not(:hover) .sidebar-menu .nav-link {
+  width: 2.1rem !important;
+}
+.compact-mode .logo-xs,
+.compact-mode .logo-xl {
+  max-height: 2.75rem;
+}
+.compact-mode .brand-image {
+  width: 1.75rem;
+  height: 1.75rem;
+}
+.compact-mode .sidebar-brand {
+  height: 2.75rem;
+}
+.compact-mode .app-footer {
+  padding: 0.5rem;
+}
+.compact-mode .sidebar-wrapper .nav-icon {
+  min-width: 1.1rem;
+  max-width: 1.1rem;
+}
+
+.astro-code {
+  padding: 0.75rem;
+  border-radius: 0.375rem;
+}
+
+.progress {
+  border-radius: 1px;
+}
+.progress.vertical {
+  position: relative;
+  display: inline-block;
+  width: 30px;
+  height: 200px;
+  margin-right: 10px;
+}
+.progress.vertical > .progress-bar {
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+}
+.progress.vertical.sm, .progress.vertical.progress-sm {
+  width: 20px;
+}
+.progress.vertical.xs, .progress.vertical.progress-xs {
+  width: 10px;
+}
+.progress.vertical.xxs, .progress.vertical.progress-xxs {
+  width: 3px;
+}
+
+.progress-group {
+  margin-bottom: 0.5rem;
+}
+
+.progress-sm {
+  height: 10px;
+}
+
+.progress-xs {
+  height: 7px;
+}
+
+.progress-xxs {
+  height: 3px;
+}
+
+.table tr > td .progress {
+  margin: 0;
+}
+
+.card {
+  box-shadow: 0 0 1px rgba(var(--bs-body-color-rgb), 0.125), 0 1px 3px rgba(var(--bs-body-color-rgb), 0.2);
+}
+.card[class*=card-]:not(.card-outline) > .card-header, .card[class*=text-bg-]:not(.card-outline) > .card-header {
+  color: var(--lte-card-variant-color);
+  background-color: var(--lte-card-variant-bg);
+}
+.card[class*=card-]:not(.card-outline) > .card-header .btn-tool, .card[class*=text-bg-]:not(.card-outline) > .card-header .btn-tool {
+  --bs-btn-color: rgba(var(--lte-card-variant-color-rgb), .8);
+  --bs-btn-hover-color: var(--lte-card-variant-color);
+}
+.card.card-outline {
+  border-top: 3px solid var(--lte-card-variant-bg);
+}
+.card.maximized-card {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 1050;
+  width: 100% !important;
+  max-width: 100% !important;
+  height: 100% !important;
+  max-height: 100% !important;
+}
+.card.maximized-card.was-collapsed .card-body {
+  display: block !important;
+}
+.card.maximized-card .card-body {
+  overflow: auto;
+}
+.card.maximized-card [data-lte-toggle=card-collapse] {
+  display: none;
+}
+.card.maximized-card [data-lte-icon=maximize] {
+  display: none;
+}
+.card.maximized-card .card-header,
+.card.maximized-card .card-footer {
+  border-radius: 0 !important;
+}
+.card:not(.maximized-card) [data-lte-icon=minimize] {
+  display: none;
+}
+.card.collapsed-card [data-lte-icon=collapse] {
+  display: none;
+}
+.card.collapsed-card .card-body,
+.card.collapsed-card .card-footer {
+  display: none;
+}
+.card:not(.collapsed-card) [data-lte-icon=expand] {
+  display: none;
+}
+.card .nav.flex-column > li {
+  margin: 0;
+  border-bottom: 1px solid var(--bs-border-color-translucent);
+}
+.card .nav.flex-column > li:last-of-type {
+  border-bottom: 0;
+}
+.card.height-control .card-body {
+  max-height: 300px;
+  overflow: auto;
+}
+.card .border-end {
+  border-right: 1px solid var(--bs-border-color-translucent);
+}
+.card .border-start {
+  border-left: 1px solid var(--bs-border-color-translucent);
+}
+.card.card-tabs:not(.card-outline) > .card-header {
+  border-bottom: 0;
+}
+.card.card-tabs:not(.card-outline) > .card-header .nav-item:first-child .nav-link {
+  border-left-color: transparent;
+}
+.card.card-tabs.card-outline .nav-item {
+  border-bottom: 0;
+}
+.card.card-tabs.card-outline .nav-item:first-child .nav-link {
+  margin-left: 0;
+  border-left: 0;
+}
+.card.card-tabs .card-tools {
+  margin: 0.3rem 0.5rem;
+}
+.card.card-tabs:not(.expanding-card).collapsed-card .card-header {
+  border-bottom: 0;
+}
+.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {
+  border-bottom: 0;
+}
+.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {
+  margin-bottom: 0;
+}
+.card.card-tabs.expanding-card .card-header .nav-tabs .nav-item {
+  margin-bottom: -1px;
+}
+.card.card-outline-tabs {
+  border-top: 0;
+}
+.card.card-outline-tabs .card-header .nav-item:first-child .nav-link {
+  margin-left: 0;
+  border-left: 0;
+}
+.card.card-outline-tabs .card-header a {
+  text-decoration: none;
+  border-top: 3px solid transparent;
+}
+.card.card-outline-tabs .card-header a:hover {
+  border-top: 3px solid var(--bs-border-color);
+}
+.card.card-outline-tabs .card-header a.active:hover {
+  margin-top: 0;
+}
+.card.card-outline-tabs .card-tools {
+  margin: 0.5rem 0.5rem 0.3rem;
+}
+.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header {
+  border-bottom: 0;
+}
+.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {
+  border-bottom: 0;
+}
+.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {
+  margin-bottom: 0;
+}
+.card.card-outline-tabs.expanding-card .card-header .nav-tabs .nav-item {
+  margin-bottom: -1px;
+}
+
+html.maximized-card {
+  overflow: hidden;
+}
+
+.card-header::after,
+.card-body::after,
+.card-footer::after {
+  display: block;
+  clear: both;
+  content: "";
+}
+
+.card-header {
+  position: relative;
+  padding: 1rem 1rem;
+  background-color: transparent;
+  border-bottom: 1px solid var(--bs-border-color-translucent);
+  border-top-left-radius: 0.375rem;
+  border-top-right-radius: 0.375rem;
+}
+.collapsed-card .card-header {
+  border-bottom: 0;
+}
+.card-header > .card-tools {
+  float: right;
+  margin-right: -0.5rem;
+}
+.card-header > .card-tools .input-group,
+.card-header > .card-tools .nav,
+.card-header > .card-tools .pagination {
+  margin-top: -0.4rem;
+  margin-bottom: -0.4rem;
+}
+.card-header > .card-tools [data-bs-toggle=tooltip] {
+  position: relative;
+}
+
+.card-title {
+  float: left;
+  margin: 0;
+  font-size: 1.1rem;
+  font-weight: 400;
+  line-height: 1.9rem;
+}
+
+.btn-tool {
+  --bs-btn-padding-x: .5rem;
+  --bs-btn-padding-y: .25rem;
+  margin: -1rem 0;
+  font-size: 0.875rem;
+}
+.btn-tool:not(.btn-tool-custom) {
+  --bs-btn-color: var(--bs-tertiary-color);
+  --bs-btn-bg: transparent;
+  --bs-btn-box-shadow: none;
+  --bs-btn-hover-color: var(--bs-secondary-color);
+  --bs-btn-active-border-color: transparent;
+}
+
+.card-primary,
+.bg-primary,
+.text-bg-primary {
+  --lte-card-variant-bg: #01445E;
+  --lte-card-variant-bg-rgb: 13, 110, 253;
+  --lte-card-variant-color: #fff;
+  --lte-card-variant-color-rgb: 255, 255, 255;
+}
+
+.card-secondary,
+.bg-secondary,
+.text-bg-secondary {
+  --lte-card-variant-bg: #6c757d;
+  --lte-card-variant-bg-rgb: 108, 117, 125;
+  --lte-card-variant-color: #fff;
+  --lte-card-variant-color-rgb: 255, 255, 255;
+}
+
+.card-success,
+.bg-success,
+.text-bg-success {
+  --lte-card-variant-bg: #198754;
+  --lte-card-variant-bg-rgb: 25, 135, 84;
+  --lte-card-variant-color: #fff;
+  --lte-card-variant-color-rgb: 255, 255, 255;
+}
+
+.card-info,
+.bg-info,
+.text-bg-info {
+  --lte-card-variant-bg: #0dcaf0;
+  --lte-card-variant-bg-rgb: 13, 202, 240;
+  --lte-card-variant-color: #000;
+  --lte-card-variant-color-rgb: 0, 0, 0;
+}
+
+.card-warning,
+.bg-warning,
+.text-bg-warning {
+  --lte-card-variant-bg: #ffc107;
+  --lte-card-variant-bg-rgb: 255, 193, 7;
+  --lte-card-variant-color: #000;
+  --lte-card-variant-color-rgb: 0, 0, 0;
+}
+
+.card-danger,
+.bg-danger,
+.text-bg-danger {
+  --lte-card-variant-bg: #dc3545;
+  --lte-card-variant-bg-rgb: 220, 53, 69;
+  --lte-card-variant-color: #fff;
+  --lte-card-variant-color-rgb: 255, 255, 255;
+}
+
+.card-light,
+.bg-light,
+.text-bg-light {
+  --lte-card-variant-bg: #f8f9fa;
+  --lte-card-variant-bg-rgb: 248, 249, 250;
+  --lte-card-variant-color: #000;
+  --lte-card-variant-color-rgb: 0, 0, 0;
+}
+
+.card-dark,
+.bg-dark,
+.text-bg-dark {
+  --lte-card-variant-bg: #212529;
+  --lte-card-variant-bg-rgb: 33, 37, 41;
+  --lte-card-variant-color: #fff;
+  --lte-card-variant-color-rgb: 255, 255, 255;
+}
+
+.card-body > .table {
+  margin-bottom: 0;
+}
+.card-body > .table > thead > tr > th,
+.card-body > .table > thead > tr > td {
+  border-top-width: 0;
+}
+
+.table:not(.table-dark) {
+  color: inherit;
+}
+.table.table-head-fixed thead tr:nth-child(1) th {
+  position: -webkit-sticky;
+  position: sticky;
+  top: 0;
+  z-index: 10;
+  background-color: #fff;
+  border-bottom: 0;
+  box-shadow: inset 0 1px 0 var(--bs-border-color), inset 0 -1px 0 var(--bs-border-color);
+}
+.table.no-border,
+.table.no-border td,
+.table.no-border th {
+  border: 0;
+}
+.table.text-center,
+.table.text-center td,
+.table.text-center th {
+  text-align: center;
+}
+.table.table-valign-middle thead > tr > th,
+.table.table-valign-middle thead > tr > td,
+.table.table-valign-middle tbody > tr > th,
+.table.table-valign-middle tbody > tr > td {
+  vertical-align: middle;
+}
+.card-body.p-0 .table thead > tr > th:first-of-type,
+.card-body.p-0 .table thead > tr > td:first-of-type,
+.card-body.p-0 .table tfoot > tr > th:first-of-type,
+.card-body.p-0 .table tfoot > tr > td:first-of-type,
+.card-body.p-0 .table tbody > tr > th:first-of-type,
+.card-body.p-0 .table tbody > tr > td:first-of-type {
+  padding-left: 1.5rem;
+}
+.card-body.p-0 .table thead > tr > th:last-of-type,
+.card-body.p-0 .table thead > tr > td:last-of-type,
+.card-body.p-0 .table tfoot > tr > th:last-of-type,
+.card-body.p-0 .table tfoot > tr > td:last-of-type,
+.card-body.p-0 .table tbody > tr > th:last-of-type,
+.card-body.p-0 .table tbody > tr > td:last-of-type {
+  padding-right: 1.5rem;
+}
+
+.small-box {
+  border-radius: 0.375rem;
+  box-shadow: 0 0 1px rgba(var(--bs-body-color-rgb), 0.125), 0 1px 3px rgba(var(--bs-body-color-rgb), 0.2);
+  position: relative;
+  display: block;
+  margin-bottom: 1.25rem;
+  --bs-link-color-rgb: none;
+  --bs-link-hover-color-rgb: none;
+  --bs-heading-color: none;
+}
+.small-box > .inner {
+  padding: 10px;
+}
+.small-box > .small-box-footer {
+  position: relative;
+  z-index: 10;
+  display: block;
+  padding: 3px 0;
+  text-align: center;
+  background-color: rgba(0, 0, 0, 0.07);
+}
+.small-box > .small-box-footer:hover {
+  background-color: rgba(0, 0, 0, 0.1);
+}
+.small-box h3, .small-box .h3 {
+  font-size: calc(1.345rem + 1.14vw);
+  padding: 0;
+  margin: 0 0 10px;
+  font-weight: 700;
+  white-space: nowrap;
+}
+@media (min-width: 1200px) {
+  .small-box h3, .small-box .h3 {
+    font-size: 2.2rem;
+  }
+}
+@media (min-width: 992px) {
+  .col-xl-2 .small-box h3, .col-xl-2 .small-box .h3, .col-lg-2 .small-box h3, .col-lg-2 .small-box .h3, .col-md-2 .small-box h3, .col-md-2 .small-box .h3 {
+    font-size: calc(1.285rem + 0.42vw);
+  }
+}
+@media (min-width: 992px) and (min-width: 1200px) {
+  .col-xl-2 .small-box h3, .col-xl-2 .small-box .h3, .col-lg-2 .small-box h3, .col-lg-2 .small-box .h3, .col-md-2 .small-box h3, .col-md-2 .small-box .h3 {
+    font-size: 1.6rem;
+  }
+}
+@media (min-width: 992px) {
+  .col-xl-3 .small-box h3, .col-xl-3 .small-box .h3, .col-lg-3 .small-box h3, .col-lg-3 .small-box .h3, .col-md-3 .small-box h3, .col-md-3 .small-box .h3 {
+    font-size: calc(1.285rem + 0.42vw);
+  }
+}
+@media (min-width: 992px) and (min-width: 1200px) {
+  .col-xl-3 .small-box h3, .col-xl-3 .small-box .h3, .col-lg-3 .small-box h3, .col-lg-3 .small-box .h3, .col-md-3 .small-box h3, .col-md-3 .small-box .h3 {
+    font-size: 1.6rem;
+  }
+}
+@media (min-width: 1200px) {
+  .col-xl-2 .small-box h3, .col-xl-2 .small-box .h3, .col-lg-2 .small-box h3, .col-lg-2 .small-box .h3, .col-md-2 .small-box h3, .col-md-2 .small-box .h3 {
+    font-size: calc(1.345rem + 1.14vw);
+  }
+}
+@media (min-width: 1200px) and (min-width: 1200px) {
+  .col-xl-2 .small-box h3, .col-xl-2 .small-box .h3, .col-lg-2 .small-box h3, .col-lg-2 .small-box .h3, .col-md-2 .small-box h3, .col-md-2 .small-box .h3 {
+    font-size: 2.2rem;
+  }
+}
+@media (min-width: 1200px) {
+  .col-xl-3 .small-box h3, .col-xl-3 .small-box .h3, .col-lg-3 .small-box h3, .col-lg-3 .small-box .h3, .col-md-3 .small-box h3, .col-md-3 .small-box .h3 {
+    font-size: calc(1.345rem + 1.14vw);
+  }
+}
+@media (min-width: 1200px) and (min-width: 1200px) {
+  .col-xl-3 .small-box h3, .col-xl-3 .small-box .h3, .col-lg-3 .small-box h3, .col-lg-3 .small-box .h3, .col-md-3 .small-box h3, .col-md-3 .small-box .h3 {
+    font-size: 2.2rem;
+  }
+}
+.small-box p {
+  font-size: 1rem;
+}
+.small-box p > small, .small-box p > .small {
+  display: block;
+  margin-top: 5px;
+  font-size: 0.9rem;
+  color: #f8f9fa;
+}
+.small-box h3, .small-box .h3,
+.small-box p {
+  z-index: 5;
+}
+.small-box .small-box-icon {
+  position: absolute;
+  top: 15px;
+  right: 15px;
+  z-index: 0;
+  height: 70px;
+  font-size: 70px;
+  color: rgba(0, 0, 0, 0.15);
+  transition: transform 0.3s linear;
+}
+@media (prefers-reduced-motion: reduce) {
+  .small-box .small-box-icon {
+    transition: none;
+  }
+}
+.small-box:hover .small-box-icon {
+  transform: scale(1.1);
+}
+
+@media (max-width: 575.98px) {
+  .small-box {
+    text-align: center;
+  }
+  .small-box .small-box-icon {
+    display: none;
+  }
+  .small-box p {
+    font-size: 12px;
+  }
+}
+.info-box {
+  box-shadow: 0 0 1px rgba(var(--bs-body-color-rgb), 0.125), 0 1px 3px rgba(var(--bs-body-color-rgb), 0.2);
+  border-radius: 0.375rem;
+  position: relative;
+  display: flex;
+  width: 100%;
+  min-height: 80px;
+  padding: 0.5rem;
+  margin-bottom: 1rem;
+  color: var(--bs-body-color);
+  background-color: var(--bs-body-bg);
+}
+.info-box .progress {
+  height: 2px;
+  margin: 5px 0;
+  background-color: rgba(var(--lte-card-variant-color-rgb), 0.125);
+}
+.info-box .progress .progress-bar {
+  background-color: var(--lte-card-variant-color);
+}
+.info-box .info-box-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 70px;
+  font-size: 1.875rem;
+  text-align: center;
+  border-radius: 0.375rem;
+}
+.info-box .info-box-icon > img {
+  max-width: 100%;
+}
+.info-box .info-box-content {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  justify-content: center;
+  padding: 0 10px;
+  line-height: 1.8;
+}
+.info-box .info-box-number {
+  display: block;
+  margin-top: 0.25rem;
+  font-weight: 700;
+}
+.info-box .progress-description,
+.info-box .info-box-text {
+  display: block;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.info-box .info-box-more {
+  display: block;
+}
+.info-box .progress-description {
+  margin: 0;
+}
+@media (min-width: 768px) {
+  .col-xl-2 .info-box .progress-description, .col-lg-2 .info-box .progress-description, .col-md-2 .info-box .progress-description {
+    display: none;
+  }
+  .col-xl-3 .info-box .progress-description, .col-lg-3 .info-box .progress-description, .col-md-3 .info-box .progress-description {
+    display: none;
+  }
+}
+@media (min-width: 992px) {
+  .col-xl-2 .info-box .progress-description, .col-lg-2 .info-box .progress-description, .col-md-2 .info-box .progress-description {
+    font-size: 0.75rem;
+    display: block;
+  }
+  .col-xl-3 .info-box .progress-description, .col-lg-3 .info-box .progress-description, .col-md-3 .info-box .progress-description {
+    font-size: 0.75rem;
+    display: block;
+  }
+}
+@media (min-width: 1200px) {
+  .col-xl-2 .info-box .progress-description, .col-lg-2 .info-box .progress-description, .col-md-2 .info-box .progress-description {
+    font-size: 1rem;
+    display: block;
+  }
+  .col-xl-3 .info-box .progress-description, .col-lg-3 .info-box .progress-description, .col-md-3 .info-box .progress-description {
+    font-size: 1rem;
+    display: block;
+  }
+}
+
+.timeline {
+  position: relative;
+  padding: 0;
+  margin: 0 0 45px;
+}
+.timeline::before {
+  border-radius: 0.375rem;
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 31px;
+  width: 4px;
+  margin: 0;
+  content: "";
+  background-color: var(--bs-border-color);
+}
+.timeline > div {
+  position: relative;
+  margin-right: 10px;
+  margin-bottom: 15px;
+}
+.timeline > div::before, .timeline > div::after {
+  display: table;
+  content: "";
+}
+.timeline > div > .timeline-item {
+  box-shadow: 0 0 1px rgba(var(--bs-body-color-rgb), 0.125), 0 1px 3px rgba(var(--bs-body-color-rgb), 0.2);
+  border-radius: 0.375rem;
+  position: relative;
+  padding: 0;
+  margin-top: 0;
+  margin-right: 15px;
+  margin-left: 60px;
+  color: var(--bs-body-color);
+  background-color: var(--bs-body-bg);
+}
+.timeline > div > .timeline-item > .time {
+  float: right;
+  padding: 10px;
+  font-size: 12px;
+  color: var(--bs-secondary-color);
+}
+.timeline > div > .timeline-item > .timeline-header {
+  padding: 10px;
+  margin: 0;
+  font-size: 16px;
+  line-height: 1.1;
+  color: var(--bs-secondary-color);
+  border-bottom: 1px solid var(--bs-border-color);
+}
+.timeline > div > .timeline-item > .timeline-header > a {
+  font-weight: 600;
+  text-decoration: none;
+}
+.timeline > div > .timeline-item > .timeline-body,
+.timeline > div > .timeline-item > .timeline-footer {
+  padding: 10px;
+}
+.timeline > div > .timeline-item > .timeline-body > img {
+  margin: 10px;
+}
+.timeline > div > .timeline-item > .timeline-body > dl,
+.timeline > div > .timeline-item > .timeline-body ol,
+.timeline > div > .timeline-item > .timeline-body ul {
+  margin: 0;
+}
+.timeline > div .timeline-icon {
+  position: absolute;
+  top: 0;
+  left: 18px;
+  width: 30px;
+  height: 30px;
+  font-size: 16px;
+  line-height: 30px;
+  text-align: center;
+  background-color: var(--bs-secondary-bg);
+  border-radius: 50%;
+}
+.timeline > .time-label > span {
+  border-radius: 4px;
+  display: inline-block;
+  padding: 5px;
+  font-weight: 600;
+  background-color: var(--bs-body-bg);
+}
+
+.timeline-inverse > div > .timeline-item {
+  box-shadow: none;
+  background-color: var(--bs-tertiary-bg);
+  border: 1px solid var(--bs-border-color);
+}
+.timeline-inverse > div > .timeline-item > .timeline-header {
+  border-bottom-color: var(--bs-border-color);
+}
+
+.direct-chat .card-body {
+  position: relative;
+  padding: 0;
+  overflow-x: hidden;
+}
+.direct-chat.chat-pane-open .direct-chat-contacts {
+  transform: translate(0, 0);
+}
+.direct-chat.timestamp-light .direct-chat-timestamp {
+  color: rgba(var(--bs-body-color-rgb), 0.65);
+}
+.direct-chat.timestamp-dark .direct-chat-timestamp {
+  color: rgba(var(--bs-body-color-rgb), 0.9);
+}
+
+.direct-chat-messages {
+  height: 250px;
+  padding: 10px;
+  overflow: auto;
+  transform: translate(0, 0);
+}
+
+.direct-chat-msg,
+.direct-chat-text {
+  display: block;
+}
+
+.direct-chat-msg {
+  margin-bottom: 10px;
+}
+.direct-chat-msg::after {
+  display: block;
+  clear: both;
+  content: "";
+}
+
+.direct-chat-messages,
+.direct-chat-contacts {
+  transition: transform 0.5s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .direct-chat-messages,
+  .direct-chat-contacts {
+    transition: none;
+  }
+}
+
+.direct-chat-text {
+  border-radius: 0.5rem;
+  position: relative;
+  padding: 5px 10px;
+  margin: 5px 0 0 50px;
+  color: var(--bs-emphasis-color);
+  background-color: var(--bs-secondary-bg);
+  border: 1px solid var(--bs-border-color);
+}
+.direct-chat-text::after, .direct-chat-text::before {
+  position: absolute;
+  top: 15px;
+  right: 100%;
+  width: 0;
+  height: 0;
+  pointer-events: none;
+  content: " ";
+  border: solid transparent;
+  border-right-color: var(--bs-border-color);
+}
+.direct-chat-text::after {
+  margin-top: -5px;
+  border-width: 5px;
+}
+.direct-chat-text::before {
+  margin-top: -6px;
+  border-width: 6px;
+}
+.end .direct-chat-text {
+  margin-right: 50px;
+  margin-left: 0;
+}
+.end .direct-chat-text::after, .end .direct-chat-text::before {
+  right: auto;
+  left: 100%;
+  border-right-color: transparent;
+  border-left-color: var(--bs-border-color);
+}
+
+.direct-chat-img {
+  border-radius: 50%;
+  float: left;
+  width: 40px;
+  height: 40px;
+}
+.end .direct-chat-img {
+  float: right;
+}
+
+.direct-chat-infos {
+  display: block;
+  margin-bottom: 2px;
+  font-size: 0.875rem;
+}
+
+.direct-chat-name {
+  font-weight: 600;
+}
+
+.direct-chat-timestamp {
+  color: rgba(var(--bs-body-color-rgb), 0.75);
+}
+
+.direct-chat-contacts-open .direct-chat-contacts {
+  transform: translate(0, 0);
+}
+
+.direct-chat-contacts {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  width: 100%;
+  height: 250px;
+  overflow: auto;
+  color: var(--bs-body-bg);
+  background-color: var(--bs-body-color);
+  transform: translate(101%, 0);
+}
+
+.direct-chat-contacts-light {
+  background-color: var(--bs-light-bg-subtle);
+}
+.direct-chat-contacts-light .contacts-list-name {
+  color: var(--bs-body-color);
+}
+.direct-chat-contacts-light .contacts-list-date {
+  color: var(--bs-secondary-color);
+}
+.direct-chat-contacts-light .contacts-list-msg {
+  color: var(--bs-secondary-color);
+}
+
+.contacts-list {
+  padding-left: 0;
+  list-style: none;
+}
+.contacts-list > li {
+  padding: 10px;
+  margin: 0;
+  text-decoration: none;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
+}
+.contacts-list > li::after {
+  display: block;
+  clear: both;
+  content: "";
+}
+.contacts-list > li:last-of-type {
+  border-bottom: 0;
+}
+.contacts-list > li a {
+  text-decoration: none;
+}
+
+.contacts-list-img {
+  border-radius: 50%;
+  float: left;
+  width: 40px;
+}
+
+.contacts-list-info {
+  margin-left: 45px;
+  color: var(--bs-body-bg);
+}
+
+.contacts-list-name,
+.contacts-list-status {
+  display: block;
+}
+
+.contacts-list-name {
+  font-weight: 600;
+}
+
+.contacts-list-status {
+  font-size: 0.875rem;
+}
+
+.contacts-list-date {
+  font-weight: 400;
+  color: var(--bs-secondary-bg);
+}
+
+.contacts-list-msg {
+  color: var(--bs-secondary-bg);
+}
+
+.end > .direct-chat-text {
+  color: var(--lte-direct-chat-color);
+  background-color: var(--lte-direct-chat-bg);
+  border-color: var(--lte-direct-chat-bg);
+}
+.end > .direct-chat-text::after, .end > .direct-chat-text::before {
+  border-left-color: var(--lte-direct-chat-bg);
+}
+
+.direct-chat-primary {
+  --lte-direct-chat-color: #fff;
+  --lte-direct-chat-bg: #01445E;
+}
+
+.direct-chat-secondary {
+  --lte-direct-chat-color: #fff;
+  --lte-direct-chat-bg: #6c757d;
+}
+
+.direct-chat-success {
+  --lte-direct-chat-color: #fff;
+  --lte-direct-chat-bg: #198754;
+}
+
+.direct-chat-info {
+  --lte-direct-chat-color: #000;
+  --lte-direct-chat-bg: #0dcaf0;
+}
+
+.direct-chat-warning {
+  --lte-direct-chat-color: #000;
+  --lte-direct-chat-bg: #ffc107;
+}
+
+.direct-chat-danger {
+  --lte-direct-chat-color: #fff;
+  --lte-direct-chat-bg: #dc3545;
+}
+
+.direct-chat-light {
+  --lte-direct-chat-color: #000;
+  --lte-direct-chat-bg: #f8f9fa;
+}
+
+.direct-chat-dark {
+  --lte-direct-chat-color: #fff;
+  --lte-direct-chat-bg: #212529;
+}
+
+.toast-primary {
+  --bs-toast-header-color: #fff;
+  --bs-toast-header-bg: #01445E;
+  --bs-toast-header-border-color: #01445E;
+  --bs-toast-border-color: #01445E;
+  --bs-toast-bg: var(--bs-primary-bg-subtle);
+}
+.toast-primary .btn-close {
+  filter: var(--bs-btn-close-white-filter);
+}
+
+.toast-secondary {
+  --bs-toast-header-color: #fff;
+  --bs-toast-header-bg: #6c757d;
+  --bs-toast-header-border-color: #6c757d;
+  --bs-toast-border-color: #6c757d;
+  --bs-toast-bg: var(--bs-secondary-bg-subtle);
+}
+.toast-secondary .btn-close {
+  filter: var(--bs-btn-close-white-filter);
+}
+
+.toast-success {
+  --bs-toast-header-color: #fff;
+  --bs-toast-header-bg: #198754;
+  --bs-toast-header-border-color: #198754;
+  --bs-toast-border-color: #198754;
+  --bs-toast-bg: var(--bs-success-bg-subtle);
+}
+.toast-success .btn-close {
+  filter: var(--bs-btn-close-white-filter);
+}
+
+.toast-info {
+  --bs-toast-header-color: #000;
+  --bs-toast-header-bg: #0dcaf0;
+  --bs-toast-header-border-color: #0dcaf0;
+  --bs-toast-border-color: #0dcaf0;
+  --bs-toast-bg: var(--bs-info-bg-subtle);
+}
+
+.toast-warning {
+  --bs-toast-header-color: #000;
+  --bs-toast-header-bg: #ffc107;
+  --bs-toast-header-border-color: #ffc107;
+  --bs-toast-border-color: #ffc107;
+  --bs-toast-bg: var(--bs-warning-bg-subtle);
+}
+
+.toast-danger {
+  --bs-toast-header-color: #fff;
+  --bs-toast-header-bg: #dc3545;
+  --bs-toast-header-border-color: #dc3545;
+  --bs-toast-border-color: #dc3545;
+  --bs-toast-bg: var(--bs-danger-bg-subtle);
+}
+.toast-danger .btn-close {
+  filter: var(--bs-btn-close-white-filter);
+}
+
+.toast-light {
+  --bs-toast-header-color: #000;
+  --bs-toast-header-bg: #f8f9fa;
+  --bs-toast-header-border-color: #f8f9fa;
+  --bs-toast-border-color: #f8f9fa;
+  --bs-toast-bg: var(--bs-light-bg-subtle);
+}
+
+.toast-dark {
+  --bs-toast-header-color: #fff;
+  --bs-toast-header-bg: #212529;
+  --bs-toast-header-border-color: #212529;
+  --bs-toast-border-color: #212529;
+  --bs-toast-bg: var(--bs-dark-bg-subtle);
+}
+.toast-dark .btn-close {
+  filter: var(--bs-btn-close-white-filter);
+}
+
+[data-bs-theme=dark] .toast-info .btn-close {
+  --bs-btn-close-white-filter: none;
+}
+[data-bs-theme=dark] .toast-warning .btn-close {
+  --bs-btn-close-white-filter: none;
+}
+[data-bs-theme=dark] .toast-light .btn-close {
+  --bs-btn-close-white-filter: none;
+}
+.login-logo,
+.register-logo {
+  margin-bottom: 0.9rem;
+  font-size: 2.1rem;
+  font-weight: 300;
+  text-align: center;
+}
+.login-logo a,
+.register-logo a {
+  color: var(--bs-secondary-color);
+  text-decoration: none;
+}
+
+.login-page,
+.register-page {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  min-height: 100vh;
+}
+
+.login-box,
+.register-box {
+  width: 360px;
+}
+@media (max-width: 576px) {
+  .login-box,
+  .register-box {
+    width: 90%;
+    margin-top: 0.5rem;
+  }
+}
+.login-box .card,
+.register-box .card {
+  margin-bottom: 0;
+}
+
+.login-card-body,
+.register-card-body {
+  padding: 20px;
+  color: var(--bs-secondary-color);
+  background-color: var(--bs-body-bg);
+  border-top: 0;
+}
+.login-card-body .input-group .form-control:focus,
+.register-card-body .input-group .form-control:focus {
+  box-shadow: none;
+}
+.login-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,
+.login-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text,
+.register-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,
+.register-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text {
+  border-color: #86b7fe;
+}
+.login-card-body .input-group .form-control.is-valid:focus,
+.register-card-body .input-group .form-control.is-valid:focus {
+  box-shadow: none;
+}
+.login-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,
+.login-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text,
+.register-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,
+.register-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text {
+  border-color: #198754;
+}
+.login-card-body .input-group .form-control.is-invalid:focus,
+.register-card-body .input-group .form-control.is-invalid:focus {
+  box-shadow: none;
+}
+.login-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text,
+.register-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text {
+  border-color: #dc3545;
+}
+.login-card-body .input-group .input-group-text,
+.register-card-body .input-group .input-group-text {
+  color: var(--bs-secondary-color);
+  background-color: transparent;
+  border-top-right-radius: 0.375rem;
+  border-bottom-right-radius: 0.375rem;
+  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+@media (prefers-reduced-motion: reduce) {
+  .login-card-body .input-group .input-group-text,
+  .register-card-body .input-group .input-group-text {
+    transition: none;
+  }
+}
+
+.login-box-msg,
+.register-box-msg {
+  padding: 0 20px 20px;
+  margin: 0;
+  text-align: center;
+}
+
+.social-auth-links {
+  margin: 10px 0;
+}
+
+.lockscreen .lockscreen-name {
+  font-weight: 600;
+  text-align: center;
+}
+.lockscreen .lockscreen-logo {
+  margin-bottom: 25px;
+  font-size: 35px;
+  font-weight: 300;
+  text-align: center;
+}
+.lockscreen .lockscreen-logo a {
+  color: var(--bs-emphasis-color);
+  text-decoration: none;
+}
+.lockscreen .lockscreen-wrapper {
+  max-width: 400px;
+  margin: 0 auto;
+  margin-top: 10%;
+}
+.lockscreen .lockscreen-item {
+  position: relative;
+  width: 290px;
+  padding: 0;
+  margin: 10px auto 30px;
+  background-color: var(--bs-body-bg);
+  border-radius: 4px;
+}
+.lockscreen .lockscreen-image {
+  position: absolute;
+  top: -25px;
+  left: -10px;
+  z-index: 10;
+  padding: 5px;
+  background-color: var(--bs-body-bg);
+  border-radius: 50%;
+}
+.lockscreen .lockscreen-image > img {
+  border-radius: 50%;
+  width: 70px;
+  height: 70px;
+}
+.lockscreen .lockscreen-credentials {
+  margin-left: 70px;
+}
+.lockscreen .lockscreen-credentials .form-control {
+  border: 0;
+}
+.lockscreen .lockscreen-credentials .btn {
+  padding: 0 10px;
+  border: 0;
+}
+.lockscreen .lockscreen-footer {
+  margin-top: 10px;
+}
+
+.img-size-64,
+.img-size-50,
+.img-size-32 {
+  height: auto;
+}
+
+.img-size-64 {
+  width: 64px;
+}
+
+.img-size-50 {
+  width: 50px;
+}
+
+.img-size-32 {
+  width: 32px;
+}
+
+/*# sourceMappingURL=adminlte.css.map */

+ 134 - 0
title_creator_app/static/css/custom.css

@@ -0,0 +1,134 @@
+@font-face {
+    font-family: 'aptos';
+    src: url('../fonts/aptos-font/Aptos.eot');
+    src: url('../fonts/aptos-font/Aptos.eot?#iefix') format('embedded-opentype'),
+        /* IE6-IE8 */
+        url('../fonts/aptos-font/Aptos.woff') format('woff'),
+        /* Modern Browsers */
+        url('../fonts/aptos-font/aptos.ttf') format('truetype');
+    /* Safari, Android, iOS */
+}
+
+body {
+    font-family: 'aptos';
+    font-size: 14px;
+}
+
+.bg-body-secondary {
+    background-color: #414042 !important;
+}
+
+.bg-body-tertiary {
+    background-color: rgba(230, 231, 232, 0.3) !important;
+}
+
+.sidebar-brand .brand-link .brand-image {
+    max-height: 40px;
+}
+
+/* .sidebar-wrapper .sidebar-menu>.nav-item>.nav-link.active:not(:hover) {
+    background-color: #808285;
+} */
+
+.logo-mini {
+    display: none;
+}
+
+.sidebar-collapse .logo-mini {
+    display: block;
+}
+
+.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice__remove {
+    border: none !important;
+    background: white !important;
+}
+
+.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice {
+    margin-left: 7px;
+    margin-left: 7px;
+}
+
+.dropdown-menu-lg {
+    max-width: none !important;
+}
+
+.app-sidebar:hover .logo-mini {
+    display: none;
+}
+
+.app-sidebar:hover .brand-text {
+    max-width: none !important;
+}
+
+.sidebar-brand{
+    height: 3.55rem;
+}
+
+.form-control{
+    font-size: 14px;
+}
+
+.fa-angle-left.rotate {
+    transform: rotate(-90deg);
+    transition: transform 0.3s;
+}
+
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year {
+    width: 45%;
+}
+
+.select2-container .select2-search--inline .select2-search__field {
+    position: absolute;
+    top: 3px;
+    font-size: 14px;
+}
+
+.hiddenSpan {
+    display: none;
+}
+
+.btn-outline-success i {
+    color: green;
+}
+
+.btn-outline-danger i {
+    color: red;
+}
+.sidebar-menu .nav-link>.right, .sidebar-menu .nav-link>p>.right {
+    position: absolute;
+    right: 1rem;
+    top: .7rem;
+}
+.breadcrumb li{
+    line-height: 1.9rem;
+}
+
+.overlay {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: rgba(0, 0, 0, 0.5);
+    /* semi-transparent */
+    display: none;
+    /* hidden by default */
+    align-items: center;
+    justify-content: center;
+    z-index: 1050;
+    /* above most elements */
+}
+
+.selectRight{
+    padding-left: 50px;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+}
+
+
+.attributeName{
+    font-weight: 400;
+    color: black;
+}

+ 309 - 0
title_creator_app/static/css/login.css

@@ -0,0 +1,309 @@
+/*
+Project Name: WFM
+Date : 05-11-2020
+Author: Amar Kholambe (amar.kholambe@luminad.com)
+*/
+body {
+    background: rgb(239, 138, 227);
+    background: url('./../images/45.jpg') no-repeat center center fixed;
+    -webkit-background-size: cover;
+    -moz-background-size: cover;
+    -o-background-size: cover;
+    background-size: cover;
+}
+
+.login {
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    background: #fff;
+}
+
+.login-container {
+    width: 800px;
+    margin: 0 auto;
+    height: 430px;
+    box-shadow: 0 0 178px #9b9b9b;
+    border: 1px solid #d1d1d1;
+    background: #f2f2f2;
+}
+
+.login-left {
+    /* background: #0070CD; */
+    background: url('./../images/left.png');
+    float: left;
+    width: 60%;
+    padding: 2 5px 30px 0;
+    height: 100%;
+    position: relative;
+    /* border-right: 1px solid #ddd; */
+    overflow: hidden;
+    background-size: cover;
+}
+
+.login-right {
+    float: left;
+    width: 48%;
+    padding: 45px 30px 0;
+    height: 100%;
+    position: absolute;
+    top:0;
+    right: 0;
+    bottom: 0;
+}
+
+.form-signin,
+.form-reset-password {
+    padding: 0 30px 0 0;
+    overflow: auto;
+    height: 410px;
+}
+
+.lbox {
+    width: 100%;
+    display: block;
+}
+
+.clogo {
+    /* width: 200px; */
+    margin: 25px 120px;
+    display: block;
+    text-align: center;
+    font-size: 35px;
+    color: #424242;
+    z-index: 1;
+    position: relative;
+}
+
+.llogo {
+    width: 115px;
+    position: absolute;
+    bottom: 10px;
+    right: 90px;
+}
+
+.lbox p {
+    margin: 320px auto auto;
+    color: #424242;
+    font-size: 12px;
+    text-align: right;
+}
+
+.form-signin-heading {
+    padding-bottom: 30px;
+    text-align: center;
+    color: #0070CD;
+    margin: 0;
+}
+
+.lang_label {
+    position: initial !important;
+    display: inline-block;
+    margin-right: 5px;
+}
+
+select {
+    width: 70px !important;
+    display: inline-block !important;
+}
+
+/* form starting stylings ------------------------------- */
+
+.login .group {
+    position: relative;
+    margin-bottom: 55px;
+}
+
+.login input {
+    padding: 5px;
+    display: block;
+    width: 100%;
+    border: none;
+    border-bottom: 1px solid #0070CD;
+    background-color: transparent;
+}
+
+.login input:focus {
+    outline: none;
+}
+
+/* LABEL ======================================= */
+
+.login label {
+    color: #999;
+    /* font-size: 18px; */
+    font-weight: normal;
+    position: absolute;
+    pointer-events: none;
+    left: 5px;
+    top: 15px;
+    transition: 0.2s ease all;
+    -moz-transition: 0.2s ease all;
+    -webkit-transition: 0.2s ease all;
+}
+
+/* active state */
+
+.login input:focus~label,
+.login input:valid~label {
+    top: -20px;
+    font-size: 14px;
+    color: #5264AE;
+}
+
+/* active state */
+
+.login input:focus~.highlight {
+    -webkit-animation: inputHighlighter 0.3s ease;
+    -moz-animation: inputHighlighter 0.3s ease;
+    animation: inputHighlighter 0.3s ease;
+}
+
+/* ANIMATIONS ================ */
+
+@-webkit-keyframes inputHighlighter {
+    from {
+        background: #5264AE;
+    }
+
+    to {
+        width: 0;
+        background: transparent;
+    }
+}
+
+@-moz-keyframes inputHighlighter {
+    from {
+        background: #5264AE;
+    }
+
+    to {
+        width: 0;
+        background: transparent;
+    }
+}
+
+@keyframes inputHighlighter {
+    from {
+        background: #5264AE;
+    }
+
+    to {
+        width: 0;
+        background: transparent;
+    }
+}
+
+.login button {
+    border-color: #0070CD;
+    color: #0070CD;
+    font-size: 18px;
+}
+
+.login button:hover,
+.login button:active,
+.login button:focus {
+    background-color: #0070CD !important;
+    color: #ffffff !important;
+}
+
+.forgotPass {
+    color: #55729c;
+    margin-top: 25px;
+    display: block;
+}
+
+.form-reset-password input {
+    padding: 8px 8px 8px 5px;
+}
+
+.form-reset-password label {
+    top: 10px;
+}
+
+.form-reset-password .group {
+    margin-bottom: 40px;
+}
+
+.header {
+    height: 200px;
+    background: #425464;
+}
+
+.footer {
+    height: 50px;
+    background: #425464;
+}
+
+@media only screen and (max-width: 900px) {
+
+    .login,
+    .login-container {
+        width: 100%;
+    }
+}
+
+@media only screen and (max-width: 768px) {
+    .login {
+        top: 150px;
+    }
+
+    .login-left,
+    .login-right {
+        width: 100%;
+        float: none;
+    }
+
+    .form-signin,
+    .form-reset-password {
+        height: 100%;
+        display: initial;
+    }
+}
+
+/* width */
+
+::-webkit-scrollbar {
+    width: 5px;
+}
+
+/* Track */
+
+::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px grey;
+    border-radius: 2px;
+}
+
+/* Handle */
+
+::-webkit-scrollbar-thumb {
+    background: #444;
+    border-radius: 2px;
+}
+
+/* Handle on hover */
+
+::-webkit-scrollbar-thumb:hover {
+    background: #333333;
+}
+
+.alert {
+    display: none;
+}
+
+.alert {
+    z-index: 9999;
+    position: fixed;
+    top: 10px;
+    width: auto;
+    left: 50%;
+    transform: translateX(-50%);
+}
+
+#triangle-topleft {
+    clip-path: polygon(0 0, 0% 100%, 100% 0);
+    background: #C4161C;
+    width: 100%;
+    height: 430px;
+}

Різницю між файлами не показано, бо вона завелика
+ 8 - 0
title_creator_app/static/css/overlayscrollbars.min.css


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
title_creator_app/static/css/select2-bootstrap4.min.css


BIN
title_creator_app/static/fonts/aptos-font/Aptos.eot


BIN
title_creator_app/static/fonts/aptos-font/Aptos.woff


BIN
title_creator_app/static/fonts/aptos-font/Aptos.woff2


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-black-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-black.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-bold.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic 2.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-extrabold.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-light-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-light.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos-semibold.ttf


BIN
title_creator_app/static/fonts/aptos-font/__MACOSX/._aptos.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-black-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-black.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-bold.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-extrabold-italic 2.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-extrabold-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-extrabold.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-light-italic.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-light.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos-semibold.ttf


BIN
title_creator_app/static/fonts/aptos-font/aptos.ttf


BIN
title_creator_app/static/images/45.jpg


BIN
title_creator_app/static/images/bg.png


BIN
title_creator_app/static/images/left.png


BIN
title_creator_app/static/images/logo-mini.png


BIN
title_creator_app/static/images/logo.png


BIN
title_creator_app/static/images/user2-160x160.jpg


+ 715 - 0
title_creator_app/static/js/adminlte.js

@@ -0,0 +1,715 @@
+/*!
+ * AdminLTE v4.0.0-beta2 (https://adminlte.io)
+ * Copyright 2014-2024 Colorlib <https://colorlib.com>
+ * Licensed under MIT (https://github.com/ColorlibHQ/AdminLTE/blob/master/LICENSE)
+ */
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+    typeof define === 'function' && define.amd ? define(['exports'], factory) :
+    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.adminlte = {}));
+})(this, (function (exports) { 'use strict';
+
+    const domContentLoadedCallbacks = [];
+    const onDOMContentLoaded = (callback) => {
+        if (document.readyState === 'loading') {
+            // add listener on the first call when the document is in loading state
+            if (!domContentLoadedCallbacks.length) {
+                document.addEventListener('DOMContentLoaded', () => {
+                    for (const callback of domContentLoadedCallbacks) {
+                        callback();
+                    }
+                });
+            }
+            domContentLoadedCallbacks.push(callback);
+        }
+        else {
+            callback();
+        }
+    };
+    /* SLIDE UP */
+    const slideUp = (target, duration = 500) => {
+        target.style.transitionProperty = 'height, margin, padding';
+        target.style.transitionDuration = `${duration}ms`;
+        target.style.boxSizing = 'border-box';
+        target.style.height = `${target.offsetHeight}px`;
+        target.style.overflow = 'hidden';
+        window.setTimeout(() => {
+            target.style.height = '0';
+            target.style.paddingTop = '0';
+            target.style.paddingBottom = '0';
+            target.style.marginTop = '0';
+            target.style.marginBottom = '0';
+        }, 1);
+        window.setTimeout(() => {
+            target.style.display = 'none';
+            target.style.removeProperty('height');
+            target.style.removeProperty('padding-top');
+            target.style.removeProperty('padding-bottom');
+            target.style.removeProperty('margin-top');
+            target.style.removeProperty('margin-bottom');
+            target.style.removeProperty('overflow');
+            target.style.removeProperty('transition-duration');
+            target.style.removeProperty('transition-property');
+        }, duration);
+    };
+    /* SLIDE DOWN */
+    const slideDown = (target, duration = 500) => {
+        target.style.removeProperty('display');
+        let { display } = window.getComputedStyle(target);
+        if (display === 'none') {
+            display = 'block';
+        }
+        target.style.display = display;
+        const height = target.offsetHeight;
+        target.style.overflow = 'hidden';
+        target.style.height = '0';
+        target.style.paddingTop = '0';
+        target.style.paddingBottom = '0';
+        target.style.marginTop = '0';
+        target.style.marginBottom = '0';
+        window.setTimeout(() => {
+            target.style.boxSizing = 'border-box';
+            target.style.transitionProperty = 'height, margin, padding';
+            target.style.transitionDuration = `${duration}ms`;
+            target.style.height = `${height}px`;
+            target.style.removeProperty('padding-top');
+            target.style.removeProperty('padding-bottom');
+            target.style.removeProperty('margin-top');
+            target.style.removeProperty('margin-bottom');
+        }, 1);
+        window.setTimeout(() => {
+            target.style.removeProperty('height');
+            target.style.removeProperty('overflow');
+            target.style.removeProperty('transition-duration');
+            target.style.removeProperty('transition-property');
+        }, duration);
+    };
+
+    /**
+     * --------------------------------------------
+     * @file AdminLTE layout.ts
+     * @description Layout for AdminLTE.
+     * @license MIT
+     * --------------------------------------------
+     */
+    /**
+     * ------------------------------------------------------------------------
+     * Constants
+     * ------------------------------------------------------------------------
+     */
+    const CLASS_NAME_HOLD_TRANSITIONS = 'hold-transition';
+    const CLASS_NAME_APP_LOADED = 'app-loaded';
+    /**
+     * Class Definition
+     * ====================================================
+     */
+    class Layout {
+        constructor(element) {
+            this._element = element;
+        }
+        holdTransition() {
+            let resizeTimer;
+            window.addEventListener('resize', () => {
+                document.body.classList.add(CLASS_NAME_HOLD_TRANSITIONS);
+                clearTimeout(resizeTimer);
+                resizeTimer = setTimeout(() => {
+                    document.body.classList.remove(CLASS_NAME_HOLD_TRANSITIONS);
+                }, 400);
+            });
+        }
+    }
+    onDOMContentLoaded(() => {
+        const data = new Layout(document.body);
+        data.holdTransition();
+        setTimeout(() => {
+            document.body.classList.add(CLASS_NAME_APP_LOADED);
+        }, 400);
+    });
+
+    /**
+     * --------------------------------------------
+     * @file AdminLTE push-menu.ts
+     * @description Push menu for AdminLTE.
+     * @license MIT
+     * --------------------------------------------
+     */
+    /**
+     * ------------------------------------------------------------------------
+     * Constants
+     * ------------------------------------------------------------------------
+     */
+    const DATA_KEY$4 = 'lte.push-menu';
+    const EVENT_KEY$4 = `.${DATA_KEY$4}`;
+    const EVENT_OPEN = `open${EVENT_KEY$4}`;
+    const EVENT_COLLAPSE = `collapse${EVENT_KEY$4}`;
+    const CLASS_NAME_SIDEBAR_MINI = 'sidebar-mini';
+    const CLASS_NAME_SIDEBAR_COLLAPSE = 'sidebar-collapse';
+    const CLASS_NAME_SIDEBAR_OPEN = 'sidebar-open';
+    const CLASS_NAME_SIDEBAR_EXPAND = 'sidebar-expand';
+    const CLASS_NAME_SIDEBAR_OVERLAY = 'sidebar-overlay';
+    const CLASS_NAME_MENU_OPEN$1 = 'menu-open';
+    const SELECTOR_APP_SIDEBAR = '.app-sidebar';
+    const SELECTOR_SIDEBAR_MENU = '.sidebar-menu';
+    const SELECTOR_NAV_ITEM$1 = '.nav-item';
+    const SELECTOR_NAV_TREEVIEW = '.nav-treeview';
+    const SELECTOR_APP_WRAPPER = '.app-wrapper';
+    const SELECTOR_SIDEBAR_EXPAND = `[class*="${CLASS_NAME_SIDEBAR_EXPAND}"]`;
+    const SELECTOR_SIDEBAR_TOGGLE = '[data-lte-toggle="sidebar"]';
+    const Defaults = {
+        sidebarBreakpoint: 992
+    };
+    /**
+     * Class Definition
+     * ====================================================
+     */
+    class PushMenu {
+        constructor(element, config) {
+            this._element = element;
+            this._config = Object.assign(Object.assign({}, Defaults), config);
+        }
+        // TODO
+        menusClose() {
+            const navTreeview = document.querySelectorAll(SELECTOR_NAV_TREEVIEW);
+            navTreeview.forEach(navTree => {
+                navTree.style.removeProperty('display');
+                navTree.style.removeProperty('height');
+            });
+            const navSidebar = document.querySelector(SELECTOR_SIDEBAR_MENU);
+            const navItem = navSidebar === null || navSidebar === void 0 ? void 0 : navSidebar.querySelectorAll(SELECTOR_NAV_ITEM$1);
+            if (navItem) {
+                navItem.forEach(navI => {
+                    navI.classList.remove(CLASS_NAME_MENU_OPEN$1);
+                });
+            }
+        }
+        expand() {
+            const event = new Event(EVENT_OPEN);
+            document.body.classList.remove(CLASS_NAME_SIDEBAR_COLLAPSE);
+            document.body.classList.add(CLASS_NAME_SIDEBAR_OPEN);
+            this._element.dispatchEvent(event);
+        }
+        collapse() {
+            const event = new Event(EVENT_COLLAPSE);
+            document.body.classList.remove(CLASS_NAME_SIDEBAR_OPEN);
+            document.body.classList.add(CLASS_NAME_SIDEBAR_COLLAPSE);
+            this._element.dispatchEvent(event);
+        }
+        addSidebarBreakPoint() {
+            var _a, _b, _c;
+            const sidebarExpandList = (_b = (_a = document.querySelector(SELECTOR_SIDEBAR_EXPAND)) === null || _a === void 0 ? void 0 : _a.classList) !== null && _b !== void 0 ? _b : [];
+            const sidebarExpand = (_c = Array.from(sidebarExpandList).find(className => className.startsWith(CLASS_NAME_SIDEBAR_EXPAND))) !== null && _c !== void 0 ? _c : '';
+            const sidebar = document.getElementsByClassName(sidebarExpand)[0];
+            const sidebarContent = window.getComputedStyle(sidebar, '::before').getPropertyValue('content');
+            this._config = Object.assign(Object.assign({}, this._config), { sidebarBreakpoint: Number(sidebarContent.replace(/[^\d.-]/g, '')) });
+            if (window.innerWidth <= this._config.sidebarBreakpoint) {
+                this.collapse();
+            }
+            else {
+                if (!document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI)) {
+                    this.expand();
+                }
+                if (document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI) && document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)) {
+                    this.collapse();
+                }
+            }
+        }
+        toggle() {
+            if (document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)) {
+                this.expand();
+            }
+            else {
+                this.collapse();
+            }
+        }
+        init() {
+            this.addSidebarBreakPoint();
+        }
+    }
+    /**
+     * ------------------------------------------------------------------------
+     * Data Api implementation
+     * ------------------------------------------------------------------------
+     */
+    onDOMContentLoaded(() => {
+        var _a;
+        const sidebar = document === null || document === void 0 ? void 0 : document.querySelector(SELECTOR_APP_SIDEBAR);
+        if (sidebar) {
+            const data = new PushMenu(sidebar, Defaults);
+            data.init();
+            window.addEventListener('resize', () => {
+                data.init();
+            });
+        }
+        const sidebarOverlay = document.createElement('div');
+        sidebarOverlay.className = CLASS_NAME_SIDEBAR_OVERLAY;
+        (_a = document.querySelector(SELECTOR_APP_WRAPPER)) === null || _a === void 0 ? void 0 : _a.append(sidebarOverlay);
+        sidebarOverlay.addEventListener('touchstart', event => {
+            event.preventDefault();
+            const target = event.currentTarget;
+            const data = new PushMenu(target, Defaults);
+            data.collapse();
+        }, { passive: true });
+        sidebarOverlay.addEventListener('click', event => {
+            event.preventDefault();
+            const target = event.currentTarget;
+            const data = new PushMenu(target, Defaults);
+            data.collapse();
+        });
+        const fullBtn = document.querySelectorAll(SELECTOR_SIDEBAR_TOGGLE);
+        fullBtn.forEach(btn => {
+            btn.addEventListener('click', event => {
+                event.preventDefault();
+                let button = event.currentTarget;
+                if ((button === null || button === void 0 ? void 0 : button.dataset.lteToggle) !== 'sidebar') {
+                    button = button === null || button === void 0 ? void 0 : button.closest(SELECTOR_SIDEBAR_TOGGLE);
+                }
+                if (button) {
+                    event === null || event === void 0 ? void 0 : event.preventDefault();
+                    const data = new PushMenu(button, Defaults);
+                    data.toggle();
+                }
+            });
+        });
+    });
+
+    /**
+     * --------------------------------------------
+     * @file AdminLTE treeview.ts
+     * @description Treeview plugin for AdminLTE.
+     * @license MIT
+     * --------------------------------------------
+     */
+    /**
+     * ------------------------------------------------------------------------
+     * Constants
+     * ------------------------------------------------------------------------
+     */
+    // const NAME = 'Treeview'
+    const DATA_KEY$3 = 'lte.treeview';
+    const EVENT_KEY$3 = `.${DATA_KEY$3}`;
+    const EVENT_EXPANDED$2 = `expanded${EVENT_KEY$3}`;
+    const EVENT_COLLAPSED$2 = `collapsed${EVENT_KEY$3}`;
+    // const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`
+    const CLASS_NAME_MENU_OPEN = 'menu-open';
+    const SELECTOR_NAV_ITEM = '.nav-item';
+    const SELECTOR_NAV_LINK = '.nav-link';
+    const SELECTOR_TREEVIEW_MENU = '.nav-treeview';
+    const SELECTOR_DATA_TOGGLE$1 = '[data-lte-toggle="treeview"]';
+    const Default$1 = {
+        animationSpeed: 300,
+        accordion: true
+    };
+    /**
+     * Class Definition
+     * ====================================================
+     */
+    class Treeview {
+        constructor(element, config) {
+            this._element = element;
+            this._config = Object.assign(Object.assign({}, Default$1), config);
+        }
+        open() {
+            var _a, _b;
+            const event = new Event(EVENT_EXPANDED$2);
+            if (this._config.accordion) {
+                const openMenuList = (_a = this._element.parentElement) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_NAV_ITEM}.${CLASS_NAME_MENU_OPEN}`);
+                openMenuList === null || openMenuList === void 0 ? void 0 : openMenuList.forEach(openMenu => {
+                    if (openMenu !== this._element.parentElement) {
+                        openMenu.classList.remove(CLASS_NAME_MENU_OPEN);
+                        const childElement = openMenu === null || openMenu === void 0 ? void 0 : openMenu.querySelector(SELECTOR_TREEVIEW_MENU);
+                        if (childElement) {
+                            slideUp(childElement, this._config.animationSpeed);
+                        }
+                    }
+                });
+            }
+            this._element.classList.add(CLASS_NAME_MENU_OPEN);
+            const childElement = (_b = this._element) === null || _b === void 0 ? void 0 : _b.querySelector(SELECTOR_TREEVIEW_MENU);
+            if (childElement) {
+                slideDown(childElement, this._config.animationSpeed);
+            }
+            this._element.dispatchEvent(event);
+        }
+        close() {
+            var _a;
+            const event = new Event(EVENT_COLLAPSED$2);
+            this._element.classList.remove(CLASS_NAME_MENU_OPEN);
+            const childElement = (_a = this._element) === null || _a === void 0 ? void 0 : _a.querySelector(SELECTOR_TREEVIEW_MENU);
+            if (childElement) {
+                slideUp(childElement, this._config.animationSpeed);
+            }
+            this._element.dispatchEvent(event);
+        }
+        toggle() {
+            if (this._element.classList.contains(CLASS_NAME_MENU_OPEN)) {
+                this.close();
+            }
+            else {
+                this.open();
+            }
+        }
+    }
+    /**
+     * ------------------------------------------------------------------------
+     * Data Api implementation
+     * ------------------------------------------------------------------------
+     */
+    onDOMContentLoaded(() => {
+        const button = document.querySelectorAll(SELECTOR_DATA_TOGGLE$1);
+        button.forEach(btn => {
+            btn.addEventListener('click', event => {
+                const target = event.target;
+                const targetItem = target.closest(SELECTOR_NAV_ITEM);
+                const targetLink = target.closest(SELECTOR_NAV_LINK);
+                if ((target === null || target === void 0 ? void 0 : target.getAttribute('href')) === '#' || (targetLink === null || targetLink === void 0 ? void 0 : targetLink.getAttribute('href')) === '#') {
+                    event.preventDefault();
+                }
+                if (targetItem) {
+                    const data = new Treeview(targetItem, Default$1);
+                    data.toggle();
+                }
+            });
+        });
+    });
+
+    /**
+     * --------------------------------------------
+     * @file AdminLTE direct-chat.ts
+     * @description Direct chat for AdminLTE.
+     * @license MIT
+     * --------------------------------------------
+     */
+    /**
+     * Constants
+     * ====================================================
+     */
+    const DATA_KEY$2 = 'lte.direct-chat';
+    const EVENT_KEY$2 = `.${DATA_KEY$2}`;
+    const EVENT_EXPANDED$1 = `expanded${EVENT_KEY$2}`;
+    const EVENT_COLLAPSED$1 = `collapsed${EVENT_KEY$2}`;
+    const SELECTOR_DATA_TOGGLE = '[data-lte-toggle="chat-pane"]';
+    const SELECTOR_DIRECT_CHAT = '.direct-chat';
+    const CLASS_NAME_DIRECT_CHAT_OPEN = 'direct-chat-contacts-open';
+    /**
+     * Class Definition
+     * ====================================================
+     */
+    class DirectChat {
+        constructor(element) {
+            this._element = element;
+        }
+        toggle() {
+            if (this._element.classList.contains(CLASS_NAME_DIRECT_CHAT_OPEN)) {
+                const event = new Event(EVENT_COLLAPSED$1);
+                this._element.classList.remove(CLASS_NAME_DIRECT_CHAT_OPEN);
+                this._element.dispatchEvent(event);
+            }
+            else {
+                const event = new Event(EVENT_EXPANDED$1);
+                this._element.classList.add(CLASS_NAME_DIRECT_CHAT_OPEN);
+                this._element.dispatchEvent(event);
+            }
+        }
+    }
+    /**
+     *
+     * Data Api implementation
+     * ====================================================
+     */
+    onDOMContentLoaded(() => {
+        const button = document.querySelectorAll(SELECTOR_DATA_TOGGLE);
+        button.forEach(btn => {
+            btn.addEventListener('click', event => {
+                event.preventDefault();
+                const target = event.target;
+                const chatPane = target.closest(SELECTOR_DIRECT_CHAT);
+                if (chatPane) {
+                    const data = new DirectChat(chatPane);
+                    data.toggle();
+                }
+            });
+        });
+    });
+
+    /**
+     * --------------------------------------------
+     * @file AdminLTE card-widget.ts
+     * @description Card widget for AdminLTE.
+     * @license MIT
+     * --------------------------------------------
+     */
+    /**
+     * Constants
+     * ====================================================
+     */
+    const DATA_KEY$1 = 'lte.card-widget';
+    const EVENT_KEY$1 = `.${DATA_KEY$1}`;
+    const EVENT_COLLAPSED = `collapsed${EVENT_KEY$1}`;
+    const EVENT_EXPANDED = `expanded${EVENT_KEY$1}`;
+    const EVENT_REMOVE = `remove${EVENT_KEY$1}`;
+    const EVENT_MAXIMIZED$1 = `maximized${EVENT_KEY$1}`;
+    const EVENT_MINIMIZED$1 = `minimized${EVENT_KEY$1}`;
+    const CLASS_NAME_CARD = 'card';
+    const CLASS_NAME_COLLAPSED = 'collapsed-card';
+    const CLASS_NAME_COLLAPSING = 'collapsing-card';
+    const CLASS_NAME_EXPANDING = 'expanding-card';
+    const CLASS_NAME_WAS_COLLAPSED = 'was-collapsed';
+    const CLASS_NAME_MAXIMIZED = 'maximized-card';
+    const SELECTOR_DATA_REMOVE = '[data-lte-toggle="card-remove"]';
+    const SELECTOR_DATA_COLLAPSE = '[data-lte-toggle="card-collapse"]';
+    const SELECTOR_DATA_MAXIMIZE = '[data-lte-toggle="card-maximize"]';
+    const SELECTOR_CARD = `.${CLASS_NAME_CARD}`;
+    const SELECTOR_CARD_BODY = '.card-body';
+    const SELECTOR_CARD_FOOTER = '.card-footer';
+    const Default = {
+        animationSpeed: 500,
+        collapseTrigger: SELECTOR_DATA_COLLAPSE,
+        removeTrigger: SELECTOR_DATA_REMOVE,
+        maximizeTrigger: SELECTOR_DATA_MAXIMIZE
+    };
+    class CardWidget {
+        constructor(element, config) {
+            this._element = element;
+            this._parent = element.closest(SELECTOR_CARD);
+            if (element.classList.contains(CLASS_NAME_CARD)) {
+                this._parent = element;
+            }
+            this._config = Object.assign(Object.assign({}, Default), config);
+        }
+        collapse() {
+            var _a, _b;
+            const event = new Event(EVENT_COLLAPSED);
+            if (this._parent) {
+                this._parent.classList.add(CLASS_NAME_COLLAPSING);
+                const elm = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`);
+                elm.forEach(el => {
+                    if (el instanceof HTMLElement) {
+                        slideUp(el, this._config.animationSpeed);
+                    }
+                });
+                setTimeout(() => {
+                    if (this._parent) {
+                        this._parent.classList.add(CLASS_NAME_COLLAPSED);
+                        this._parent.classList.remove(CLASS_NAME_COLLAPSING);
+                    }
+                }, this._config.animationSpeed);
+            }
+            (_b = this._element) === null || _b === void 0 ? void 0 : _b.dispatchEvent(event);
+        }
+        expand() {
+            var _a, _b;
+            const event = new Event(EVENT_EXPANDED);
+            if (this._parent) {
+                this._parent.classList.add(CLASS_NAME_EXPANDING);
+                const elm = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`);
+                elm.forEach(el => {
+                    if (el instanceof HTMLElement) {
+                        slideDown(el, this._config.animationSpeed);
+                    }
+                });
+                setTimeout(() => {
+                    if (this._parent) {
+                        this._parent.classList.remove(CLASS_NAME_COLLAPSED);
+                        this._parent.classList.remove(CLASS_NAME_EXPANDING);
+                    }
+                }, this._config.animationSpeed);
+            }
+            (_b = this._element) === null || _b === void 0 ? void 0 : _b.dispatchEvent(event);
+        }
+        remove() {
+            var _a;
+            const event = new Event(EVENT_REMOVE);
+            if (this._parent) {
+                slideUp(this._parent, this._config.animationSpeed);
+            }
+            (_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
+        }
+        toggle() {
+            var _a;
+            if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_COLLAPSED)) {
+                this.expand();
+                return;
+            }
+            this.collapse();
+        }
+        maximize() {
+            var _a;
+            const event = new Event(EVENT_MAXIMIZED$1);
+            if (this._parent) {
+                this._parent.style.height = `${this._parent.offsetHeight}px`;
+                this._parent.style.width = `${this._parent.offsetWidth}px`;
+                this._parent.style.transition = 'all .15s';
+                setTimeout(() => {
+                    const htmlTag = document.querySelector('html');
+                    if (htmlTag) {
+                        htmlTag.classList.add(CLASS_NAME_MAXIMIZED);
+                    }
+                    if (this._parent) {
+                        this._parent.classList.add(CLASS_NAME_MAXIMIZED);
+                        if (this._parent.classList.contains(CLASS_NAME_COLLAPSED)) {
+                            this._parent.classList.add(CLASS_NAME_WAS_COLLAPSED);
+                        }
+                    }
+                }, 150);
+            }
+            (_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
+        }
+        minimize() {
+            var _a;
+            const event = new Event(EVENT_MINIMIZED$1);
+            if (this._parent) {
+                this._parent.style.height = 'auto';
+                this._parent.style.width = 'auto';
+                this._parent.style.transition = 'all .15s';
+                setTimeout(() => {
+                    var _a;
+                    const htmlTag = document.querySelector('html');
+                    if (htmlTag) {
+                        htmlTag.classList.remove(CLASS_NAME_MAXIMIZED);
+                    }
+                    if (this._parent) {
+                        this._parent.classList.remove(CLASS_NAME_MAXIMIZED);
+                        if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_WAS_COLLAPSED)) {
+                            this._parent.classList.remove(CLASS_NAME_WAS_COLLAPSED);
+                        }
+                    }
+                }, 10);
+            }
+            (_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
+        }
+        toggleMaximize() {
+            var _a;
+            if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_MAXIMIZED)) {
+                this.minimize();
+                return;
+            }
+            this.maximize();
+        }
+    }
+    /**
+     *
+     * Data Api implementation
+     * ====================================================
+     */
+    onDOMContentLoaded(() => {
+        const collapseBtn = document.querySelectorAll(SELECTOR_DATA_COLLAPSE);
+        collapseBtn.forEach(btn => {
+            btn.addEventListener('click', event => {
+                event.preventDefault();
+                const target = event.target;
+                const data = new CardWidget(target, Default);
+                data.toggle();
+            });
+        });
+        const removeBtn = document.querySelectorAll(SELECTOR_DATA_REMOVE);
+        removeBtn.forEach(btn => {
+            btn.addEventListener('click', event => {
+                event.preventDefault();
+                const target = event.target;
+                const data = new CardWidget(target, Default);
+                data.remove();
+            });
+        });
+        const maxBtn = document.querySelectorAll(SELECTOR_DATA_MAXIMIZE);
+        maxBtn.forEach(btn => {
+            btn.addEventListener('click', event => {
+                event.preventDefault();
+                const target = event.target;
+                const data = new CardWidget(target, Default);
+                data.toggleMaximize();
+            });
+        });
+    });
+
+    /**
+     * --------------------------------------------
+     * @file AdminLTE fullscreen.ts
+     * @description Fullscreen plugin for AdminLTE.
+     * @license MIT
+     * --------------------------------------------
+     */
+    /**
+     * Constants
+     * ============================================================================
+     */
+    const DATA_KEY = 'lte.fullscreen';
+    const EVENT_KEY = `.${DATA_KEY}`;
+    const EVENT_MAXIMIZED = `maximized${EVENT_KEY}`;
+    const EVENT_MINIMIZED = `minimized${EVENT_KEY}`;
+    const SELECTOR_FULLSCREEN_TOGGLE = '[data-lte-toggle="fullscreen"]';
+    const SELECTOR_MAXIMIZE_ICON = '[data-lte-icon="maximize"]';
+    const SELECTOR_MINIMIZE_ICON = '[data-lte-icon="minimize"]';
+    /**
+     * Class Definition.
+     * ============================================================================
+     */
+    class FullScreen {
+        constructor(element, config) {
+            this._element = element;
+            this._config = config;
+        }
+        inFullScreen() {
+            const event = new Event(EVENT_MAXIMIZED);
+            const iconMaximize = document.querySelector(SELECTOR_MAXIMIZE_ICON);
+            const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
+            void document.documentElement.requestFullscreen();
+            if (iconMaximize) {
+                iconMaximize.style.display = 'none';
+            }
+            if (iconMinimize) {
+                iconMinimize.style.display = 'block';
+            }
+            this._element.dispatchEvent(event);
+        }
+        outFullscreen() {
+            const event = new Event(EVENT_MINIMIZED);
+            const iconMaximize = document.querySelector(SELECTOR_MAXIMIZE_ICON);
+            const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
+            void document.exitFullscreen();
+            if (iconMaximize) {
+                iconMaximize.style.display = 'block';
+            }
+            if (iconMinimize) {
+                iconMinimize.style.display = 'none';
+            }
+            this._element.dispatchEvent(event);
+        }
+        toggleFullScreen() {
+            if (document.fullscreenEnabled) {
+                if (document.fullscreenElement) {
+                    this.outFullscreen();
+                }
+                else {
+                    this.inFullScreen();
+                }
+            }
+        }
+    }
+    /**
+     * Data Api implementation
+     * ============================================================================
+     */
+    onDOMContentLoaded(() => {
+        const buttons = document.querySelectorAll(SELECTOR_FULLSCREEN_TOGGLE);
+        buttons.forEach(btn => {
+            btn.addEventListener('click', event => {
+                event.preventDefault();
+                const target = event.target;
+                const button = target.closest(SELECTOR_FULLSCREEN_TOGGLE);
+                if (button) {
+                    const data = new FullScreen(button, undefined);
+                    data.toggleFullScreen();
+                }
+            });
+        });
+    });
+
+    exports.CardWidget = CardWidget;
+    exports.DirectChat = DirectChat;
+    exports.FullScreen = FullScreen;
+    exports.Layout = Layout;
+    exports.PushMenu = PushMenu;
+    exports.Treeview = Treeview;
+
+}));
+//# sourceMappingURL=adminlte.js.map

+ 1214 - 0
title_creator_app/static/js/attr-extraction-bkp.js

@@ -0,0 +1,1214 @@
+jQuery.noConflict(); // Release $ to other libraries
+// console.log(typeof jQuery);
+// $ = jQuery;
+
+// --- Config ---
+const UPLOAD_API_URL = '/attr/products/upload-excel/'; // TODO: set to your upload endpoint
+const ACCEPT_TYPES = '*'; // e.g., 'image/*,.csv,.xlsx'
+
+
+const thresholdInput = document.getElementById('thresholdRange');
+const thresholdValueDisplay = document.getElementById('thresholdValue');
+
+
+var PRODUCT_BASE = [
+    // { id: 1, item_id: 'SKU001', product_name: "Levi's Jeans", product_long_description: 'Classic blue denim jeans with straight fit.', product_short_description: 'Blue denim jeans.', product_type: 'Clothing', image_path: 'media/products/jeans.jpg', image: 'http://127.0.0.1:8000/media/products/jeans.png' },
+    // { id: 2, item_id: 'SKU002', product_name: 'Adidas Running Shoes', product_long_description: 'Lightweight running shoes with breathable mesh and cushioned sole.', product_short_description: "Men's running shoes.", product_type: 'Footwear', image_path: 'media/products/shoes.png', image: 'http://127.0.0.1:8000/media/products/shoes.png' },
+    // { id: 3, item_id: 'SKU003', product_name: 'Nike Sports T-Shirt', product_long_description: 'Moisture-wicking sports tee ideal for training and outdoor activities.', product_short_description: 'Performance t-shirt.', product_type: 'Clothing', image_path: 'media/products/tshirt.png', image: 'http://127.0.0.1:8000/media/products/tshirt.png' },
+    // { id: 4, item_id: 'SKU004', product_name: 'Puma Hoodie', product_long_description: 'Soft fleece hoodie with kangaroo pocket and adjustable drawstring.', product_short_description: 'Casual hoodie.', product_type: 'Clothing', image_path: 'media/products/hoodie.png', image: 'http://127.0.0.1:8000/media/products/hoodie.png' },
+    // { id: 5, item_id: 'SKU005', product_name: 'Ray-Ban Sunglasses', product_long_description: 'Classic aviator sunglasses with UV protection lenses.', product_short_description: 'Aviator sunglasses.', product_type: 'Accessories', image_path: 'media/products/sunglasses.png', image: 'http://127.0.0.1:8000/media/products/sunglasses.png' }
+];
+// --- Data ---
+const mediaUrl = "./../";
+
+document.addEventListener('DOMContentLoaded', () => {
+    jQuery('#full-page-loader').show();  
+    fetch('/attr/products', {
+        method: 'GET', // or 'POST' if your API expects POST
+        headers: {
+            'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || ''
+        }
+    })
+    .then(response => response.json())
+    .then(data => {
+        // console.log("data",data);
+        // --- Wire up ---
+        PRODUCT_BASE = data;
+        PRODUCT_BASE = PRODUCT_BASE.map((d)=>{return {...d,mandatoryAttributes:["color","size"]}});
+        // console.log("PRODUCT_BASE",PRODUCT_BASE);
+        if(PRODUCT_BASE.length > 0){
+            $('#paginationBar').style.display = 'block';
+        }
+        renderProducts();
+        getAtributeList();
+        document.getElementById('btnSubmit').addEventListener('click', submitAttributes);
+        document.getElementById('btnReset').addEventListener('click', resetAll);
+        // document.getElementById('btnSelectAll').addEventListener('click', () => {
+        // if (selectedIds.size === PRODUCT_BASE.length) { selectedIds.clear(); } else { selectedIds = new Set(PRODUCT_BASE.map(p => p.id)); }
+        // // renderProducts();
+        // });
+
+        // Replace your existing Select All listener with this:
+          document.getElementById('btnSelectAll').addEventListener('click', () => {
+            // Use the container for the active layout
+            const container = (layoutMode === 'cards')
+              ? document.getElementById('cardsContainer')
+              : document.getElementById('tableContainer');
+
+            // Collect all visible checkboxes
+            const boxes = Array.from(container.querySelectorAll('input[type="checkbox"]'));
+
+            // If every visible checkbox is already checked, we'll deselect; otherwise select all
+            const allChecked = boxes.length > 0 && boxes.every(cb => cb.checked);
+
+            boxes.forEach(cb => {
+              const target = !allChecked; // true to select, false to deselect
+              if (cb.checked !== target) {
+                cb.checked = target;
+                // Trigger your existing "change" handler so selectedIds & row .selected class update
+                cb.dispatchEvent(new Event('change', { bubbles: true }));
+              }
+            });
+
+            // Update the selection pill text (doesn't re-render the list)
+            updateSelectionInfo();
+          });
+        document.getElementById('btnCards').addEventListener('click', () => setLayout('cards'));
+        document.getElementById('btnTable').addEventListener('click', () => setLayout('table'));
+        jQuery('#full-page-loader').hide();
+        // if (data.success) {
+        // }
+    });
+});
+
+var FAKE_API_RESPONSE = {
+    // results: [
+    // { product_id: 'SKU001', mandatory: { 'Clothing Neck Style': 'V-Neck', 'Clothing Top Style': 'Pullover', 'Condition': 'New', 'T-Shirt Type': 'Classic T-Shirt' }, additional: { 'Material': 'Turkish Pima Cotton', 'Size': 'Large', 'Color': 'Blue', 'Brand': 'Sierra', 'Fabric Type': 'Soft & Breathable', 'Fabric Composition': '95% Turkish Pima cotton', 'Care Instructions': 'Machine Washable', 'Sizes Available': 'S-XL' } },
+    // { product_id: 'SKU002', mandatory: { 'Shoe Type': 'Running', 'Closure': 'Lace-Up', 'Condition': 'New', 'Gender': 'Men' }, additional: { 'Upper Material': 'Engineered Mesh', 'Midsole': 'EVA Foam', 'Outsole': 'Rubber', 'Color': 'Black/White', 'Brand': 'Adidas', 'Size': 'UK 9', 'Care Instructions': 'Surface Clean' } },
+    // { product_id: 'SKU003', mandatory: { 'Clothing Neck Style': 'Crew Neck', 'Sleeve Length': 'Short Sleeve', 'Condition': 'New', 'T-Shirt Type': 'Performance' }, additional: { 'Material': 'Polyester Blend', 'Color': 'Red', 'Brand': 'Nike', 'Size': 'Medium', 'Fabric Technology': 'Dri-FIT', 'Care Instructions': 'Machine Wash Cold' } },
+    // { product_id: 'SKU004', mandatory: { 'Clothing Top Style': 'Hoodie', 'Closure': 'Pullover', 'Condition': 'New', 'Fit': 'Relaxed' }, additional: { 'Material': 'Cotton Fleece', 'Color': 'Charcoal', 'Brand': 'Puma', 'Size': 'Large', 'Care Instructions': 'Machine Wash Warm' } },
+    // { product_id: 'SKU005', mandatory: { 'Accessory Type': 'Sunglasses', 'Frame Style': 'Aviator', 'Condition': 'New', 'Lens Protection': 'UV 400' }, additional: { 'Frame Material': 'Metal', 'Lens Color': 'Green', 'Brand': 'Ray-Ban', 'Size': 'Standard', 'Case Included': 'Yes', 'Care Instructions': 'Clean with microfiber' } }
+    // ],
+    // total_products: 5,
+    // successful: 5,
+    // failed: 0
+};
+
+// --- State ---
+let selectedIds = new Set();
+// NEW: Array of objects { item_id: string, mandatory_attrs: { [attribute_name]: string[] } }
+let selectedProductsWithAttributes = [];
+let selectedAttributes = new Array();
+const lastSeen = new Map(); // per-product memory for NEW highlighting (product_id -> maps)
+let layoutMode = 'table'; // 'cards' | 'table'
+
+// --- Helpers ---
+const $ = (sel) => document.querySelector(sel);
+const el = (tag, cls) => { const e = document.createElement(tag); if (cls) e.className = cls; return e; }
+
+function updateSelectionInfo() {
+    const pill = $('#selectionInfo');
+    const total = PRODUCT_BASE.length;
+    // const count = selectedIds.size;
+    const count = selectedProductsWithAttributes.length;
+    pill.textContent = count === 0 ? 'No products selected' : `${count} of ${total} selected`;
+}
+
+function setChecked(id, checked) { if (checked) selectedIds.add(id); else selectedIds.delete(id); updateSelectionInfo(); }
+// function setCheckedAttributes(id,attribute, checked) { if (checked) selectedAttributes.add({id: [attribute]}); else selectedIds.delete({id:[attribute]}); updateSelectionInfo(); }
+function formatString(str) {
+  return str
+    // Replace underscores with spaces
+    .replace(/_/g, ' ')
+    // Insert a space before any uppercase letter (except at the start)
+    .replace(/([a-z])([A-Z])/g, '$1 $2')
+    // Capitalize the first letter
+    .replace(/^./, char => char.toUpperCase());
+}
+
+// --- Chips rendering ---
+function renderChips(container, obj, memoryMap) {
+    container.innerHTML = '';
+    let count = 0;
+    Object.entries(obj || {}).forEach(([k, v]) => {
+    const chip = el('span', 'chip');
+    const kEl = el('span', 'k'); kEl.textContent = formatString(k) + ':';
+    // console.log("v",v);
+    let resVal = "";
+    let sourceVal = "";
+    if(v instanceof Array){
+        resVal  = String(v.map(v => formatString(v.value)).join(", "));
+        sourceVal = String(formatString(v[0]?.source));
+        const vEl = el('span', 'v'); vEl.textContent = ' ' + resVal  +' (' + sourceVal + ')';
+        chip.appendChild(kEl); chip.appendChild(vEl);
+    }
+    // console.log("k",k);
+    if(v instanceof Array){
+    const was = memoryMap.get(k);
+    if (was === undefined || was !== v) chip.classList.add('new');
+    container.appendChild(chip);
+    memoryMap.set(k, v);
+    
+    count++;
+    }
+    });
+    return count;
+}
+
+function findApiResultForProduct(p, index, api) { return api.results?.find(r => r.product_id === p.item_id) || api.results?.[index] || null; }
+
+// --- Cards layout ---
+function createProductCard(p) {
+    const row = el('div', 'product');
+    // Check selection using the new helper
+    if (isProductSelected(p.item_id)) row.classList.add('selected');
+    // if (selectedIds.has(p.item_id)) row.classList.add('selected');
+
+    const left = el('div', 'thumb');
+    const img = new Image(); img.src = p.image_path || p.image || '';
+    // console.log("image path",p.image_path);
+     img.alt = `${p.product_name} image`;
+    // console.log("img",img);
+    // img.onerror = () => { img.remove(); const fb = el('div', 'fallback'); fb.textContent = (p.product_name || 'Product').split(' ').map(w => w[0]).slice(0,2).join('').toUpperCase(); left.appendChild(fb); };
+    img.onerror = () => { img.src = mediaUrl+"media/images/no-product.png" };
+    left.appendChild(img);
+
+    const mid = el('div', 'meta');
+    const name = el('div', 'name'); name.textContent = p.product_name || '—';
+    const desc = el('div', 'desc'); desc.innerHTML = p.product_short_description || '';
+    const badges = el('div', 'badges');
+    const sku = el('span', 'pill'); sku.textContent = `SKU: ${p.item_id || '—'}`; badges.appendChild(sku);
+    const type = el('span', 'pill'); type.textContent = p.product_type || '—'; badges.appendChild(type);
+    const long = el('div', 'desc'); long.innerHTML = p.product_long_description || ''; long.style.marginTop = '4px';
+    mid.appendChild(name); mid.appendChild(desc); mid.appendChild(badges); mid.appendChild(long);
+
+    // Helper function to create the chip UI for attributes
+    function createAttributeChips(p, attr, initialSelected, isMandatory, updateCallback) {
+        const wrapper = el('div', 'attribute-chip-group');
+        wrapper.dataset.attrName = attr.attribute_name;
+        wrapper.innerHTML = `<p class="attribute-header">${attr.attribute_name} (${isMandatory ? 'Mandatory' : 'Optional'}):</p>`;
+
+        const chipContainer = el('div', 'chips-container');
+
+        attr.possible_values.forEach(value => {
+            const chip = el('label', 'attribute-chip');
+            
+            // Checkbox input is hidden, but drives the selection state
+            const checkbox = document.createElement('input');
+            checkbox.type = 'checkbox';
+            checkbox.value = value;
+            checkbox.name = `${p.item_id}-${attr.attribute_name}`;
+            
+            // Set initial state
+            checkbox.checked = initialSelected.includes(value); 
+            
+            // The visual part of the chip
+            const span = el('span');
+            span.textContent = value;
+            
+            chip.appendChild(checkbox);
+            chip.appendChild(span);
+            chipContainer.appendChild(chip);
+        });
+        
+        // Use event delegation on the container for performance
+        chipContainer.addEventListener('change', updateCallback);
+
+        wrapper.appendChild(chipContainer);
+        return wrapper;
+    }
+
+    // --- Main Select Checkbox (Product Selection) ---
+    const right = el('label', 'select');
+    const cb = document.createElement('input'); cb.type = 'checkbox';
+    cb.checked = isProductSelected(p.item_id);
+    const lbl = el('span'); lbl.textContent = 'Select Product';
+    right.appendChild(cb); right.appendChild(lbl);
+
+    
+    // --- Dynamic Attribute Selects ---
+    const attrContainer = el('div', 'attribute-selectors');
+if(p.product_type_details.length > 0){
+    // Find all mandatory and non-mandatory attributes for this product
+    const mandatoryAttributes = p.product_type_details?.filter(a => a.is_mandatory === 'Yes') || [];
+    const optionalAttributes = p.product_type_details?.filter(a => a.is_mandatory !== 'Yes') || [];
+
+    // Helper to update the main state object with all current selections
+    const updateProductState = () => {
+        const isSelected = cb.checked;
+        const currentSelections = {};
+
+        if (isSelected) {
+            // Iterate over all attribute groups (Mandatory and Optional)
+            attrContainer.querySelectorAll('.attribute-chip-group').forEach(group => {
+                const attrName = group.dataset.attrName;
+                
+                // Collect selected chip values
+                const selectedOptions = Array.from(group.querySelectorAll('input[type="checkbox"]:checked'))
+                    .map(checkbox => checkbox.value);
+
+                if (selectedOptions.length > 0) {
+                    currentSelections[attrName] = selectedOptions;
+                }
+            });
+        }
+        
+        toggleProductSelection(p.item_id, isSelected, currentSelections);
+        row.classList.toggle('selected', isSelected);
+    };
+
+    // Attach listener to main checkbox
+    cb.addEventListener('change', () => {
+        attrContainer.classList.toggle('disabled', !cb.checked);
+        updateProductState();
+    });
+    
+    // --- Render Mandatory Attributes ---
+    if (mandatoryAttributes.length > 0) {
+        const manTitle = el('p', "pSelectRight mandatory-title");
+        manTitle.innerHTML = "Mandatory Attributes:";
+        attrContainer.appendChild(manTitle);
+
+        mandatoryAttributes.forEach(attr => {
+            const initialSelected = getSelectedAttributes(p.item_id)[attr.attribute_name] || attr.possible_values;
+            const chipGroup = createAttributeChips(p, attr, initialSelected, true, updateProductState);
+            attrContainer.appendChild(chipGroup);
+        });
+    }
+
+    // --- Render Optional Attributes ---
+    if (optionalAttributes.length > 0) {
+      const br = el('br');
+        const optTitle = el('p', "pSelectRight optional-title");
+        optTitle.innerHTML = "Additional Attributes:";
+        attrContainer.appendChild(br);
+        attrContainer.appendChild(optTitle);
+
+        optionalAttributes.forEach(attr => {
+            const initialSelected = getSelectedAttributes(p.item_id)[attr.attribute_name] || attr.possible_values;
+            const chipGroup = createAttributeChips(p, attr, initialSelected, false, updateProductState);
+            attrContainer.appendChild(chipGroup);
+        });
+    }
+
+    // Initialize attribute selectors' enabled state and state data
+    attrContainer.classList.toggle('disabled', !cb.checked);
+    // Initial state setup if the product was already selected (e.g., after a re-render)
+    if (cb.checked) {
+        // This is important to set the initial state correctly on load
+        // We defer this until all selects are mounted, or ensure the initial state is correct.
+        // For simplicity, we assume the data from PRODUCT_BASE already includes selected attributes if a selection exists
+        // (which it won't in this case, so they default to all/empty)
+    }
+    }
+
+    const inline = el('div', 'attr-inline');
+    inline.dataset.pid = p.item_id; // use item_id for mapping
+    
+    row.appendChild(left); row.appendChild(mid);
+    if(p.product_type_details.length > 0){
+        console.log("IN ");
+        row.appendChild(attrContainer); // Append the new attribute selectors container
+    }    
+    row.appendChild(right);
+    // if (p.mandatoryAttributes && p.mandatoryAttributes.length > 0) { 
+      // const hr = el('hr');
+      // row.appendChild(hr);
+      // row.appendChild(attri);
+      // row.appendChild(secondRight);
+    // }
+
+    row.appendChild(inline);
+    
+    return row;
+}
+
+
+// Cards layout
+function renderProductsCards(items = getCurrentSlice()) {
+  const cards = document.getElementById('cardsContainer');
+  cards.innerHTML = '';
+  if(items.length > 0){
+    items.forEach(p => cards.appendChild(createProductCard(p)));
+  }else{
+      cards.innerHTML = "<p>No Products Found.</p>"
+  }
+}
+
+
+// --- Table layout ---
+function createMiniThumb(p) {
+    const mt = el('div', 'mini-thumb');
+    const img = new Image(); img.src = p.image_path || p.image || ''; img.alt = `${p.product_name} image`;
+    // console.log("image path",p.image_path);
+    // console.log("img",img);
+    img.onerror = () => { img.src = mediaUrl+"media/images/no-product.png" };
+    // img.onerror = () => { img.remove(); const fb = el('div', 'fallback'); fb.textContent = (p.product_name || 'Product').split(' ').map(w => w[0]).slice(0,2).join('').toUpperCase(); mt.appendChild(fb); };
+    mt.appendChild(img);
+    return mt;
+}
+
+
+// Table layout
+// function renderProductsTable(items = getCurrentSlice()) {
+//   const wrap = document.getElementById('tableContainer');
+//   wrap.innerHTML = '';
+//   const table = document.createElement('table');
+//   const thead = document.createElement('thead'); const trh = document.createElement('tr');
+//   ['Select', 'Image', 'Product', 'SKU', 'Type', 'Short Description'].forEach(h => {
+//     const th = document.createElement('th'); th.textContent = h; trh.appendChild(th);
+//   });
+//   thead.appendChild(trh); table.appendChild(thead);
+//   const tbody = document.createElement('tbody');
+//   if(items.length > 0 ){
+//     items.forEach(p => {
+//         const tr = document.createElement('tr'); tr.id = `row-${p.id}`;
+//         const tdSel = document.createElement('td'); tdSel.className = 'select-cell';
+//         const cb = document.createElement('input'); cb.type = 'checkbox'; cb.checked = selectedIds.has(p.item_id);
+//         cb.addEventListener('change', () => { setChecked(p.item_id, cb.checked); tr.classList.toggle('selected', cb.checked); });
+//         tdSel.appendChild(cb); tr.appendChild(tdSel);
+
+//         const tdImg = document.createElement('td'); tdImg.className = 'thumb-cell'; tdImg.appendChild(createMiniThumb(p)); tr.appendChild(tdImg);
+//         const tdName = document.createElement('td'); tdName.textContent = p.product_name || '—'; tr.appendChild(tdName);
+//         const tdSku  = document.createElement('td'); tdSku.textContent = p.item_id || '—'; tr.appendChild(tdSku);
+//         const tdType = document.createElement('td'); const b = document.createElement('span'); b.className = 'badge'; b.textContent = p.product_type || '—'; tdType.appendChild(b); tr.appendChild(tdType);
+//         const tdDesc = document.createElement('td'); tdDesc.textContent = p.product_short_description || ''; tr.appendChild(tdDesc);
+
+//         tr.addEventListener('click', (e) => { if (e.target.tagName.toLowerCase() !== 'input') { cb.checked = !cb.checked; cb.dispatchEvent(new Event('change')); } });
+//         tbody.appendChild(tr);
+//     });
+//   }else{
+//     const tr = el('tr'); 
+//     // tr.id = `row-${p.id}`;
+//     const tdName = el('td');
+//     tdName.colSpan = 6;
+//     tdName.innerHTML = "No Products Found."
+//     tr.appendChild(tdName);
+//     // tr.colspan = 6;
+//     // tr.innerHTML 
+//     tbody.appendChild(tr);
+//   }
+
+//   table.appendChild(tbody);
+//   wrap.appendChild(table);
+// }
+
+// NOTE: Ensure getProductStateUpdater and generateAttributeUI functions are defined globally or accessible here.
+
+/**
+ * Returns a closure function that updates the global selectedProductsWithAttributes state
+ * based on the current selections (chips) found in the DOM for a specific product.
+ * This is used for both card and table views.
+ * * @param {Object} p - The product object.
+ * @param {HTMLElement} cb - The main product selection checkbox element.
+ * @param {HTMLElement} tr - The main row/card element (used for toggling 'selected' class).
+ * @returns {function} A function to be used as the attribute change handler.
+ */
+const getProductStateUpdater = (p, cb, tr) => () => {
+    const isSelected = cb.checked;
+    const currentSelections = {};
+    
+    // Find the attribute container using its unique ID, which is the same structure 
+    // used in both card and table detail views (e.g., 'attr-container-124353498' or just the main card element).
+    // For card view, the container is often the attrContainer element itself. 
+    // For table view, we use the explicit ID.
+    const attrContainer = document.getElementById(`attr-container-${p.item_id}`) || tr.querySelector('.attribute-selectors');
+
+    if (isSelected && attrContainer) {
+        // Iterate over all attribute groups (Mandatory and Optional) within the container
+        attrContainer.querySelectorAll('.attribute-chip-group').forEach(group => {
+            const attrName = group.dataset.attrName;
+            
+            // Collect selected chip values
+            const selectedOptions = Array.from(group.querySelectorAll('input[type="checkbox"]:checked'))
+                .map(checkbox => checkbox.value);
+
+            // Only add to the selection if at least one option is selected
+            if (selectedOptions.length > 0) {
+                currentSelections[attrName] = selectedOptions;
+            }
+        });
+    }
+    
+    // Update the global state array (selectedProductsWithAttributes)
+    toggleProductSelection(p.item_id, isSelected, currentSelections);
+    
+    // Update the visual status of the row/card
+    tr.classList.toggle('selected', isSelected);
+};
+
+/**
+ * Generates the full attribute selection UI (chips) for a given product.
+ * NOTE: Assumes el(), createAttributeChips(), and getSelectedAttributes() are defined globally.
+ * @param {Object} p - The product object from PRODUCT_BASE.
+ * @param {function} updateProductState - The callback to run on chip changes.
+ * @param {HTMLElement} attrContainer - The container to append the UI to.
+ */
+function generateAttributeUI(p, updateProductState, attrContainer) {
+    // Clear the container first, just in case
+    attrContainer.innerHTML = ''; 
+
+    const mandatoryAttributes = p.product_type_details?.filter(a => a.is_mandatory === 'Yes') || [];
+    const optionalAttributes = p.product_type_details?.filter(a => a.is_mandatory !== 'Yes') || [];
+
+    // --- Render Mandatory Attributes ---
+    if (mandatoryAttributes.length > 0) {
+        // Use a general title for the section header
+        const manTitle = el('p', "pSelectRight mandatory-title");
+        manTitle.innerHTML = "Mandatory Attributes:";
+        attrContainer.appendChild(manTitle);
+
+        mandatoryAttributes.forEach(attr => {
+            const initialSelected = getSelectedAttributes(p.item_id)[attr.attribute_name] || attr.possible_values;
+            // The createAttributeChips function must be globally available
+            const chipGroup = createAttributeChips(p, attr, initialSelected, true, updateProductState);
+            attrContainer.appendChild(chipGroup);
+        });
+    }
+
+    // --- Render Optional Attributes ---
+    if (optionalAttributes.length > 0) {
+        // Add visual separation using the optional-title class
+        const optTitle = el('p', "pSelectRight optional-title");
+        optTitle.innerHTML = "Additional Attributes:";
+        
+        // Append the title for separation
+        attrContainer.appendChild(optTitle);
+
+        optionalAttributes.forEach(attr => {
+            const initialSelected = getSelectedAttributes(p.item_id)[attr.attribute_name] || attr.possible_values;
+            const chipGroup = createAttributeChips(p, attr, initialSelected, false, updateProductState);
+            attrContainer.appendChild(chipGroup);
+        });
+    }
+}
+
+/**
+ * Creates the HTML structure for a single attribute group using chip/checkbox labels.
+ * Assumes the helper function 'el' is available.
+ * * @param {Object} p - The product object.
+ * @param {Object} attr - The specific attribute detail object.
+ * @param {string[]} initialSelected - Array of values that should be pre-checked.
+ * @param {boolean} isMandatory - True if the attribute is mandatory.
+ * @param {function} updateCallback - The function to call when a chip selection changes.
+ * @returns {HTMLElement} The attribute chip group container (div).
+ */
+function createAttributeChips(p, attr, initialSelected, isMandatory, updateCallback) {
+    const wrapper = el('div', 'attribute-chip-group');
+    wrapper.dataset.attrName = attr.attribute_name;
+    
+    // Determine the header text based on structure preference (e.g., just the name)
+    const statusText = isMandatory ? ' (Mandatory)' : ' (Optional)';
+    wrapper.innerHTML = `<p class="attribute-header">${attr.attribute_name}${statusText}:</p>`;
+
+    const chipContainer = el('div', 'chips-container');
+
+    attr.possible_values.forEach(value => {
+        const chip = el('label', 'attribute-chip');
+        
+        // Checkbox input is hidden, but drives the selection state
+        const checkbox = document.createElement('input');
+        checkbox.type = 'checkbox';
+        checkbox.value = value;
+        // Ensure the name is unique per product/attribute group
+        checkbox.name = `${p.item_id}-${attr.attribute_name}`; 
+        
+        // Set initial state
+        checkbox.checked = initialSelected.includes(value); 
+        
+        // The visual part of the chip
+        const span = el('span');
+        span.textContent = value;
+        
+        chip.appendChild(checkbox);
+        chip.appendChild(span);
+        chipContainer.appendChild(chip);
+    });
+    
+    // Attach listener to the container using event delegation
+    chipContainer.addEventListener('change', updateCallback);
+
+    wrapper.appendChild(chipContainer);
+    return wrapper;
+}
+
+function renderProductsTable(items = getCurrentSlice()) {
+    const wrap = document.getElementById('tableContainer');
+    wrap.innerHTML = '';
+    const table = document.createElement('table');
+    table.classList.add('table', 'table-striped', 'table-bordered','table-responsive');
+    
+    const thead = document.createElement('thead'); 
+    const trh = document.createElement('tr');
+    
+    // Table Headers
+    ['Select', 'Image', 'Product', 'SKU', 'Type', 'Short Description', 'Attributes'].forEach(h => {
+        const th = document.createElement('th'); th.textContent = h; trh.appendChild(th);
+    });
+    thead.appendChild(trh); table.appendChild(thead);
+    
+    const tbody = document.createElement('tbody');
+    
+    if (items.length > 0) {
+        items.forEach(p => {
+            const tr = document.createElement('tr'); 
+            tr.id = `row-${p.id}`;
+            if (isProductSelected(p.item_id)) tr.classList.add('selected');
+
+            // --- Define Checkbox (cb) and State Updater ---
+            const cb = document.createElement('input'); 
+            cb.type = 'checkbox'; 
+            cb.checked = isProductSelected(p.item_id);
+            // console.log("checkkkkk")
+            
+            // The state updater function is bound to this specific row/checkbox
+            const updateProductState = getProductStateUpdater(p, cb, tr);
+
+            // --- Select Cell ---
+            const tdSel = document.createElement('td'); 
+            tdSel.className = 'select-cell';
+            tdSel.appendChild(cb); 
+            tr.appendChild(tdSel);
+
+            // --- Other Cells ---
+            const tdImg = document.createElement('td'); tdImg.className = 'thumb-cell'; tdImg.appendChild(createMiniThumb(p)); tr.appendChild(tdImg);
+            const tdName = document.createElement('td'); tdName.textContent = p.product_name || '—'; tr.appendChild(tdName);
+            const tdSku  = document.createElement('td'); tdSku.textContent = p.item_id || '—'; tr.appendChild(tdSku);
+            const tdType = document.createElement('td'); const b = document.createElement('span'); b.className = 'badge'; b.textContent = p.product_type || '—'; tdType.appendChild(b); tr.appendChild(tdType);
+            const tdDesc = document.createElement('td'); tdDesc.textContent = p.product_short_description || ''; tr.appendChild(tdDesc);
+
+            // ---------------------------------------------
+            // --- ATTRIBUTE SELECTION IMPLEMENTATION ---
+            // ---------------------------------------------
+
+            // 1. DETAIL ROW STRUCTURE
+            const detailRow = document.createElement('tr');
+            detailRow.classList.add('attribute-detail-row'); // Custom class for styling
+            detailRow.style.display = 'none'; // Initially hidden
+            detailRow.id = `detail-row-${p.id}`;
+            
+            const detailCell = document.createElement('td');
+            detailCell.colSpan = 7; // Must span all columns
+            
+            const attrContainer = document.createElement('div');
+            attrContainer.id = `attr-container-${p.item_id}`; // Unique ID for targeting by updateProductState
+            attrContainer.classList.add('attribute-selectors', 'table-selectors');
+
+            // 2. GENERATE CHIPS UI
+            generateAttributeUI(p, updateProductState, attrContainer);
+
+            // Initially disable the chips if the product is not selected
+            attrContainer.classList.toggle('disabled', !cb.checked);
+
+            detailCell.appendChild(attrContainer);
+            detailRow.appendChild(detailCell);
+
+            if(p.product_type_details.length > 0){
+                // 3. TOGGLE BUTTON (in the main row)
+                const tdAttr = document.createElement('td'); 
+                const toggleButton = document.createElement('button');
+                toggleButton.textContent = 'Configure';
+                toggleButton.classList.add('btn', 'btn-sm', 'btn-info', 'attribute-toggle-btn');
+                tdAttr.appendChild(toggleButton);
+                tr.appendChild(tdAttr);
+            
+
+
+            // 4. EVENT LISTENERS
+            
+            // a) Toggle Button Logic
+            toggleButton.addEventListener('click', (e) => {
+                e.stopPropagation(); // Stop row click event
+                const isHidden = detailRow.style.display === 'none';
+                detailRow.style.display = isHidden ? '' : 'none'; // Toggle visibility
+                toggleButton.textContent = isHidden ? 'Hide Attributes' : 'Configure';
+                toggleButton.classList.toggle('btn-info', !isHidden);
+                toggleButton.classList.toggle('btn-secondary', isHidden);
+            });
+            
+            // b) Main Checkbox Change Logic
+            cb.addEventListener('change', () => { 
+                // console.log("cheeeeeeeeee");
+                updateProductState(); // Update state on check/uncheck
+                attrContainer.classList.toggle('disabled', !cb.checked); // Enable/Disable chips
+            });
+
+            // c) Row Click Listener (Updated to ignore button clicks)
+            tr.addEventListener('click', (e) => { 
+                const tag = e.target.tagName.toLowerCase();
+                // console.log("clikk")
+                if (tag !== 'input' && tag !== 'button') { 
+                    cb.checked = !cb.checked; 
+                    cb.dispatchEvent(new Event('change')); 
+                } 
+            });
+            }else{
+                const tdAttr = document.createElement('td'); 
+                tr.appendChild(tdAttr);
+                // b) Main Checkbox Change Logic
+                cb.addEventListener('change', () => { 
+                    // console.log("cheeeeeeeeee");
+                    updateProductState(); // Update state on check/uncheck
+                    attrContainer.classList.toggle('disabled', !cb.checked); // Enable/Disable chips
+                });
+                tr.addEventListener('click', (e) => { 
+                    const tag = e.target.tagName.toLowerCase();
+                    // console.log("clikk")
+                    if (tag !== 'input' && tag !== 'button') { 
+                        cb.checked = !cb.checked; 
+                        cb.dispatchEvent(new Event('change')); 
+                    } 
+                });
+            }
+            
+            // 5. Append Rows to TBODY
+            tbody.appendChild(tr);
+            tbody.appendChild(detailRow); // Append the detail row right after the main row
+        });
+    } else {
+        const tr = el('tr'); 
+        const tdName = el('td');
+        tdName.colSpan = 7; 
+        tdName.innerHTML = "No Products Found.";
+        tr.appendChild(tdName);
+        tbody.appendChild(tr);
+    }
+
+    table.appendChild(tbody);
+    wrap.appendChild(table);
+}
+
+function renderInlineForCards() {
+    const api = FAKE_API_RESPONSE;
+    // Clear all inline sections first
+    document.querySelectorAll('.attr-inline').forEach(div => div.innerHTML = '');
+
+    PRODUCT_BASE.forEach((p, idx) => {
+        const inline = document.querySelector(`.attr-inline[data-pid="${p.item_id}"]`);
+        if (!inline) return;
+        
+        // --- CHANGE HERE: Use the new helper function ---
+        if (!isProductSelected(p.item_id)) return; // only show for selected
+        
+        const res = findApiResultForProduct(p, idx, api);
+        const pid = p.item_id;
+        if (!lastSeen.has(pid)) lastSeen.set(pid, { mandatory: new Map(), additional: new Map(), ocr_results: new Map(), visual_results: new Map() });
+        const mem = lastSeen.get(pid);
+
+        // Build sections
+        const manTitle = el('div', 'section-title'); manTitle.innerHTML = '<strong>Mandatory</strong>';
+        const manChips = el('div', 'chips');
+        const addTitle = el('div', 'section-title'); addTitle.innerHTML = '<strong>Additional</strong>';
+        const addChips = el('div', 'chips');
+
+        const addOcr = el('div', 'section-title'); addOcr.innerHTML = '<strong>Ocr</strong>';
+        const ocrChips = el('div', 'chips');
+        const addVisual = el('div', 'section-title'); addVisual.innerHTML = '<strong>Visual</strong>';
+        const visualChips = el('div', 'chips');
+        
+
+        const mandCount = renderChips(manChips, res?.mandatory || {}, mem.mandatory);
+        const addCount = renderChips(addChips, res?.additional || {}, mem.additional);
+        const ocrCount = renderChips(ocrChips, res?.ocr_results?.extracted_attributes || {}, mem?.ocr_results);
+        const visualCount = renderChips(visualChips, res?.visual_results?.visual_attributes || {}, mem?.visual_results);
+
+        const counts = el('div'); counts.style.display = 'flex'; counts.style.gap = '8px'; counts.style.margin = '8px 0 0';
+        const c1 = el('span', 'pill'); c1.textContent = `Mandatory: ${mandCount}`;
+        const c2 = el('span', 'pill'); c2.textContent = `Additional: ${addCount}`;
+        const c3 = el('span', 'pill'); c3.textContent = `OCR: ${ocrCount}`;
+        const c4 = el('span', 'pill'); c4.textContent = `Visuals: ${visualCount}`;
+        counts.appendChild(c1); counts.appendChild(c2);  counts.appendChild(c3);  counts.appendChild(c4);
+
+        inline.appendChild(manTitle); inline.appendChild(manChips);
+        inline.appendChild(addTitle); inline.appendChild(addChips);
+        inline.appendChild(addOcr); inline.appendChild(ocrChips);
+        inline.appendChild(addVisual); inline.appendChild(visualChips);
+        inline.appendChild(counts);
+    });
+
+    // Update summary
+    $('#statTotal').textContent = api.total_products ?? 0;
+    $('#statOk').textContent = api.successful ?? 0;
+    $('#statKo').textContent = api.failed ?? 0;
+    $('#api-summary').style.display = 'block';
+}
+
+// -----------------------------------------------------------
+
+function renderInlineForTable() {
+    const api = FAKE_API_RESPONSE;
+    const table = $('#tableContainer');
+    if (!table) return;
+    // Remove existing detail rows
+    table.querySelectorAll('tr.detail-row').forEach(r => r.remove());
+
+    PRODUCT_BASE.forEach((p, idx) => {
+        // --- CHANGE HERE: Use the new helper function ---
+        if (!isProductSelected(p.item_id)) return;
+        
+        const res = findApiResultForProduct(p, idx, api);
+        const pid = p.item_id;
+        if (!lastSeen.has(pid)) lastSeen.set(pid, { mandatory: new Map(), additional: new Map(), ocr_results: new Map(), visual_results: new Map() });
+        const mem = lastSeen.get(pid);
+
+        const tbody = table.querySelector('tbody');
+        // NOTE: The table rendering uses p.id for the row ID: `row-${p.id}`.
+        // Assuming p.id is still valid for finding the base row, as your original code used it.
+        const baseRow = tbody.querySelector(`#row-${p.id}`); 
+        if (!baseRow) return;
+
+        const detail = el('tr', 'detail-row');
+        const td = el('td'); td.colSpan = 6; // number of columns
+        const content = el('div', 'detail-content');
+
+        const manTitle = el('div', 'section-title'); manTitle.innerHTML = '<strong>Mandatory</strong>';
+        const manChips = el('div', 'chips');
+        const addTitle = el('div', 'section-title'); addTitle.innerHTML = '<strong>Additional</strong>';
+        const addChips = el('div', 'chips');
+        const addOcr = el('div', 'section-title'); addOcr.innerHTML = '<strong>Ocr</strong>';
+        const ocrChips = el('div', 'chips');
+        const addVisuals = el('div', 'section-title'); addVisuals.innerHTML = '<strong>Visuals</strong>';
+        const visualsChips = el('div', 'chips');
+
+        const mandCount = renderChips(manChips, res?.mandatory || {}, mem.mandatory);
+        const addCount = renderChips(addChips, res?.additional || {}, mem.additional);
+        const ocrCount = renderChips(ocrChips, res?.ocr_results?.extracted_attributes || {}, mem.ocr_results);
+        const visualCount = renderChips(visualsChips, res?.visual_results?.visual_attributes || {}, mem.visual_results);
+
+        const counts = el('div'); counts.style.display = 'flex'; counts.style.gap = '8px'; counts.style.margin = '8px 0 0';
+        const c1 = el('span', 'pill'); c1.textContent = `Mandatory: ${mandCount}`;
+        const c2 = el('span', 'pill'); c2.textContent = `Additional: ${addCount}`;
+        const c3 = el('span', 'pill'); c3.textContent = `Ocr: ${ocrCount}`;
+        const c4 = el('span', 'pill'); c4.textContent = `Visuals: ${visualCount}`;
+        counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
+
+        content.appendChild(manTitle); content.appendChild(manChips);
+        content.appendChild(addTitle); content.appendChild(addChips);
+        content.appendChild(addOcr); content.appendChild(ocrChips);
+        content.appendChild(addVisuals); content.appendChild(visualsChips);
+        content.appendChild(counts);
+        td.appendChild(content); detail.appendChild(td);
+
+        // insert after base row
+        baseRow.insertAdjacentElement('afterend', detail);
+    });
+
+    // Update summary
+    $('#statTotal').textContent = api.total_products ?? 0;
+    $('#statOk').textContent = api.successful ?? 0;
+    $('#statKo').textContent = api.failed ?? 0;
+    $('#api-summary').style.display = 'block';
+}
+
+
+function renderInlineAttributes() {
+    if (layoutMode === 'cards') renderInlineForCards(); else renderInlineForTable();
+}
+
+// --- Main rendering ---
+function renderProducts() {
+    if (layoutMode === 'cards') {
+    $('#cardsContainer').style.display = '';
+    $('#tableContainer').style.display = 'none';
+    // console.log("PRODUCT_BASE",PRODUCT_BASE);
+    renderProductsCards();
+    } else {
+    $('#cardsContainer').style.display = 'none';
+    $('#tableContainer').style.display = '';
+    renderProductsTable();
+    }
+    updateSelectionInfo();
+    renderPagination();               
+
+    // If there is a selection, re-render inline attributes (persist across toggle)
+    if (selectedIds.size > 0) renderInlineAttributes();
+}
+
+// --- Submit & Reset ---
+function submitAttributes() {
+  // Check the length of the new array
+    if (selectedProductsWithAttributes.length === 0) { 
+        alert('Please select at least one product.'); 
+        return; 
+    } 
+    // if (selectedIds.size === 0) { alert('Please select at least one product.'); return; }
+    // console.log("selectedIds",selectedIds);
+    jQuery('#full-page-loader').show();  
+    // let inputArray = {
+    //       "product_ids" : [...selectedIds]
+    //     }
+    const extractAdditional = document.getElementById('extract_additional').checked;
+    const processImage = document.getElementById('process_image').checked;
+    // const selectedMultiples = document.getElementById('#mandatory-attributes');
+    // const selectedValues = Array.from(selectedMultiples.selectedOptions).map(option => option.value);
+    const selectElement = document.getElementById('mandatory-attributes');
+
+    const selectedValues = Array.from(selectElement.selectedOptions).map(option => option.value);
+
+    // console.log(selectedValues); // Logs an array of selected values
+    // console.log("thresholdValueDisplay",thresholdValueDisplay.value);
+    const threshold = parseFloat(document.getElementById('thresholdRange').value);
+
+
+    // Transform the new state array into the required API format
+    const itemIds = selectedProductsWithAttributes.map(p => p.item_id);
+    
+    // Create the mandatory_attrs map: { item_id: { attr_name: [values] } }
+    // NOTE: The backend API you showed expects a flattened list of "mandatory_attrs"
+    // like: { "color": ["color", "shade"], "size": ["size", "fit"] } 
+    // It seems to ignore the selected product-specific values and uses a general list of synonyms.
+    // Assuming the request needs a general map of *all unique* selected attributes across all selected products:
+    
+    let mandatoryAttrsMap = {};
+    selectedProductsWithAttributes.forEach(product => {
+        // Merge attributes from all selected products
+        Object.assign(mandatoryAttrsMap, product.mandatory_attrs);
+    });
+
+    // If the API expects the complex, product-specific payload from your Q1 example:
+    const payloadForQ1 = selectedProductsWithAttributes.map(p => ({
+        item_id: p.item_id,
+        mandatory_attrs: p.mandatory_attrs
+    }));
+
+    let inputArray = {
+              "products": payloadForQ1,
+              "model": "llama-3.1-8b-instant",
+              "extract_additional": extractAdditional,
+              "process_image": processImage,
+              "multiple": selectedValues,
+              "threshold_abs": threshold,  // Lower threshold to be more permissive
+            //   "margin": 0.3,  // Larger margin to include more candidates
+            //   "use_adaptive_margin": true,
+            //   "use_semantic_clustering": true
+            }
+    let raw = JSON.stringify(inputArray);
+     fetch('/attr/batch-extract/', {
+        method: 'POST', // or 'POST' if your API expects POST
+        headers: {
+            'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || '',
+            'Content-Type': "application/json"
+        },
+        body: raw
+    })
+    .then(response => response.json())
+    .then(data => {
+        // console.log("response data",data); 
+        FAKE_API_RESPONSE = data;   
+        renderInlineAttributes();
+        jQuery('#full-page-loader').hide();  
+     });
+}
+
+function resetAll() {
+  selectedProductsWithAttributes = []; // Reset the main array
+    // selectedIds.clear();
+    lastSeen.clear();
+    renderProducts();
+    // Clear summary
+    document.getElementById('statTotal').textContent = '0';
+    document.getElementById('statOk').textContent = '0';
+    document.getElementById('statKo').textContent = '0';
+    $('#api-summary').style.display = 'none';
+
+    // ✅ Clear Select2 selections
+    jQuery('#mandatory-attributes').val(null).trigger('change');
+
+    // ✅ Reset threshold input (and display)
+    const thresholdInput = document.getElementById('thresholdRange');
+    const thresholdDisplay = document.getElementById('thresholdValue');
+
+    thresholdInput.value = '0.5'; // or any default value you prefer
+    if (thresholdDisplay) {
+        thresholdDisplay.textContent = '0.5';
+    }
+}
+
+function setLayout(mode) {
+    layoutMode = mode;
+    const btnCards = document.getElementById('btnCards');
+    const btnTable = document.getElementById('btnTable');
+    if (mode === 'cards') { btnCards.classList.add('active'); btnCards.setAttribute('aria-selected', 'true'); btnTable.classList.remove('active'); btnTable.setAttribute('aria-selected', 'false'); }
+    else { btnTable.classList.add('active'); btnTable.setAttribute('aria-selected', 'true'); btnCards.classList.remove('active'); btnCards.setAttribute('aria-selected', 'false'); }
+    renderProducts();
+}
+
+
+// Upload elements (Bootstrap modal version)
+const uploadModalEl = document.getElementById('uploadModal');
+const dropzone      = document.getElementById('dropzone');
+const uploadFiles   = document.getElementById('uploadFiles');
+const fileInfo      = document.getElementById('fileInfo');
+const uploadBar     = document.getElementById('uploadBar');
+const uploadStatus  = document.getElementById('uploadStatus');
+
+// Reset modal on show
+uploadModalEl.addEventListener('shown.bs.modal', () => {
+  uploadStatus.textContent = '';
+  uploadStatus.className = '';             // clear success/error class
+  uploadBar.style.width = '0%';
+  uploadBar.setAttribute('aria-valuenow', '0');
+  uploadFiles.value = '';
+  uploadFiles.setAttribute('accept', ACCEPT_TYPES);
+  fileInfo.textContent = 'No files selected.';
+});
+
+function describeFiles(list) {
+  if (!list || list.length === 0) { fileInfo.textContent = 'No files selected.'; return; }
+  const names = Array.from(list).map(f => `${f.name} (${Math.round(f.size/1024)} KB)`);
+  fileInfo.textContent = names.join(', ');
+}
+
+// Drag & drop feedback
+['dragenter','dragover'].forEach(evt => {
+  dropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); dropzone.classList.add('drag'); });
+});
+['dragleave','drop'].forEach(evt => {
+  dropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('drag'); });
+});
+
+// Handle drop
+dropzone.addEventListener('drop', e => {
+  uploadFiles.files = e.dataTransfer.files;
+  describeFiles(uploadFiles.files);
+});
+
+// Click to browse
+// dropzone.addEventListener('click', () => uploadFiles.click());
+
+// Picker change
+uploadFiles.addEventListener('change', () => describeFiles(uploadFiles.files));
+
+function startUpload() {
+  const files = uploadFiles.files;
+  if (!files || files.length === 0) { alert('Please select file(s) to upload.'); return; }
+  jQuery('#full-page-loader').show();
+  uploadStatus.textContent = 'Uploading...';
+  uploadStatus.className = ''; // neutral
+  uploadBar.style.width = '0%';
+  uploadBar.setAttribute('aria-valuenow', '0');
+
+  const form = new FormData();
+  Array.from(files).forEach(f => form.append('file', f));
+  // form.append('uploaded_by', 'Vishal'); // example extra field
+
+  const xhr = new XMLHttpRequest();
+  xhr.open('POST', UPLOAD_API_URL, true);
+  // If you need auth:
+  // xhr.setRequestHeader('Authorization', 'Bearer <token>');
+
+  xhr.upload.onprogress = (e) => {
+    if (e.lengthComputable) {
+      const pct = Math.round((e.loaded / e.total) * 100);
+      uploadBar.style.width = pct + '%';
+      uploadBar.setAttribute('aria-valuenow', String(pct));
+    }
+  };
+
+  xhr.onreadystatechange = () => {
+    if (xhr.readyState === 4) {
+      const ok = (xhr.status >= 200 && xhr.status < 300);
+      try {
+        const resp = JSON.parse(xhr.responseText || '{}');
+        uploadStatus.textContent = ok ? (resp.message || 'Upload successful') : (resp.error || `Upload failed (${xhr.status})`);
+      } catch {
+        uploadStatus.textContent = ok ? 'Upload successful' : `Upload failed (${xhr.status})`;
+      }
+      uploadStatus.className = ok ? 'success' : 'error';
+      // Optional: auto-close the modal on success after 1.2s:
+      // if (ok) setTimeout(() => bootstrap.Modal.getInstance(uploadModalEl).hide(), 1200);
+    }
+  };
+
+  xhr.onerror = () => {
+    uploadStatus.textContent = 'Network error during upload.';
+    uploadStatus.className = 'error';
+  };
+
+  xhr.send(form);
+  setTimeout(()=>{
+      jQuery('#uploadModal').modal('hide');
+  },3000)
+  jQuery('#full-page-loader').hide();
+
+}
+
+// Wire Start button
+document.getElementById('uploadStart').addEventListener('click', startUpload);
+// Cancel button already closes the modal via data-bs-dismiss
+
+
+
+
+// --- Pagination state ---
+let page = 1;
+let pageSize = 50; // default rows per page
+
+function totalPages() {
+  return Math.max(1, Math.ceil(PRODUCT_BASE.length / pageSize));
+}
+
+function clampPage() {
+  page = Math.min(Math.max(1, page), totalPages());
+}
+
+function getCurrentSlice() {
+  clampPage();
+  const start = (page - 1) * pageSize;
+  return PRODUCT_BASE.slice(start, start + pageSize);
+}
+
+function renderPagination() {
+  const bar = document.getElementById('paginationBar');
+  if (!bar) return;
+
+  const tp = totalPages();
+  clampPage();
+
+  bar.innerHTML = `
+    <div class="page-size">
+      <label for="pageSizeSelect">Rows per page</label>
+      <select id="pageSizeSelect">
+        <option value="5"  ${pageSize===5  ? 'selected' : ''}>5</option>
+        <option value="10" ${pageSize===10 ? 'selected' : ''}>10</option>
+        <option value="20" ${pageSize===20 ? 'selected' : ''}>20</option>
+        <option value="50" ${pageSize===50 ? 'selected' : ''}>50</option>
+        <option value="all" ${pageSize>=PRODUCT_BASE.length ? 'selected' : ''}>All</option>
+      </select>
+    </div>
+
+    <div class="pager">
+      <button class="pager-btn" id="prevPage" ${page<=1 ? 'disabled' : ''} aria-label="Previous page">‹</button>
+      <span class="page-info">Page ${page} of ${tp}</span>
+      <button class="pager-btn" id="nextPage" ${page>=tp ? 'disabled' : ''} aria-label="Next page">›</button>
+    </div>
+  `;
+
+  // wire events
+  document.getElementById('prevPage')?.addEventListener('click', () => { if (page > 1) { page--; renderProducts(); } });
+  document.getElementById('nextPage')?.addEventListener('click', () => { if (page < tp) { page++; renderProducts(); } });
+
+  const sel = document.getElementById('pageSizeSelect');
+  if (sel) {
+    sel.addEventListener('change', () => {
+      const val = sel.value;
+      pageSize = (val === 'all') ? PRODUCT_BASE.length : parseInt(val, 10);
+      page = 1;            // reset to first page when size changes
+      renderProducts();
+    });
+  }
+}
+
+// Function to add/remove product from the state and manage its attributes
+function toggleProductSelection(itemId, isChecked, attributes = {}) {
+    const index = selectedProductsWithAttributes.findIndex(p => p.item_id === itemId);
+    // console.log("index",index);
+    if (isChecked) {
+        // If selecting, ensure the product object exists in the array
+        if (index === -1) {
+            selectedProductsWithAttributes.push({
+                item_id: itemId,
+                mandatory_attrs: attributes
+            });
+        } else {
+            // Update attributes if the product is already selected
+            selectedProductsWithAttributes[index].mandatory_attrs = attributes;
+        }
+    } else {
+        // If deselecting, remove the product object from the array
+        if (index !== -1) {
+            selectedProductsWithAttributes.splice(index, 1);
+        }
+    }
+    updateSelectionInfo();
+}
+
+// Function to get the current mandatory attributes for a selected item
+function getSelectedAttributes(itemId) {
+    const productEntry = selectedProductsWithAttributes.find(p => p.item_id === itemId);
+    return productEntry ? productEntry.mandatory_attrs : {};
+}
+
+// Helper to check if a product is selected
+function isProductSelected(itemId) {
+    return selectedProductsWithAttributes.some(p => p.item_id === itemId);
+}
+
+// Helper to check if a specific attribute/value is selected
+function isAttributeValueSelected(itemId, attrName, value) {
+    const attrs = getSelectedAttributes(itemId);
+    const values = attrs[attrName];
+    return values ? values.includes(value) : false; // Default all selected when first loaded
+}
+
+
+
+// $('.attribute-select').select2({
+//     placeholder: 'Select product attributes'
+// });
+
+function getAtributeList(){
+        jQuery('#full-page-loader').show();  
+            try{
+            fetch('/attr/products/attributes', {
+                method: 'GET', // or 'POST' if your API expects POST
+                headers: {
+                    'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || ''
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                // console.log("data",data);
+                let attributesData = data;
+                 // Step 1: Extract unique mandatory attribute names
+                const mandatoryAttributes = [...new Set(
+                    attributesData
+                        .filter(attr => attr.is_mandatory === "Yes")
+                        .map(attr => attr.attribute_name)
+                )];
+
+                // Step 2: Populate the select element
+                const $select = jQuery('#mandatory-attributes');
+                $select.append(new Option("Select All", "select_all")); // Add "Select All" option first
+
+                mandatoryAttributes.forEach(attr => {
+                    $select.append(new Option(attr, attr));
+                });
+
+                // Step 3: Initialize Select2 with placeholder
+                // $select.select2({
+                //     placeholder: "Select mandatory attributes",
+                //     allowClear: true
+                // });
+
+                // Step 4: Handle 'Select All' logic
+                $select.on('select2:select', function (e) {
+                    if (e.params.data.id === "select_all") {
+                        // Select all real options except "Select All"
+                        const allOptions = mandatoryAttributes;
+                        $select.val(allOptions).trigger('change');
+                    }
+                });
+
+                jQuery('#full-page-loader').hide();
+            });
+        }catch(err){
+            console.log("err",err);
+            jQuery('#full-page-loader').hide();
+
+        }
+    
+}
+document.addEventListener("DOMContentLoaded", function () {
+    // Update span when range changes
+    thresholdInput.addEventListener('input', function () {
+        // console.log("this.value",this.value);
+        thresholdValueDisplay.textContent = this.value;
+    });
+});
+
+// Get threshold value when needed
+function getThreshold() {
+    // console.log("parseFloat(thresholdInput.value)",parseFloat(thresholdInput.value));
+    return parseFloat(thresholdInput.value);
+}

+ 3471 - 0
title_creator_app/static/js/attr-extraction.js

@@ -0,0 +1,3471 @@
+jQuery.noConflict(); // Release $ to other libraries
+// console.log(typeof jQuery);
+// $ = jQuery;
+
+// --- Config ---
+const UPLOAD_API_URL = '/attr/products/upload-excel/'; // TODO: set to your upload endpoint
+const ACCEPT_TYPES = '*'; // e.g., 'image/*,.csv,.xlsx'
+
+
+const thresholdInput = document.getElementById('thresholdRange');
+const thresholdValueDisplay = document.getElementById('thresholdValue');
+
+var attributesFullData = [];
+
+var PRODUCT_BASE = [
+    // { id: 1, item_id: 'SKU001', product_name: "Levi's Jeans", product_long_description: 'Classic blue denim jeans with straight fit.', product_short_description: 'Blue denim jeans.', product_type: 'Clothing', image_path: 'media/products/jeans.jpg', image: 'http://127.0.0.1:8000/media/products/jeans.png' },
+    // { id: 2, item_id: 'SKU002', product_name: 'Adidas Running Shoes', product_long_description: 'Lightweight running shoes with breathable mesh and cushioned sole.', product_short_description: "Men's running shoes.", product_type: 'Footwear', image_path: 'media/products/shoes.png', image: 'http://127.0.0.1:8000/media/products/shoes.png' },
+    // { id: 3, item_id: 'SKU003', product_name: 'Nike Sports T-Shirt', product_long_description: 'Moisture-wicking sports tee ideal for training and outdoor activities.', product_short_description: 'Performance t-shirt.', product_type: 'Clothing', image_path: 'media/products/tshirt.png', image: 'http://127.0.0.1:8000/media/products/tshirt.png' },
+    // { id: 4, item_id: 'SKU004', product_name: 'Puma Hoodie', product_long_description: 'Soft fleece hoodie with kangaroo pocket and adjustable drawstring.', product_short_description: 'Casual hoodie.', product_type: 'Clothing', image_path: 'media/products/hoodie.png', image: 'http://127.0.0.1:8000/media/products/hoodie.png' },
+    // { id: 5, item_id: 'SKU005', product_name: 'Ray-Ban Sunglasses', product_long_description: 'Classic aviator sunglasses with UV protection lenses.', product_short_description: 'Aviator sunglasses.', product_type: 'Accessories', image_path: 'media/products/sunglasses.png', image: 'http://127.0.0.1:8000/media/products/sunglasses.png' }
+];
+// --- Data ---
+const mediaUrl = "./../";
+
+document.addEventListener('DOMContentLoaded', () => {
+    jQuery('#full-page-loader').show();  
+    fetch('/attr/products', {
+        method: 'GET', // or 'POST' if your API expects POST
+        headers: {
+            'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || ''
+        }
+    })
+    .then(response => response.json())
+    .then(data => {
+        // console.log("data",data);
+        // --- Wire up ---
+        PRODUCT_BASE = data;
+        PRODUCT_BASE = PRODUCT_BASE.map((d)=>{return {...d,mandatoryAttributes:["color","size"]}});
+        // console.log("PRODUCT_BASE",PRODUCT_BASE);
+        if(PRODUCT_BASE.length > 0){
+            $('#paginationBar').style.display = 'block';
+        }
+        renderProducts();
+        getAtributeList();
+        document.getElementById('btnSubmit').addEventListener('click', submitAttributes);
+        // document.getElementById('btnReset').addEventListener('click', resetAll);
+        // document.getElementById('btnSelectAll').addEventListener('click', () => {
+        // if (selectedIds.size === PRODUCT_BASE.length) { selectedIds.clear(); } else { selectedIds = new Set(PRODUCT_BASE.map(p => p.id)); }
+        // // renderProducts();
+        // });
+
+        // Replace your existing Select All listener with this:
+          document.getElementById('btnSelectAll').addEventListener('click', () => {
+            // Use the container for the active layout
+            const container = (layoutMode === 'cards')
+              ? document.getElementById('cardsContainer')
+              : document.getElementById('tableContainer');
+
+            // Collect all visible checkboxes
+            const boxes = Array.from(container.querySelectorAll('input[type="checkbox"]'));
+
+            // If every visible checkbox is already checked, we'll deselect; otherwise select all
+            const allChecked = boxes.length > 0 && boxes.every(cb => cb.checked);
+
+            boxes.forEach(cb => {
+              const target = !allChecked; // true to select, false to deselect
+              if (cb.checked !== target) {
+                cb.checked = target;
+                // Trigger your existing "change" handler so selectedIds & row .selected class update
+                cb.dispatchEvent(new Event('change', { bubbles: true }));
+              }
+            });
+
+            // Update the selection pill text (doesn't re-render the list)
+            updateSelectionInfo();
+          });
+        document.getElementById('btnCards').addEventListener('click', () => setLayout('cards'));
+        document.getElementById('btnTable').addEventListener('click', () => setLayout('table'));
+        jQuery('#full-page-loader').hide();
+        // if (data.success) {
+        // }
+    });
+});
+
+var API_RESPONSE_AI = {
+    // results: [
+    // { product_id: 'SKU001', mandatory: { 'Clothing Neck Style': 'V-Neck', 'Clothing Top Style': 'Pullover', 'Condition': 'New', 'T-Shirt Type': 'Classic T-Shirt' }, additional: { 'Material': 'Turkish Pima Cotton', 'Size': 'Large', 'Color': 'Blue', 'Brand': 'Sierra', 'Fabric Type': 'Soft & Breathable', 'Fabric Composition': '95% Turkish Pima cotton', 'Care Instructions': 'Machine Washable', 'Sizes Available': 'S-XL' } },
+    // { product_id: 'SKU002', mandatory: { 'Shoe Type': 'Running', 'Closure': 'Lace-Up', 'Condition': 'New', 'Gender': 'Men' }, additional: { 'Upper Material': 'Engineered Mesh', 'Midsole': 'EVA Foam', 'Outsole': 'Rubber', 'Color': 'Black/White', 'Brand': 'Adidas', 'Size': 'UK 9', 'Care Instructions': 'Surface Clean' } },
+    // { product_id: 'SKU003', mandatory: { 'Clothing Neck Style': 'Crew Neck', 'Sleeve Length': 'Short Sleeve', 'Condition': 'New', 'T-Shirt Type': 'Performance' }, additional: { 'Material': 'Polyester Blend', 'Color': 'Red', 'Brand': 'Nike', 'Size': 'Medium', 'Fabric Technology': 'Dri-FIT', 'Care Instructions': 'Machine Wash Cold' } },
+    // { product_id: 'SKU004', mandatory: { 'Clothing Top Style': 'Hoodie', 'Closure': 'Pullover', 'Condition': 'New', 'Fit': 'Relaxed' }, additional: { 'Material': 'Cotton Fleece', 'Color': 'Charcoal', 'Brand': 'Puma', 'Size': 'Large', 'Care Instructions': 'Machine Wash Warm' } },
+    // { product_id: 'SKU005', mandatory: { 'Accessory Type': 'Sunglasses', 'Frame Style': 'Aviator', 'Condition': 'New', 'Lens Protection': 'UV 400' }, additional: { 'Frame Material': 'Metal', 'Lens Color': 'Green', 'Brand': 'Ray-Ban', 'Size': 'Standard', 'Case Included': 'Yes', 'Care Instructions': 'Clean with microfiber' } }
+    // ],
+    // total_products: 5,
+    // successful: 5,
+    // failed: 0
+};
+
+// --- State ---
+let selectedIds = new Set();
+// NEW: Array of objects { item_id: string, mandatory_attrs: { [attribute_name]: string[] } }
+let selectedProductsWithAttributes = [];
+let selectedAttributes = new Array();
+const lastSeen = new Map(); // per-product memory for NEW highlighting (product_id -> maps)
+let layoutMode = 'table'; // 'cards' | 'table'
+
+// --- Helpers ---
+const $ = (sel) => document.querySelector(sel);
+const el = (tag, cls) => { const e = document.createElement(tag); if (cls) e.className = cls; return e; }
+
+function updateSelectionInfo() {
+    const pill = $('#selectionInfo');
+    const total = PRODUCT_BASE.length;
+    // const count = selectedIds.size;
+    const count = selectedProductsWithAttributes.length;
+    pill.textContent = count === 0 ? 'No products selected' : `${count} of ${total} selected`;
+}
+
+function setChecked(id, checked) { if (checked) selectedIds.add(id); else selectedIds.delete(id); updateSelectionInfo(); }
+// function setCheckedAttributes(id,attribute, checked) { if (checked) selectedAttributes.add({id: [attribute]}); else selectedIds.delete({id:[attribute]}); updateSelectionInfo(); }
+function formatString(str) {
+  return str
+    // Replace underscores with spaces
+    .replace(/_/g, ' ')
+    // Insert a space before any uppercase letter (except at the start)
+    .replace(/([a-z])([A-Z])/g, '$1 $2')
+    // Capitalize the first letter
+    .replace(/^./, char => char.toUpperCase());
+}
+
+// --- Chips rendering ---
+function renderChips(container, obj, memoryMap) {
+    container.innerHTML = '';
+    let count = 0;
+    Object.entries(obj || {}).forEach(([k, v]) => {
+    const chip = el('span', 'chip');
+    const kEl = el('span', 'k'); kEl.textContent = formatString(k) + ':';
+    // console.log("v",v);
+    let resVal = "";
+    let sourceVal = "";
+    if(v instanceof Array){
+        resVal  = String(v.map(v => formatString(v.value)).join(", "));
+        sourceVal = String(formatString(v[0]?.source));
+        const vEl = el('span', 'v'); vEl.textContent = ' ' + resVal  +' (' + sourceVal + ')';
+        chip.appendChild(kEl); chip.appendChild(vEl);
+    }
+    // console.log("k",k);
+    if(v instanceof Array){
+    const was = memoryMap.get(k);
+    if (was === undefined || was !== v) chip.classList.add('new');
+    container.appendChild(chip);
+    memoryMap.set(k, v);
+    
+    count++;
+    }
+    });
+    return count;
+}
+
+function findApiResultForProduct(p, index, api) { return api.results?.find(r => r.product_id === p.item_id) || api.results?.[index] || null; }
+
+// --- Cards layout ---
+function createProductCard(p) {
+    const row = el('div', 'product');
+    // Check selection using the new helper
+    if (isProductSelected(p.item_id)) row.classList.add('selected');
+    // if (selectedIds.has(p.item_id)) row.classList.add('selected');
+
+    // const left = el('div', 'thumb');
+    // const img = new Image(); img.src = p.image_path || p.image || '';
+    // // console.log("image path",p.image_path);
+    //  img.alt = `${p.product_name} image`;
+    // // console.log("img",img);
+    // // img.onerror = () => { img.remove(); const fb = el('div', 'fallback'); fb.textContent = (p.product_name || 'Product').split(' ').map(w => w[0]).slice(0,2).join('').toUpperCase(); left.appendChild(fb); };
+    // img.onerror = () => { img.src = mediaUrl+"media/images/no-product.png" };
+    // left.appendChild(img);
+
+    // Assume 'el' is a function that creates an element (e.g., document.createElement)
+// Assume 'p' (product data) and 'mediaUrl' are available in scope.
+
+    // 1. Create the main container for the hover effect
+    const container = el('div', 'mini-thumb-container'); 
+
+    // 2. Create the visible thumbnail wrapper (your original 'left' element)
+    // This will serve as the base for the small image
+    const left = el('div', 'thumb'); 
+
+    // Get the image source (same as before)
+    const imageSrc = p.image_path || p.image || '';
+
+    // 3. Create the thumbnail image element (same as before)
+    const thumbImg = new Image(); 
+    thumbImg.src = imageSrc; 
+    thumbImg.alt = `${p.product_name} image`;
+    thumbImg.onerror = () => { thumbImg.src = mediaUrl+"media/images/no-product.png" };
+    // Use thumbImg instead of img to avoid naming conflict with other code if possible
+    // We will call it 'img' here to match your original code:
+    const img = thumbImg; 
+
+    left.appendChild(img);
+    container.appendChild(left); // Add the visible thumbnail to the main container
+
+    // 4. Create the full-size image element for hover (new part)
+    const fullImgWrapper = el('div', 'full-image-hover'); // Class for CSS control
+
+    const fullImg = new Image();
+    fullImg.src = imageSrc; // Use the same source, or p.full_image_path if available
+    fullImg.alt = `${p.product_name} full view`;
+    fullImg.onerror = () => { fullImg.src = mediaUrl + "media/images/no-product.png" }; // Same fallback
+
+    fullImgWrapper.appendChild(fullImg);
+    container.appendChild(fullImgWrapper); // Add full image wrapper to the main container
+
+    // The variable to use when appending this element to the DOM is 'container'.
+    // return container; // If this block is inside a function
+
+
+    const mid = el('div', 'meta');
+    const name = el('div', 'name'); name.textContent = p.product_name || '—';
+    const desc = el('div', 'desc'); desc.innerHTML = p.product_short_description || '';
+    const badges = el('div', 'badges');
+    const sku = el('span', 'pill'); sku.textContent = `SKU: ${p.item_id || '—'}`; badges.appendChild(sku);
+    const type = el('span', 'pill'); type.textContent = p.product_type || '—'; badges.appendChild(type);
+    const long = el('div', 'desc'); long.innerHTML = p.product_long_description || ''; long.style.marginTop = '4px';
+    mid.appendChild(name); mid.appendChild(desc); mid.appendChild(badges); mid.appendChild(long);
+
+    // Helper function to create the chip UI for attributes
+    function createAttributeChips(p, attr, initialSelected, isMandatory, updateCallback) {
+        const wrapper = el('div', 'attribute-chip-group');
+        wrapper.dataset.attrName = attr?.attribute_name;
+        wrapper.innerHTML = `<p class="attribute-header">${attr?.attribute_name} (${isMandatory ? 'Mandatory' : 'Optional'}):</p>`;
+
+        const chipContainer = el('div', 'chips-container');
+
+        attr?.possible_values.forEach(value => {
+            const chip = el('label', 'attribute-chip');
+            
+            // Checkbox input is hidden, but drives the selection state
+            const checkbox = document.createElement('input');
+            checkbox.type = 'checkbox';
+            checkbox.value = value;
+            checkbox.name = `${p.item_id}-${attr?.attribute_name}`;
+            
+            // Set initial state
+            checkbox.checked = initialSelected.includes(value); 
+            
+            // The visual part of the chip
+            const span = el('span');
+            span.textContent = value;
+            
+            chip.appendChild(checkbox);
+            chip.appendChild(span);
+            chipContainer.appendChild(chip);
+        });
+        
+        // Use event delegation on the container for performance
+        chipContainer.addEventListener('change', updateCallback);
+
+        wrapper.appendChild(chipContainer);
+        return wrapper;
+    }
+
+    // --- Main Select Checkbox (Product Selection) ---
+    const right = el('label', 'select');
+    const cb = document.createElement('input'); cb.type = 'checkbox';
+    cb.checked = isProductSelected(p.item_id);
+    const lbl = el('span'); lbl.textContent = 'Select Product';
+    right.appendChild(cb); right.appendChild(lbl);
+
+    
+    // --- Dynamic Attribute Selects ---
+    const attrContainer = el('div', 'attribute-selectors');
+if(p.product_type_details.length > 0){
+    // Find all mandatory and non-mandatory attributes for this product
+    const mandatoryAttributes = p.product_type_details?.filter(a => a.is_mandatory === 'Yes') || [];
+    const optionalAttributes = p.product_type_details?.filter(a => a.is_mandatory !== 'Yes') || [];
+
+    // Helper to update the main state object with all current selections
+    const updateProductState = () => {
+        const isSelected = cb.checked;
+        const currentSelections = {};
+
+        if (isSelected) {
+            // Iterate over all attribute groups (Mandatory and Optional)
+            attrContainer.querySelectorAll('.attribute-chip-group').forEach(group => {
+                const attrName = group.dataset.attrName;
+                
+                // Collect selected chip values
+                const selectedOptions = Array.from(group.querySelectorAll('input[type="checkbox"]:checked'))
+                    .map(checkbox => checkbox.value);
+
+                if (selectedOptions.length > 0) {
+                    currentSelections[attrName] = selectedOptions;
+                }
+            });
+        }
+        
+        toggleProductSelection(p.item_id, isSelected, currentSelections);
+        row.classList.toggle('selected', isSelected);
+    };
+
+    // Attach listener to main checkbox
+    cb.addEventListener('change', () => {
+        attrContainer.classList.toggle('disabled', !cb.checked);
+        updateProductState();
+    });
+    
+    // --- Render Mandatory Attributes ---
+    // if (mandatoryAttributes.length > 0) {
+    //     const manTitle = el('p', "pSelectRight mandatory-title");
+    //     manTitle.innerHTML = "Mandatory Attributes:";
+    //     attrContainer.appendChild(manTitle);
+
+    //     mandatoryAttributes.forEach(attr => {
+    //         const initialSelected = getSelectedAttributes(p.item_id)[attr?.attribute_name] || attr?.possible_values;
+    //         const chipGroup = createAttributeChips(p, attr, initialSelected, true, updateProductState);
+    //         attrContainer.appendChild(chipGroup);
+    //     });
+    // }
+
+    // --- Render Optional Attributes ---
+    if (optionalAttributes.length > 0) {
+      const br = el('br');
+        const optTitle = el('p', "pSelectRight optional-title");
+        optTitle.innerHTML = "Additional Attributes:";
+        attrContainer.appendChild(br);
+        attrContainer.appendChild(optTitle);
+
+        optionalAttributes.forEach(attr => {
+            const initialSelected = getSelectedAttributes(p.item_id)[attr?.attribute_name] || attr?.possible_values;
+            const chipGroup = createAttributeChips(p, attr, initialSelected, false, updateProductState);
+            attrContainer.appendChild(chipGroup);
+        });
+    }
+
+    // Initialize attribute selectors' enabled state and state data
+    attrContainer.classList.toggle('disabled', !cb.checked);
+    // Initial state setup if the product was already selected (e.g., after a re-render)
+    if (cb.checked) {
+        // This is important to set the initial state correctly on load
+        // We defer this until all selects are mounted, or ensure the initial state is correct.
+        // For simplicity, we assume the data from PRODUCT_BASE already includes selected attributes if a selection exists
+        // (which it won't in this case, so they default to all/empty)
+    }
+    }
+
+    const inline = el('div', 'attr-inline');
+    inline.dataset.pid = p.item_id; // use item_id for mapping
+    
+    row.appendChild(container); row.appendChild(mid);
+    if(p.product_type_details.length > 0){
+        console.log("IN ");
+        // row.appendChild(attrContainer); // Append the new attribute selectors container
+    }    
+    row.appendChild(right);
+    // if (p.mandatoryAttributes && p.mandatoryAttributes.length > 0) { 
+      // const hr = el('hr');
+      // row.appendChild(hr);
+      // row.appendChild(attri);
+      // row.appendChild(secondRight);
+    // }
+
+    row.appendChild(inline);
+    
+    return row;
+}
+
+
+// Cards layout
+function renderProductsCards(items = getCurrentSlice()) {
+  const cards = document.getElementById('cardsContainer');
+  cards.innerHTML = '';
+  if(items.length > 0){
+    items.forEach(p => cards.appendChild(createProductCard(p)));
+  }else{
+      cards.innerHTML = "<p>No Products Found.</p>"
+  }
+}
+
+
+// --- Table layout ---
+// function createMiniThumb(p) {
+//     const mt = el('div', 'mini-thumb');
+//     const img = new Image(); img.src = p.image_path || p.image || ''; img.alt = `${p.product_name} image`;
+//     // console.log("image path",p.image_path);
+//     // console.log("img",img);
+//     img.onerror = () => { img.src = mediaUrl+"media/images/no-product.png" };
+//     // img.onerror = () => { img.remove(); const fb = el('div', 'fallback'); fb.textContent = (p.product_name || 'Product').split(' ').map(w => w[0]).slice(0,2).join('').toUpperCase(); mt.appendChild(fb); };
+//     mt.appendChild(img);
+//     return mt;
+// }
+
+function createMiniThumb(p) {
+    // 1. Create a container for the hover effect
+    const container = document.createElement('div');
+    container.className = 'mini-thumb-container';
+
+    // 2. Create the visible thumbnail (Mini Thumb)
+    const mt = document.createElement('div');
+    mt.className = 'mini-thumb';
+
+    // Get the image source
+    const imageSrc = p.image_path || p.image || '';
+
+    // 3. Create the thumbnail image element
+    const thumbImg = new Image(); 
+    thumbImg.src = imageSrc; 
+    thumbImg.alt = `${p.product_name} thumbnail`;
+    thumbImg.onerror = () => { thumbImg.src = mediaUrl + "media/images/no-product.png" };
+    
+    mt.appendChild(thumbImg);
+    container.appendChild(mt); // Add thumbnail to container
+
+    // 4. Create the full-size image element for hover
+    const fullImgWrapper = document.createElement('div');
+    fullImgWrapper.className = 'full-image-hover'; // Class for CSS control
+    
+    const fullImg = new Image();
+    // Assuming the same source is used for the full image, but you can change this 
+    // to p.full_image_path if your product object has a separate full-size URL.
+    fullImg.src = imageSrc; 
+    fullImg.alt = `${p.product_name} full view`;
+    fullImg.onerror = () => { fullImg.src = mediaUrl + "media/images/no-product.png" }; // Same fallback
+    
+    fullImgWrapper.appendChild(fullImg);
+    container.appendChild(fullImgWrapper); // Add full image wrapper to container
+
+    // Return the main container instead of just the mini-thumb
+    return container; 
+}
+
+
+
+// function createMiniThumb(p) {
+//     const container = document.createElement('div');
+//     container.className = 'mini-thumb-container';
+
+//     const mt = document.createElement('div');
+//     mt.className = 'mini-thumb';
+
+//     const imageSrc = p.image_path || p.image || '';
+//     const thumbImg = new Image();
+//     thumbImg.src = imageSrc;
+//     thumbImg.alt = `${p.product_name} thumbnail`;
+//     thumbImg.onerror = () => { thumbImg.src = mediaUrl + "media/images/no-product.png" };
+
+//     mt.appendChild(thumbImg);
+//     container.appendChild(mt);
+
+//     const fullImgWrapper = document.createElement('div');
+//     fullImgWrapper.className = 'full-image-hover';
+
+//     const fullImg = new Image();
+//     fullImg.src = imageSrc;
+//     fullImg.alt = `${p.product_name} full view`;
+//     fullImg.onerror = () => { fullImg.src = mediaUrl + "media/images/no-product.png" };
+
+//     fullImgWrapper.appendChild(fullImg);
+//     document.body.appendChild(fullImgWrapper); // Append to body, not container
+
+//     // Hover logic
+//     mt.addEventListener('mouseenter', () => {
+//         fullImgWrapper.style.display = 'block';
+//     });
+
+//     mt.addEventListener('mousemove', (e) => {
+//         fullImgWrapper.style.top = (e.clientY + 20) + 'px'; // 20px offset
+//         fullImgWrapper.style.left = (e.clientX + 20) + 'px';
+//     });
+
+//     mt.addEventListener('mouseleave', () => {
+//         fullImgWrapper.style.display = 'none';
+//     });
+
+//     return container;
+// }
+
+
+// Create one global hover container
+// const hoverContainer = document.createElement('div');
+// hoverContainer.className = 'full-image-hover';
+// const hoverImg = new Image();
+// hoverContainer.appendChild(hoverImg);
+// document.body.appendChild(hoverContainer);
+
+// let hoverActive = false;
+
+// function createMiniThumb(p) {
+//     const container = document.createElement('div');
+//     container.className = 'mini-thumb-container';
+
+//     const mt = document.createElement('div');
+//     mt.className = 'mini-thumb';
+
+//     const imageSrc = p.image_path || p.image || '';
+//     const thumbImg = new Image();
+//     thumbImg.src = imageSrc;
+//     thumbImg.alt = `${p.product_name} thumbnail`;
+//     thumbImg.onerror = () => { thumbImg.src = mediaUrl + "media/images/no-product.png" };
+
+//     mt.appendChild(thumbImg);
+//     container.appendChild(mt);
+
+//     // Hover logic
+//     mt.addEventListener('mouseenter', () => {
+//         hoverImg.src = imageSrc;
+//         hoverImg.alt = `${p.product_name} full view`;
+//         hoverContainer.style.display = 'block';
+//     });
+
+//     mt.addEventListener('mousemove', (e) => {
+//         hoverContainer.style.top = (e.clientY + 20) + 'px';
+//         hoverContainer.style.left = (e.clientX + 20) + 'px';
+//     });
+
+//     mt.addEventListener('mouseleave', () => {
+//         setTimeout(() => {
+//             if (!hoverActive) hoverContainer.style.display = 'none';
+//         }, 100);
+//     });
+
+//     hoverContainer.addEventListener('mouseenter', () => {
+//         hoverActive = true;
+//     });
+
+//     hoverContainer.addEventListener('mouseleave', () => {
+//         hoverActive = false;
+//         hoverContainer.style.display = 'none';
+//     });
+
+//     return container;
+// }
+
+
+
+// Table layout
+// function renderProductsTable(items = getCurrentSlice()) {
+//   const wrap = document.getElementById('tableContainer');
+//   wrap.innerHTML = '';
+//   const table = document.createElement('table');
+//   const thead = document.createElement('thead'); const trh = document.createElement('tr');
+//   ['Select', 'Image', 'Product', 'SKU', 'Type', 'Short Description'].forEach(h => {
+//     const th = document.createElement('th'); th.textContent = h; trh.appendChild(th);
+//   });
+//   thead.appendChild(trh); table.appendChild(thead);
+//   const tbody = document.createElement('tbody');
+//   if(items.length > 0 ){
+//     items.forEach(p => {
+//         const tr = document.createElement('tr'); tr.id = `row-${p.id}`;
+//         const tdSel = document.createElement('td'); tdSel.className = 'select-cell';
+//         const cb = document.createElement('input'); cb.type = 'checkbox'; cb.checked = selectedIds.has(p.item_id);
+//         cb.addEventListener('change', () => { setChecked(p.item_id, cb.checked); tr.classList.toggle('selected', cb.checked); });
+//         tdSel.appendChild(cb); tr.appendChild(tdSel);
+
+//         const tdImg = document.createElement('td'); tdImg.className = 'thumb-cell'; tdImg.appendChild(createMiniThumb(p)); tr.appendChild(tdImg);
+//         const tdName = document.createElement('td'); tdName.textContent = p.product_name || '—'; tr.appendChild(tdName);
+//         const tdSku  = document.createElement('td'); tdSku.textContent = p.item_id || '—'; tr.appendChild(tdSku);
+//         const tdType = document.createElement('td'); const b = document.createElement('span'); b.className = 'badge'; b.textContent = p.product_type || '—'; tdType.appendChild(b); tr.appendChild(tdType);
+//         const tdDesc = document.createElement('td'); tdDesc.textContent = p.product_short_description || ''; tr.appendChild(tdDesc);
+
+//         tr.addEventListener('click', (e) => { if (e.target.tagName.toLowerCase() !== 'input') { cb.checked = !cb.checked; cb.dispatchEvent(new Event('change')); } });
+//         tbody.appendChild(tr);
+//     });
+//   }else{
+//     const tr = el('tr'); 
+//     // tr.id = `row-${p.id}`;
+//     const tdName = el('td');
+//     tdName.colSpan = 6;
+//     tdName.innerHTML = "No Products Found."
+//     tr.appendChild(tdName);
+//     // tr.colspan = 6;
+//     // tr.innerHTML 
+//     tbody.appendChild(tr);
+//   }
+
+//   table.appendChild(tbody);
+//   wrap.appendChild(table);
+// }
+
+// NOTE: Ensure getProductStateUpdater and generateAttributeUI functions are defined globally or accessible here.
+
+/**
+ * Returns a closure function that updates the global selectedProductsWithAttributes state
+ * based on the current selections (chips) found in the DOM for a specific product.
+ * This is used for both card and table views.
+ * * @param {Object} p - The product object.
+ * @param {HTMLElement} cb - The main product selection checkbox element.
+ * @param {HTMLElement} tr - The main row/card element (used for toggling 'selected' class).
+ * @returns {function} A function to be used as the attribute change handler.
+ */
+const getProductStateUpdater = (p, cb, tr) => () => {
+    const isSelected = cb.checked;
+    const currentSelections = {};
+    
+    // Find the attribute container using its unique ID, which is the same structure 
+    // used in both card and table detail views (e.g., 'attr-container-124353498' or just the main card element).
+    // For card view, the container is often the attrContainer element itself. 
+    // For table view, we use the explicit ID.
+    const attrContainer = document.getElementById(`attr-container-${p.item_id}`) || tr.querySelector('.attribute-selectors');
+
+    if (isSelected && attrContainer) {
+        // Iterate over all attribute groups (Mandatory and Optional) within the container
+        attrContainer.querySelectorAll('.attribute-chip-group').forEach(group => {
+            const attrName = group.dataset.attrName;
+            
+            // Collect selected chip values
+            const selectedOptions = Array.from(group.querySelectorAll('input[type="checkbox"]:checked'))
+                .map(checkbox => checkbox.value);
+
+            // Only add to the selection if at least one option is selected
+            if (selectedOptions.length > 0) {
+                currentSelections[attrName] = selectedOptions;
+            }
+        });
+    }
+    
+    // Update the global state array (selectedProductsWithAttributes)
+    toggleProductSelection(p.item_id, isSelected, currentSelections);
+    
+    // Update the visual status of the row/card
+    tr.classList.toggle('selected', isSelected);
+};
+
+/**
+ * Generates the full attribute selection UI (chips) for a given product.
+ * NOTE: Assumes el(), createAttributeChips(), and getSelectedAttributes() are defined globally.
+ * @param {Object} p - The product object from PRODUCT_BASE.
+ * @param {function} updateProductState - The callback to run on chip changes.
+ * @param {HTMLElement} attrContainer - The container to append the UI to.
+ */
+function generateAttributeUI(p, updateProductState, attrContainer) {
+    // Clear the container first, just in case
+    attrContainer.innerHTML = ''; 
+
+    const mandatoryAttributes = p.product_type_details?.filter(a => a.is_mandatory === 'Yes') || [];
+    const optionalAttributes = p.product_type_details?.filter(a => a.is_mandatory !== 'Yes') || [];
+
+    // --- Render Mandatory Attributes ---
+    if (mandatoryAttributes.length > 0) {
+        // Use a general title for the section header
+        const manTitle = el('p', "pSelectRight mandatory-title");
+        manTitle.innerHTML = "Mandatory Attributes:";
+        attrContainer.appendChild(manTitle);
+
+        mandatoryAttributes.forEach(attr => {
+            const initialSelected = getSelectedAttributes(p.item_id)[attr?.attribute_name] || attr?.possible_values;
+            // The createAttributeChips function must be globally available
+            const chipGroup = createAttributeChips(p, attr, initialSelected, true, updateProductState);
+            attrContainer.appendChild(chipGroup);
+        });
+    }
+
+    // --- Render Optional Attributes ---
+    if (optionalAttributes.length > 0) {
+        // Add visual separation using the optional-title class
+        const optTitle = el('p', "pSelectRight optional-title");
+        optTitle.innerHTML = "Additional Attributes:";
+        
+        // Append the title for separation
+        attrContainer.appendChild(optTitle);
+
+        optionalAttributes.forEach(attr => {
+            const initialSelected = getSelectedAttributes(p.item_id)[attr?.attribute_name] || attr?.possible_values;
+            const chipGroup = createAttributeChips(p, attr, initialSelected, false, updateProductState);
+            attrContainer.appendChild(chipGroup);
+        });
+    }
+}
+
+/**
+ * Creates the HTML structure for a single attribute group using chip/checkbox labels.
+ * Assumes the helper function 'el' is available.
+ * * @param {Object} p - The product object.
+ * @param {Object} attr - The specific attribute detail object.
+ * @param {string[]} initialSelected - Array of values that should be pre-checked.
+ * @param {boolean} isMandatory - True if the attribute is mandatory.
+ * @param {function} updateCallback - The function to call when a chip selection changes.
+ * @returns {HTMLElement} The attribute chip group container (div).
+ */
+function createAttributeChips(p, attr, initialSelected, isMandatory, updateCallback) {
+    const wrapper = el('div', 'attribute-chip-group');
+    wrapper.dataset.attrName = attr?.attribute_name;
+    
+    // Determine the header text based on structure preference (e.g., just the name)
+    const statusText = isMandatory ? ' (Mandatory)' : ' (Optional)';
+    wrapper.innerHTML = `<p class="attribute-header">${attr?.attribute_name}${statusText}:</p>`;
+
+    const chipContainer = el('div', 'chips-container');
+
+    attr?.possible_values.forEach(value => {
+        const chip = el('label', 'attribute-chip');
+        
+        // Checkbox input is hidden, but drives the selection state
+        const checkbox = document.createElement('input');
+        checkbox.type = 'checkbox';
+        checkbox.value = value;
+        // Ensure the name is unique per product/attribute group
+        checkbox.name = `${p.item_id}-${attr?.attribute_name}`; 
+        
+        // Set initial state
+        checkbox.checked = initialSelected.includes(value); 
+        
+        // The visual part of the chip
+        const span = el('span');
+        span.textContent = value;
+        
+        chip.appendChild(checkbox);
+        chip.appendChild(span);
+        chipContainer.appendChild(chip);
+    });
+    
+    // Attach listener to the container using event delegation
+    chipContainer.addEventListener('change', updateCallback);
+
+    wrapper.appendChild(chipContainer);
+    return wrapper;
+}
+
+// function renderProductsTable(items = getCurrentSlice()) {
+//     const wrap = document.getElementById('tableContainer');
+//     wrap.innerHTML = '';
+//     const table = document.createElement('table');
+//     table.classList.add('table', 'table-striped', 'table-bordered','table-responsive');
+    
+//     const thead = document.createElement('thead'); 
+//     const trh = document.createElement('tr');
+    
+//     // Table Headers
+//     ['Select', 'Image', 'Product', 'SKU', 'Type', 'Short Description'].forEach(h => {
+//         const th = document.createElement('th'); th.textContent = h; trh.appendChild(th);
+//     });
+//     thead.appendChild(trh); table.appendChild(thead);
+    
+//     const tbody = document.createElement('tbody');
+    
+//     if (items.length > 0) {
+//         items.forEach(p => {
+//             const tr = document.createElement('tr'); 
+//             tr.id = `row-${p.id}`;
+//             if (isProductSelected(p.item_id)) tr.classList.add('selected');
+
+//             // --- Define Checkbox (cb) and State Updater ---
+//             const cb = document.createElement('input'); 
+//             cb.type = 'checkbox'; 
+//             cb.checked = isProductSelected(p.item_id);
+//             // console.log("checkkkkk")
+            
+//             // The state updater function is bound to this specific row/checkbox
+//             const updateProductState = getProductStateUpdater(p, cb, tr);
+
+//             // --- Select Cell ---
+//             const tdSel = document.createElement('td'); 
+//             tdSel.className = 'select-cell';
+//             tdSel.appendChild(cb); 
+//             tr.appendChild(tdSel);
+
+//             // --- Other Cells ---
+//             const tdImg = document.createElement('td'); tdImg.className = 'thumb-cell'; tdImg.appendChild(createMiniThumb(p)); tr.appendChild(tdImg);
+//             const tdName = document.createElement('td'); tdName.textContent = p.product_name || '—'; tr.appendChild(tdName);
+//             const tdSku  = document.createElement('td'); tdSku.textContent = p.item_id || '—'; tr.appendChild(tdSku);
+//             const tdType = document.createElement('td'); const b = document.createElement('span'); b.className = 'badge'; b.textContent = p.product_type || '—'; tdType.appendChild(b); tr.appendChild(tdType);
+//             const tdDesc = document.createElement('td'); tdDesc.innerHTML = p.product_short_description || ''; tr.appendChild(tdDesc);
+
+//             // ---------------------------------------------
+//             // --- ATTRIBUTE SELECTION IMPLEMENTATION ---
+//             // ---------------------------------------------
+
+//             // 1. DETAIL ROW STRUCTURE
+//             const detailRow = document.createElement('tr');
+//             detailRow.classList.add('attribute-detail-row'); // Custom class for styling
+//             detailRow.style.display = 'none'; // Initially hidden
+//             detailRow.id = `detail-row-${p.id}`;
+            
+//             const detailCell = document.createElement('td');
+//             detailCell.colSpan = 6; // Must span all columns
+            
+//             const attrContainer = document.createElement('div');
+//             attrContainer.id = `attr-container-${p.item_id}`; // Unique ID for targeting by updateProductState
+//             attrContainer.classList.add('attribute-selectors', 'table-selectors');
+
+//             // 2. GENERATE CHIPS UI
+//             generateAttributeUI(p, updateProductState, attrContainer);
+
+//             // Initially disable the chips if the product is not selected
+//             attrContainer.classList.toggle('disabled', !cb.checked);
+
+//             detailCell.appendChild(attrContainer);
+//             detailRow.appendChild(detailCell);
+
+//             if(p.product_type_details.length > 0){
+//                 // 3. TOGGLE BUTTON (in the main row)
+//                 const tdAttr = document.createElement('td'); 
+//                 const toggleButton = document.createElement('button');
+//                 toggleButton.textContent = 'Configure';
+//                 toggleButton.classList.add('btn', 'btn-sm', 'btn-info', 'attribute-toggle-btn');
+//                 tdattr?.appendChild(toggleButton);
+//                 // tr.appendChild(tdAttr);
+            
+
+
+//             // 4. EVENT LISTENERS
+            
+//             // a) Toggle Button Logic
+//             toggleButton.addEventListener('click', (e) => {
+//                 e.stopPropagation(); // Stop row click event
+//                 const isHidden = detailRow.style.display === 'none';
+//                 detailRow.style.display = isHidden ? '' : 'none'; // Toggle visibility
+//                 toggleButton.textContent = isHidden ? 'Hide Attributes' : 'Configure';
+//                 toggleButton.classList.toggle('btn-info', !isHidden);
+//                 toggleButton.classList.toggle('btn-secondary', isHidden);
+//             });
+            
+//             // b) Main Checkbox Change Logic
+//             cb.addEventListener('change', () => { 
+//                 // console.log("cheeeeeeeeee");
+//                 updateProductState(); // Update state on check/uncheck
+//                 attrContainer.classList.toggle('disabled', !cb.checked); // Enable/Disable chips
+//             });
+
+//             // c) Row Click Listener (Updated to ignore button clicks)
+//             tr.addEventListener('click', (e) => { 
+//                 const tag = e.target.tagName.toLowerCase();
+//                 // console.log("clikk")
+//                 if (tag !== 'input' && tag !== 'button') { 
+//                     cb.checked = !cb.checked; 
+//                     cb.dispatchEvent(new Event('change')); 
+//                 } 
+//             });
+//             }else{
+//                 const tdAttr = document.createElement('td'); 
+//                 tr.appendChild(tdAttr);
+//                 // b) Main Checkbox Change Logic
+//                 cb.addEventListener('change', () => { 
+//                     // console.log("cheeeeeeeeee");
+//                     updateProductState(); // Update state on check/uncheck
+//                     attrContainer.classList.toggle('disabled', !cb.checked); // Enable/Disable chips
+//                 });
+//                 tr.addEventListener('click', (e) => { 
+//                     const tag = e.target.tagName.toLowerCase();
+//                     // console.log("clikk")
+//                     if (tag !== 'input' && tag !== 'button') { 
+//                         cb.checked = !cb.checked; 
+//                         cb.dispatchEvent(new Event('change')); 
+//                     } 
+//                 });
+//             }
+            
+//             // 5. Append Rows to TBODY
+//             tbody.appendChild(tr);
+//             tbody.appendChild(detailRow); // Append the detail row right after the main row
+//         });
+//     } else {
+//         const tr = el('tr'); 
+//         const tdName = el('td');
+//         tdName.colSpan = 6; 
+//         tdName.innerHTML = "No Products Found.";
+//         tr.appendChild(tdName);
+//         tbody.appendChild(tr);
+//     }
+
+//     table.appendChild(tbody);
+//     wrap.appendChild(table);
+// }
+
+function renderProductsTable(items = getCurrentSlice()) {
+    const wrap = document.getElementById('tableContainer');
+    wrap.innerHTML = '';
+    const table = document.createElement('table');
+    table.classList.add('table', 'table-striped', 'table-bordered', 'table-responsive');
+    table.id = 'productsTable'; // ✅ Add a unique table ID
+
+
+    const thead = document.createElement('thead');
+    const trh = document.createElement('tr');
+
+    // --- "Select All" Header with Checkbox ---
+    const thSelect = document.createElement('th');
+    const selectAllCheckbox = document.createElement('input');
+    selectAllCheckbox.type = 'checkbox';
+    selectAllCheckbox.id = 'selectAllCheckbox';
+    thSelect.appendChild(selectAllCheckbox);
+    trh.appendChild(thSelect);
+
+    // Other headers
+    ['Image', 'Product', 'SKU', 'Type', 'Description'].forEach(h => {
+        const th = document.createElement('th');
+        th.textContent = h;
+        trh.appendChild(th);
+    });
+    thead.appendChild(trh);
+    table.appendChild(thead);
+
+    const tbody = document.createElement('tbody');
+
+    if (items.length > 0) {
+        items.forEach(p => {
+            const tr = document.createElement('tr');
+            tr.id = `row-${p.id}`;
+            if (isProductSelected(p.item_id)) tr.classList.add('selected');
+
+            // Checkbox for each row
+            const cb = document.createElement('input');
+            cb.type = 'checkbox';
+            cb.classList.add('checkbox-productlist'); // ✅ correct way to add class
+            cb.checked = isProductSelected(p.item_id);
+
+            const updateProductState = getProductStateUpdater(p, cb, tr);
+
+            const tdSel = document.createElement('td');
+            tdSel.className = 'select-cell';
+            tdSel.appendChild(cb);
+            tr.appendChild(tdSel);
+
+            const tdImg = document.createElement('td');
+            tdImg.className = 'thumb-cell';
+            tdImg.appendChild(createMiniThumb(p));
+            tr.appendChild(tdImg);
+
+            const tdName = document.createElement('td');
+            tdName.textContent = p.product_name || '—';
+            tr.appendChild(tdName);
+
+            const tdSku = document.createElement('td');
+            tdSku.textContent = p.item_id || '—';
+            tr.appendChild(tdSku);
+
+            const tdType = document.createElement('td');
+            const b = document.createElement('span');
+            b.className = 'badge';
+            b.textContent = p.product_type || '—';
+            tdType.appendChild(b);
+            tr.appendChild(tdType);
+
+            // const tdDesc = document.createElement('td');
+            // tdDesc.innerHTML = p.product_short_description || '';
+            // tr.appendChild(tdDesc);
+
+            const tdDesc = document.createElement('td');
+
+            // Create a container for descriptions
+            const descContainer = document.createElement('div');
+            descContainer.classList.add('description-cell');
+
+            // Short description (always visible)
+            const shortDesc = document.createElement('div');
+            shortDesc.classList.add('short-desc');
+            shortDesc.innerHTML = `<strong>Short Description:</strong> ${p.product_short_description || 'N/A'}`;
+
+            // Long description (hidden initially)
+            const longDesc = document.createElement('div');
+            longDesc.classList.add('long-desc');
+            longDesc.innerHTML = `<strong>Long Description:</strong> ${p.product_long_description || 'N/A'}`;
+            longDesc.style.display = 'none';
+
+            // Button to toggle long description
+            const toggleBtn = document.createElement('button');
+            toggleBtn.textContent = 'Show Long Description';
+            toggleBtn.classList.add('btn');
+            toggleBtn.classList.add('btn-primary');
+            toggleBtn.classList.add('btn-sm');
+            toggleBtn.classList.add('btn-toggle-desc');
+            toggleBtn.style.fontSize = 'smaller';
+            toggleBtn.style.margin = '5px';
+
+
+            // Toggle logic
+            toggleBtn.addEventListener('click', () => {
+            const isVisible = longDesc.style.display === 'block';
+            longDesc.style.display = isVisible ? 'none' : 'block';
+            toggleBtn.textContent = isVisible ? 'Show Long Description' : 'Hide Long Description';
+            });
+
+            // Append elements in order
+            descContainer.appendChild(shortDesc);
+            descContainer.appendChild(toggleBtn);
+            descContainer.appendChild(longDesc);
+            tdDesc.appendChild(descContainer);
+            tr.appendChild(tdDesc);
+
+
+            // Handle attribute rows (kept your same logic)
+            const detailRow = document.createElement('tr');
+            detailRow.classList.add('attribute-detail-row');
+            detailRow.style.display = 'none';
+            detailRow.id = `detail-row-${p.id}`;
+
+            const detailCell = document.createElement('td');
+            detailCell.colSpan = 6;
+
+            const attrContainer = document.createElement('div');
+            attrContainer.id = `attr-container-${p.item_id}`;
+            attrContainer.classList.add('attribute-selectors', 'table-selectors');
+
+            generateAttributeUI(p, updateProductState, attrContainer);
+            attrContainer.classList.toggle('disabled', !cb.checked);
+
+            detailCell.appendChild(attrContainer);
+            detailRow.appendChild(detailCell);
+
+            // Checkbox behavior
+            cb.addEventListener('change', () => {
+                updateProductState();
+                attrContainer.classList.toggle('disabled', !cb.checked);
+            });
+
+            tr.addEventListener('click', (e) => {
+                const tag = e.target.tagName.toLowerCase();
+                if (tag !== 'input' && tag !== 'button') {
+                    cb.checked = !cb.checked;
+                    cb.dispatchEvent(new Event('change'));
+                }
+            });
+
+            tbody.appendChild(tr);
+            tbody.appendChild(detailRow);
+        });
+    } else {
+        const tr = document.createElement('tr');
+        const td = document.createElement('td');
+        td.colSpan = 6;
+        td.textContent = 'No Products Found.';
+        tr.appendChild(td);
+        tbody.appendChild(tr);
+    }
+
+    table.appendChild(tbody);
+    wrap.appendChild(table);
+
+    // --- Select All Checkbox Logic ---
+    selectAllCheckbox.addEventListener('change', () => {
+        // ✅ Only get product checkboxes inside *this* table
+        const allCheckboxes = table.querySelectorAll('tbody .checkbox-productlist');
+        console.log("allCheckboxes", allCheckboxes);
+
+        allCheckboxes.forEach(cb => {
+            cb.checked = selectAllCheckbox.checked;
+            cb.dispatchEvent(new Event('change'));
+        });
+    });
+
+    // --- Keep "Select All" synced with individual selections ---
+    tbody.addEventListener('change', (e) => {
+        if (e.target && e.target.classList.contains('checkbox-productlist')) {
+            // ✅ Again, limit scope to *this* table
+            const allCheckboxes = table.querySelectorAll('tbody .checkbox-productlist');
+            const allChecked = Array.from(allCheckboxes).every(cb => cb.checked);
+            const someChecked = Array.from(allCheckboxes).some(cb => cb.checked);
+
+            selectAllCheckbox.checked = allChecked;
+            selectAllCheckbox.indeterminate = !allChecked && someChecked;
+        }
+    });
+
+
+}
+
+
+// function renderInlineForCards() {
+//     const api = API_RESPONSE_AI;
+//     // Clear all inline sections first
+//     document.querySelectorAll('.attr-inline').forEach(div => div.innerHTML = '');
+
+//     PRODUCT_BASE.forEach((p, idx) => {
+//         const inline = document.querySelector(`.attr-inline[data-pid="${p.item_id}"]`);
+//         if (!inline) return;
+        
+//         // --- CHANGE HERE: Use the new helper function ---
+//         if (!isProductSelected(p.item_id)) return; // only show for selected
+        
+//         const res = findApiResultForProduct(p, idx, api);
+//         const pid = p.item_id;
+//         if (!lastSeen.has(pid)) lastSeen.set(pid, { mandatory: new Map(), additional: new Map(), ocr_results: new Map(), visual_results: new Map() });
+//         const mem = lastSeen.get(pid);
+
+//         // Build sections
+//         const manTitle = el('div', 'section-title'); manTitle.innerHTML = '<strong>Mandatory</strong>';
+//         const manChips = el('div', 'chips');
+//         const addTitle = el('div', 'section-title'); addTitle.innerHTML = '<strong>Additional</strong>';
+//         const addChips = el('div', 'chips');
+
+//         const addOcr = el('div', 'section-title'); addOcr.innerHTML = '<strong>Ocr</strong>';
+//         const ocrChips = el('div', 'chips');
+//         const addVisual = el('div', 'section-title'); addVisual.innerHTML = '<strong>Visual</strong>';
+//         const visualChips = el('div', 'chips');
+        
+
+//         const mandCount = renderChips(manChips, res?.mandatory || {}, mem.mandatory);
+//         const addCount = renderChips(addChips, res?.additional || {}, mem.additional);
+//         const ocrCount = renderChips(ocrChips, res?.ocr_results?.extracted_attributes || {}, mem?.ocr_results);
+//         const visualCount = renderChips(visualChips, res?.visual_results?.visual_attributes || {}, mem?.visual_results);
+
+//         const counts = el('div'); counts.style.display = 'flex'; counts.style.gap = '8px'; counts.style.margin = '8px 0 0';
+//         const c1 = el('span', 'pill'); c1.textContent = `Mandatory: ${mandCount}`;
+//         const c2 = el('span', 'pill'); c2.textContent = `Additional: ${addCount}`;
+//         const c3 = el('span', 'pill'); c3.textContent = `OCR: ${ocrCount}`;
+//         const c4 = el('span', 'pill'); c4.textContent = `Visuals: ${visualCount}`;
+//         counts.appendChild(c1); counts.appendChild(c2);  counts.appendChild(c3);  counts.appendChild(c4);
+
+//         inline.appendChild(manTitle); inline.appendChild(manChips);
+//         inline.appendChild(addTitle); inline.appendChild(addChips);
+//         inline.appendChild(addOcr); inline.appendChild(ocrChips);
+//         inline.appendChild(addVisual); inline.appendChild(visualChips);
+//         inline.appendChild(counts);
+//     });
+
+//     // Update summary
+//     $('#statTotal').textContent = api.total_products ?? 0;
+//     $('#statOk').textContent = api.successful ?? 0;
+//     $('#statKo').textContent = api.failed ?? 0;
+//     $('#api-summary').style.display = 'block';
+// }
+
+// -----------------------------------------------------------
+
+// function renderInlineForTable() {
+//     const api = API_RESPONSE_AI;
+//     const table = $('#tableContainer');
+//     if (!table) return;
+//     // Remove existing detail rows
+//     table.querySelectorAll('tr.detail-row').forEach(r => r.remove());
+
+//     PRODUCT_BASE.forEach((p, idx) => {
+//         // --- CHANGE HERE: Use the new helper function ---
+//         if (!isProductSelected(p.item_id)) return;
+        
+//         const res = findApiResultForProduct(p, idx, api);
+//         const pid = p.item_id;
+//         if (!lastSeen.has(pid)) lastSeen.set(pid, { mandatory: new Map(), additional: new Map(), ocr_results: new Map(), visual_results: new Map() });
+//         const mem = lastSeen.get(pid);
+
+//         const tbody = table.querySelector('tbody');
+//         // NOTE: The table rendering uses p.id for the row ID: `row-${p.id}`.
+//         // Assuming p.id is still valid for finding the base row, as your original code used it.
+//         const baseRow = tbody.querySelector(`#row-${p.id}`); 
+//         if (!baseRow) return;
+
+//         const detail = el('tr', 'detail-row');
+//         const td = el('td'); td.colSpan = 6; // number of columns
+//         const content = el('div', 'detail-content');
+
+//         const manTitle = el('div', 'section-title'); manTitle.innerHTML = '<strong>Mandatory</strong>';
+//         const manChips = el('div', 'chips');
+//         const addTitle = el('div', 'section-title'); addTitle.innerHTML = '<strong>Additional</strong>';
+//         const addChips = el('div', 'chips');
+//         const addOcr = el('div', 'section-title'); addOcr.innerHTML = '<strong>Ocr</strong>';
+//         const ocrChips = el('div', 'chips');
+//         const addVisuals = el('div', 'section-title'); addVisuals.innerHTML = '<strong>Visuals</strong>';
+//         const visualsChips = el('div', 'chips');
+
+//         const mandCount = renderChips(manChips, res?.mandatory || {}, mem.mandatory);
+//         const addCount = renderChips(addChips, res?.additional || {}, mem.additional);
+//         const ocrCount = renderChips(ocrChips, res?.ocr_results?.extracted_attributes || {}, mem.ocr_results);
+//         const visualCount = renderChips(visualsChips, res?.visual_results?.visual_attributes || {}, mem.visual_results);
+
+//         const counts = el('div'); counts.style.display = 'flex'; counts.style.gap = '8px'; counts.style.margin = '8px 0 0';
+//         const c1 = el('span', 'pill'); c1.textContent = `Mandatory: ${mandCount}`;
+//         const c2 = el('span', 'pill'); c2.textContent = `Additional: ${addCount}`;
+//         const c3 = el('span', 'pill'); c3.textContent = `Ocr: ${ocrCount}`;
+//         const c4 = el('span', 'pill'); c4.textContent = `Visuals: ${visualCount}`;
+//         counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
+
+//         content.appendChild(manTitle); content.appendChild(manChips);
+//         content.appendChild(addTitle); content.appendChild(addChips);
+//         content.appendChild(addOcr); content.appendChild(ocrChips);
+//         content.appendChild(addVisuals); content.appendChild(visualsChips);
+//         content.appendChild(counts);
+//         td.appendChild(content); detail.appendChild(td);
+
+//         // insert after base row
+//         baseRow.insertAdjacentElement('afterend', detail);
+//     });
+
+//     // Update summary
+//     $('#statTotal').textContent = api.total_products ?? 0;
+//     $('#statOk').textContent = api.successful ?? 0;
+//     $('#statKo').textContent = api.failed ?? 0;
+//     $('#api-summary').style.display = 'block';
+// }
+
+function renderInlineForCards() {
+    const api = API_RESPONSE_AI;
+    document.querySelectorAll('.attr-inline').forEach(div => div.innerHTML = '');
+
+    PRODUCT_BASE.forEach((p, idx) => {
+        const inline = document.querySelector(`.attr-inline[data-pid="${p.item_id}"]`);
+        if (!inline) return;
+        
+        if (!isProductSelected(p.item_id)) return;
+        
+        const res = findApiResultForProduct(p, idx, api);
+        
+        // Clear existing content
+        inline.innerHTML = '';
+
+
+        // 1. MANDATORY SECTION: RENDER AS COMPARISON CARDS
+        const mandatorySection = renderMandatoryComparisonCards(
+            res?.mandatory || {}, 
+            'Mandatory Attributes Comparison'
+        );
+        inline.appendChild(mandatorySection);
+        const mandCount = Object.keys(res?.mandatory || {}).length;
+        
+
+        const combinedTitle = el('div', 'section-title');
+        combinedTitle.innerHTML = '<h2>Additional & AI-Driven Attributes</h2>';
+        inline.appendChild(combinedTitle);
+
+        const combinedAttributesContainer = el('div', 'combined-attributes-container');
+
+        // 2. ADDITIONAL SECTION: RENDER AS SIMPLE TABLE
+        const additionalSection = renderAttributesAsTable(
+            res?.additional || {}, 
+            'Additional Attributes'
+        );
+        // inline.appendChild(additionalSection);
+        const addCount = Object.keys(res?.additional || {}).length;
+        
+        // 3. OCR SECTION: RENDER AS SIMPLE TABLE
+        const ocrSection = renderAttributesAsTable(
+            res?.ocr_results?.extracted_attributes || {}, 
+            'OCR Results'
+        );
+        // inline.appendChild(ocrSection);
+        const ocrCount = Object.keys(res?.ocr_results?.extracted_attributes || {}).length;
+        
+        // 4. VISUAL SECTION: RENDER AS SIMPLE TABLE
+        const visualSection = renderAttributesAsTable(
+            res?.visual_results?.visual_attributes || {}, 
+            'Visual Attributes'
+        );
+        // inline.appendChild(visualSection);
+        const visualCount = Object.keys(res?.visual_results?.visual_attributes || {}).length;
+        combinedAttributesContainer.appendChild(additionalSection);
+        combinedAttributesContainer.appendChild(ocrSection);
+        combinedAttributesContainer.appendChild(visualSection);
+        inline.appendChild(combinedAttributesContainer);
+        // --- Summary Counts (Pills) ---
+        const counts = el('div'); counts.style.display = 'flex'; counts.style.gap = '8px'; counts.style.margin = '8px 0 0';
+        const c1 = el('span', 'pill'); c1.textContent = `Mandatory: ${mandCount}`;
+        const c2 = el('span', 'pill'); c2.textContent = `Additional: ${addCount}`;
+        const c3 = el('span', 'pill'); c3.textContent = `OCR: ${ocrCount}`;
+        const c4 = el('span', 'pill'); c4.textContent = `Visuals: ${visualCount}`;
+        counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
+
+        inline.appendChild(counts);
+    });
+
+    // Update summary
+    $('#statTotal').textContent = selectedProductsWithAttributes.length ?? api.total_products ?? 0;
+    $('#statOk').textContent = selectedProductsWithAttributes.length ?? api.successful ?? 0;
+    $('#statKo').textContent = api.failed ?? 0;
+    $('#api-summary').style.display = 'block';
+}
+
+// function renderInlineForCards() {
+//     const api = API_RESPONSE_AI;
+//     // Clear all inline sections first
+//     document.querySelectorAll('.attr-inline').forEach(div => div.innerHTML = '');
+
+//     PRODUCT_BASE.forEach((p, idx) => {
+//         const inline = document.querySelector(`.attr-inline[data-pid="${p.item_id}"]`);
+//         if (!inline) return;
+        
+//         if (!isProductSelected(p.item_id)) return;
+        
+//         const res = findApiResultForProduct(p, idx, api);
+//         const pid = p.item_id;
+//         // Memory map (mem) is no longer needed since we removed chip rendering
+//         // I'll keep the variable declarations for count consistency, but remove the memory map usage.
+
+//         // --- 1. MANDATORY SECTION: RENDER AS COMPARISON CARDS ---
+//         const mandatorySection = renderMandatoryComparisonCards(
+//             res?.mandatory || {}, 
+//             'Mandatory Attributes Comparison'
+//         );
+//         inline.appendChild(mandatorySection);
+        
+//         const mandCount = Object.keys(res?.mandatory || {}).length;
+        
+//         // --- 2. ADDITIONAL SECTION: RENDER AS SIMPLE CARDS ---
+//         const additionalSection = renderAttributesAsTable(
+//             res?.additional || {}, 
+//             'Additional Attributes'
+//         );
+//         inline.appendChild(additionalSection);
+//         const addCount = Object.keys(res?.additional || {}).length;
+        
+//         // --- 3. OCR SECTION: RENDER AS SIMPLE CARDS ---
+//         const ocrSection = renderAttributesAsTable(
+//             res?.ocr_results?.extracted_attributes || {}, 
+//             'OCR Results'
+//         );
+//         inline.appendChild(ocrSection);
+//         const ocrCount = Object.keys(res?.ocr_results?.extracted_attributes || {}).length;
+        
+//         // --- 4. VISUAL SECTION: RENDER AS SIMPLE CARDS ---
+//         const visualSection = renderAttributesAsTable(
+//             res?.visual_results?.visual_attributes || {}, 
+//             'Visual Results'
+//         );
+//         inline.appendChild(visualSection);
+//         const visualCount = Object.keys(res?.visual_results?.visual_attributes || {}).length;
+        
+//         // --- Summary Counts (Pills) ---
+//         const counts = el('div'); counts.style.display = 'flex'; counts.style.gap = '8px'; counts.style.margin = '8px 0 0';
+//         const c1 = el('span', 'pill'); c1.textContent = `Mandatory: ${mandCount}`;
+//         const c2 = el('span', 'pill'); c2.textContent = `Additional: ${addCount}`;
+//         const c3 = el('span', 'pill'); c3.textContent = `OCR: ${ocrCount}`;
+//         const c4 = el('span', 'pill'); c4.textContent = `Visuals: ${visualCount}`;
+//         counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
+
+//         inline.appendChild(counts);
+//     });
+
+//     // Update summary
+//     $('#statTotal').textContent = api.total_products ?? 0;
+//     $('#statOk').textContent = api.successful ?? 0;
+//     $('#statKo').textContent = api.failed ?? 0;
+//     $('#api-summary').style.display = 'block';
+// }
+
+// ----------------------------------------------------------------
+// NOTE: You MUST include renderMandatoryComparisonCards (from previous response)
+// and the necessary CSS for both card styles!
+// ----------------------------------------------------------------
+
+// ------------------------------------------------------------------
+// --- 1. MANDATORY COMPARISON HELPER (Existing vs. AI, with Highlighting) ---
+// ------------------------------------------------------------------
+
+// function renderInlineForCards() {
+//     const api = API_RESPONSE_AI;
+//     // Clear all inline sections first
+//     document.querySelectorAll('.attr-inline').forEach(div => div.innerHTML = '');
+
+//     PRODUCT_BASE.forEach((p, idx) => {
+//         const inline = document.querySelector(`.attr-inline[data-pid="${p.item_id}"]`);
+//         if (!inline) return;
+        
+//         // --- CHANGE HERE: Use the new helper function ---
+//         if (!isProductSelected(p.item_id)) return; // only show for selected
+        
+//         const res = findApiResultForProduct(p, idx, api);
+//         const pid = p.item_id;
+//         // Keep memory logic for old chip renderer, even though card renderer doesn't need it
+//         if (!lastSeen.has(pid)) lastSeen.set(pid, { mandatory: new Map(), additional: new Map(), ocr_results: new Map(), visual_results: new Map() });
+//         const mem = lastSeen.get(pid);
+
+//         // ------------------------------------------------
+//         // 1. MANDATORY SECTION: RENDER AS COMPARISON CARDS
+//         // ------------------------------------------------
+//         const mandatorySection = renderMandatoryComparisonCards(
+//             res?.mandatory || {}, 
+//             'Mandatory Attributes Comparison'
+//         );
+//         inline.appendChild(mandatorySection);
+        
+//         // Count the attributes for the summary pill
+//         const mandCount = Object.keys(res?.mandatory || {}).length;
+        
+//         // ------------------------------------------------
+//         // 2. ADDITIONAL/OCR/VISUALS: RENDER AS CHIPS (Original Logic)
+//         // ------------------------------------------------
+        
+//         // ADDITIONAL
+//         const addTitle = el('div', 'section-title'); addTitle.innerHTML = '<strong>Additional</strong>';
+//         const addChips = el('div', 'chips');
+//         const addCount = renderChips(addChips, res?.additional || {}, mem.additional);
+//         inline.appendChild(addTitle); inline.appendChild(addChips);
+        
+//         // OCR
+//         const addOcr = el('div', 'section-title'); addOcr.innerHTML = '<strong>Ocr</strong>';
+//         const ocrChips = el('div', 'chips');
+//         const ocrCount = renderChips(ocrChips, res?.ocr_results?.extracted_attributes || {}, mem?.ocr_results);
+//         inline.appendChild(addOcr); inline.appendChild(ocrChips);
+        
+//         // VISUALS
+//         const addVisual = el('div', 'section-title'); addVisual.innerHTML = '<strong>Visual</strong>';
+//         const visualChips = el('div', 'chips');
+//         const visualCount = renderChips(visualChips, res?.visual_results?.visual_attributes || {}, mem?.visual_results);
+//         inline.appendChild(addVisual); inline.appendChild(visualChips);
+        
+//         // --- Summary Counts (Pills) ---
+//         const counts = el('div'); counts.style.display = 'flex'; counts.style.gap = '8px'; counts.style.margin = '8px 0 0';
+//         const c1 = el('span', 'pill'); c1.textContent = `Mandatory: ${mandCount}`;
+//         const c2 = el('span', 'pill'); c2.textContent = `Additional: ${addCount}`;
+//         const c3 = el('span', 'pill'); c3.textContent = `OCR: ${ocrCount}`;
+//         const c4 = el('span', 'pill'); c4.textContent = `Visuals: ${visualCount}`;
+//         counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
+
+//         inline.appendChild(counts);
+//     });
+
+//     // Update summary
+//     $('#statTotal').textContent = api.total_products ?? 0;
+//     $('#statOk').textContent = api.successful ?? 0;
+//     $('#statKo').textContent = api.failed ?? 0;
+//     $('#api-summary').style.display = 'block';
+// }
+
+/**
+ * Renders a table for Mandatory attributes, comparing AI-extracted value ('value') 
+ * against the existing value ('original_value'). Includes a scroll wrapper.
+ * @param {Object} attributes - The mandatory attribute data.
+ * @param {string} title - The title for the table section.
+ * @returns {HTMLElement} A div containing the comparison table.
+ */
+// function renderMandatoryComparisonTable(attributes, title) {
+//     const section = el('div', 'attribute-section');
+    
+//     let attributeEntries = [];
+
+//     Object.keys(attributes).forEach(key => {
+//         const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]]; 
+//         valuesArray.forEach(v => {
+//             const aiValue = v.value || 'N/A';
+//             const originalValue = v.original_value || 'N/A';
+//             const source = v.source || 'N/A';
+            
+//             // Comparison is case-insensitive and ignores leading/trailing whitespace
+//             const isMatch = (String(aiValue).trim().toLowerCase() === String(originalValue).trim().toLowerCase());
+
+//             attributeEntries.push({
+//                 name: key,
+//                 aiValue: aiValue,
+//                 originalValue: originalValue,
+//                 source: source,
+//                 isMatch: isMatch
+//             });
+//         });
+//     });
+
+//     const titleEl = el('div', 'section-title');
+//     titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+//     section.appendChild(titleEl);
+    
+//     if (attributeEntries.length === 0) {
+//         const msg = el('p', 'no-attributes-message');
+//         msg.textContent = `No ${title.toLowerCase()} found.`;
+//         section.appendChild(msg);
+//         return section;
+//     }
+
+//     // --- SCROLL WRAPPER ADDITION ---
+//     const scrollWrapper = el('div', 'attribute-scroll-wrapper');
+//     const table = el('table', 'attribute-detail-table comparison-table');
+    
+//     const thead = el('thead');
+//     const headerRow = el('tr');
+    
+//     ['Attribute Name', 'Source', 'Manually Identified Value', 'AI Generated Value'].forEach(text => {
+//         const th = el('th');
+//         th.textContent = text;
+//         headerRow.appendChild(th);
+//     });
+//     thead.appendChild(headerRow);
+//     table.appendChild(thead);
+
+//     const tbody = el('tbody');
+
+//     attributeEntries.forEach(attr => {
+//         // Highlight the entire row in red if the values do not match
+//         const row = el('tr', attr?.isMatch ? 'match' : 'mismatch-row'); 
+        
+//         // 1. Attribute Name
+//         const nameTd = el('td', 'attribute-name');
+//         nameTd.textContent = attr?.name.replace(/_/g, ' '); 
+//         row.appendChild(nameTd);
+
+//         // 2. Source
+//         const sourceTd = el('td', 'attribute-source');
+//         sourceTd.textContent = formatString(attr?.source);
+//         row.appendChild(sourceTd);
+
+
+//         // 3. Existing Value
+//         const originalTd = el('td', 'original-value');
+//         originalTd.textContent = formatString(attr?.originalValue);
+//         row.appendChild(originalTd);
+
+//         // 4. AI Extracted Value (Highlight if mismatch)
+//         const aiTd = el('td', `ai-value ${attr?.aiValue ? '' : 'mismatch-value'}`);
+//         aiTd.textContent = attr?.aiValue;
+//         row.appendChild(aiTd);
+        
+
+//         // 5. Match Status
+//         // const matchTd = el('td', 'match-status');
+//         // const matchPill = el('span', `pill status-pill status-${attr?.isMatch ? 'match' : 'mismatch'}`);
+//         // matchPill.textContent = attr?.isMatch ? '✅ MATCH' : '❌ MISMATCH';
+//         // matchTd.appendChild(matchPill);
+//         // row.appendChild(matchTd);
+
+//         tbody.appendChild(row);
+//     });
+
+//     table.appendChild(tbody);
+//     scrollWrapper.appendChild(table); // Append table to wrapper
+//     section.appendChild(scrollWrapper); // Append wrapper to section
+//     return section;
+// }
+
+// ------------------------------------------------------------------
+// --- 2. GENERAL ATTRIBUTE HELPER (Name, Value, Source, with Scroll) ---
+// ------------------------------------------------------------------
+/**
+ * Renders a table for Mandatory attributes, comparing AI-extracted value ('value') 
+ * against the existing value ('original_value'). Includes a scroll wrapper and mismatch highlighting.
+ * @param {Object} attributes - The mandatory attribute data.
+ * @param {string} title - The title for the table section.
+ * @returns {HTMLElement} A div containing the comparison table.
+ */
+function renderMandatoryComparisonTable(attributes, title, productType) {
+    const section = el('div', 'attribute-section');
+
+    // --- 1. Intermediate object for merging values ---
+    let mergedAttributes = {};
+
+    // --- Build mergedAttributes ---
+    Object.keys(attributes).forEach(key => {
+        const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]];
+
+        if (!mergedAttributes[key]) {
+            mergedAttributes[key] = {
+                aiValues: new Set(),
+                originalValues: new Set(),
+                sources: new Set(),
+                reasons: new Set(),
+                // Store the first encountered config for possibleValues lookup
+                firstValue: valuesArray[0]
+            };
+        }
+
+        valuesArray.forEach(v => {
+            const aiValue = v.value || 'N/A';
+            const originalValue = v.original_value || 'N/A';
+            const source = v.source || 'N/A';
+            const reason = v.reason || 'N/A';
+
+            // Add values to sets for unique collection
+            if (aiValue !== 'N/A') mergedAttributes[key].aiValues.add(aiValue);
+            if (originalValue !== 'N/A') mergedAttributes[key].originalValues.add(originalValue);
+            if (source !== 'N/A') mergedAttributes[key].sources.add(source);
+            if (reason !== 'N/A') mergedAttributes[key].reasons.add(reason);
+        });
+    });
+    
+    // --- 2. Final attributeEntries from merged data ---
+    let attributeEntries = [];
+
+    Object.keys(mergedAttributes).forEach(key => {
+        const mergedData = mergedAttributes[key];
+        
+        // Find possible values for this attribute from full data using the stored firstValue
+        const attrConfig = attributesFullData.find(item => item.attribute_name === key);
+        
+        let possibleValues = [];
+        if (attrConfig && attrConfig.possible_values) {
+            possibleValues = attrConfig.possible_values.split(',').map(s => s.trim());
+        }
+
+        // Get merged AI Value
+        const aiValueString = Array.from(mergedData.aiValues).join(', ');
+        
+        // Determine match flag: check if ANY of the AI values are in possibleValues
+        const isFoundInPossible = Array.from(mergedData.aiValues).some(aiVal => possibleValues.includes(aiVal));
+        const matchFlag = isFoundInPossible ? true : false;
+        
+        // Get merged Original Value, Source, and Reason strings
+        const originalValueString = Array.from(mergedData.originalValues).join(', ');
+        const sourceString = Array.from(mergedData.sources).join(', ');
+        const reasonString = Array.from(mergedData.reasons).join(' | ');
+
+        attributeEntries.push({
+            name: key,
+            aiValue: aiValueString || 'N/A', // Use merged string
+            possibleValues: possibleValues,
+            originalValue: originalValueString || 'N/A',
+            source: sourceString || 'N/A',
+            isMatch: matchFlag,
+            reason: reasonString || 'N/A'
+        });
+    });
+
+    // --- Rest of the function (unchanged) ---
+    // ... (Section title, empty check, splitting into two tables, and buildTable helper) ...
+
+    // --- Section title ---
+    const titleEl = el('div', 'section-title');
+    titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+    section.appendChild(titleEl);
+
+    if (attributeEntries.length === 0) {
+        const msg = el('p', 'no-attributes-message');
+        msg.textContent = `No ${title.toLowerCase()} found.`;
+        section.appendChild(msg);
+        return section;
+    }
+
+    // --- Split attributes in half ---
+    const midIndex = Math.ceil(attributeEntries.length / 2);
+    const leftAttributes = attributeEntries.slice(0, midIndex);
+    const rightAttributes = attributeEntries.slice(midIndex);
+
+    // --- Create a container for two tables ---
+    const tableContainer = el('div', 'two-column-table-container');
+
+    // Helper function to build a table
+    function buildTable(attrArray) {
+        const scrollWrapper = el('div', 'attribute-scroll-wrapper');
+        const table = el('table', 'attribute-detail-table comparison-table');
+
+        const thead = el('thead');
+        const headerRow = el('tr');
+        // , 'AI Generated Value'
+        ['Attribute Name', 'Recommended Attribute Value(s)'].forEach(text => {
+            const th = el('th');
+            th.textContent = text;
+            headerRow.appendChild(th);
+        });
+        thead.appendChild(headerRow);
+        table.appendChild(thead);
+
+        const tbody = el('tbody');
+
+        attrArray.forEach(attr => {
+            const row = el('tr', attr?.isMatch ? 'match-row' : 'mismatch-row');
+
+            // Attribute name
+            const nameTd = el('td', 'attribute-name');
+            nameTd.textContent = formatString(attr?.name).replace(/_/g, ' ');
+            row.appendChild(nameTd);
+
+            // Dropdown
+            const aiTd = el('td', 'attribute-source');
+
+            // Create a multi-select dropdown
+            const select = document.createElement('select');
+            select.classList.add('select2-dropdown');
+            select.name = 'manuallyUpdatedAttributes[]';
+            select.setAttribute('multiple', 'multiple');
+            select.setAttribute('data-attribute', attr?.name);
+
+            // The values selected will be the merged AI values
+            const selectedValues = attr?.aiValue.split(', ').filter(v => v !== 'N/A' && v.trim() !== '');
+
+            // Populate options
+            // Add possible values
+            // attr?.possibleValues.forEach(val => {
+            //     const option = document.createElement('option');
+            //     option.value = val;
+            //     option.textContent = val;
+            //     if (selectedValues.includes(val)) option.selected = true;
+            //     select.appendChild(option);
+            // });
+
+            // attr?.possibleValues.forEach(val => {
+            //     // Split by comma if there are multiple values, trim spaces
+            //     const values = val.split(',').map(v => v.trim());
+
+            //     values.forEach(v => {
+            //         const option = document.createElement('option');
+            //         option.value = v;
+            //         option.textContent = v;
+            //         if (selectedValues.includes(v)) option.selected = true;
+            //         select.appendChild(option);
+            //     });
+            // });
+
+            
+            // Add AI values not found in possibleValues as new selected options
+            // selectedValues.forEach(aiVal => {
+            //     if (!attr?.possibleValues.includes(aiVal)) {
+            //         const newOpt = document.createElement('option');
+            //         newOpt.value = aiVal;
+            //         newOpt.textContent = aiVal;
+            //         newOpt.selected = true;
+            //         select.appendChild(newOpt);
+            //     }
+            // });
+            // 1. Prepare and clean the selected values first, flattening the comma-separated data.
+                let allSelectedValues = new Set();
+                selectedValues.forEach(aiVal => {
+                    aiVal.split(',')
+                        .map(value => value.trim())
+                        .filter(value => value.length > 0)
+                        .forEach(singleVal => allSelectedValues.add(singleVal));
+                });
+
+                // Convert the Set back to an array for easy checking, or keep it as a Set for O(1) lookups.
+                // We'll keep it as a Set for efficient checking.
+
+                // ---
+
+                // 2. Add all default possible values.
+                attr?.possibleValues.forEach(val => {
+                    const option = document.createElement('option');
+                    option.value = val;
+                    option.textContent = val;
+
+                    // Check if the possible value is one of the cleaned selected values
+                    if (allSelectedValues.has(val)) {
+                        option.selected = true;
+                        // IMPORTANT: Remove this value from the Set so we know which ones are left over (the custom ones)
+                        allSelectedValues.delete(val);
+                    }
+                    
+                    select.appendChild(option);
+                });
+
+                // ---
+
+                // 3. Add any "custom" selected values that weren't in possibleValues.
+                // The allSelectedValues Set now only contains values that are NOT in attr?.possibleValues.
+                allSelectedValues.forEach(singleVal => {
+                    const newOpt = document.createElement('option');
+                    newOpt.value = singleVal;
+                    newOpt.textContent = singleVal;
+                    newOpt.selected = true; // These are guaranteed to be selected values
+                    select.appendChild(newOpt);
+                });
+            aiTd.appendChild(select);
+            row.appendChild(aiTd);
+
+            tbody.appendChild(row);
+
+            // Initialize Select2
+            jQuery(select).select2({
+                tags: true,
+                width: 'resolve'
+            });
+        });
+
+        table.appendChild(tbody);
+        scrollWrapper.appendChild(table);
+        return scrollWrapper;
+    }
+
+    // --- Build and append both tables ---
+    const leftTable = buildTable(leftAttributes);
+    const rightTable = buildTable(rightAttributes);
+
+    tableContainer.appendChild(leftTable);
+    tableContainer.appendChild(rightTable);
+
+    section.appendChild(tableContainer);
+    return section;
+}
+
+// function renderMandatoryComparisonTable(attributes, title, productType) {
+//     const section = el('div', 'attribute-section');
+
+//     let attributeEntries = [];
+
+//     // --- Build attributeEntries ---
+//     Object.keys(attributes).forEach(key => {
+//         const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]];
+//         valuesArray.forEach(v => {
+//             const aiValue = v.value || 'N/A';
+//             const originalValue = v.original_value || 'N/A';
+//             const source = v.source || 'N/A';
+//             const reason = v.reason || 'N/A';
+
+//             // Find possible values for this attribute from full data
+//             const attrConfig = attributesFullData.find(item => item.attribute_name === key);
+            
+//             let possibleValues = [];
+//             if (attrConfig && attrConfig.possible_values) {
+//                 possibleValues = attrConfig.possible_values.split(',').map(s => s.trim());
+//             }
+
+//             // Determine match flag
+//             const isFoundInPossible = possibleValues.includes(aiValue);
+//             const matchFlag = isFoundInPossible ? true : false;
+
+//             attributeEntries.push({
+//                 name: key,
+//                 aiValue: aiValue,
+//                 possibleValues: possibleValues,
+//                 originalValue: originalValue,
+//                 source: source,
+//                 isMatch: matchFlag,
+//                 reason: reason
+//             });
+//         });
+//     });
+
+//     // --- Section title ---
+//     const titleEl = el('div', 'section-title');
+//     titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+//     section.appendChild(titleEl);
+
+//     if (attributeEntries.length === 0) {
+//         const msg = el('p', 'no-attributes-message');
+//         msg.textContent = `No ${title.toLowerCase()} found.`;
+//         section.appendChild(msg);
+//         return section;
+//     }
+
+//     // --- Split attributes in half ---
+//     const midIndex = Math.ceil(attributeEntries.length / 2);
+//     const leftAttributes = attributeEntries.slice(0, midIndex);
+//     const rightAttributes = attributeEntries.slice(midIndex);
+
+//     // --- Create a container for two tables ---
+//     const tableContainer = el('div', 'two-column-table-container');
+
+//     // Helper function to build a table
+//     function buildTable(attrArray) {
+//         const scrollWrapper = el('div', 'attribute-scroll-wrapper');
+//         const table = el('table', 'attribute-detail-table comparison-table');
+
+//         const thead = el('thead');
+//         const headerRow = el('tr');
+//         // , 'AI Generated Value'
+//         ['Attribute Name', 'Recommended Attribute Value(s)'].forEach(text => {
+//             const th = el('th');
+//             th.textContent = text;
+//             headerRow.appendChild(th);
+//         });
+//         thead.appendChild(headerRow);
+//         table.appendChild(thead);
+
+//         const tbody = el('tbody');
+
+//         attrArray.forEach(attr => {
+//             const row = el('tr', attr?.isMatch ? 'match-row' : 'mismatch-row');
+
+//             // Attribute name
+//             const nameTd = el('td', 'attribute-name');
+//             nameTd.textContent = formatString(attr?.name).replace(/_/g, ' ');
+//             row.appendChild(nameTd);
+
+//             // AI Value display
+//             // AI Value cell with info icon
+//             // const aiValueTd = el('td', `ai-value ${attr?.isMatch ? '' : 'mismatch-value'}`);
+
+//             // // Create a wrapper for text and icon
+//             // const aiValueWrapper = document.createElement('div');
+//             // aiValueWrapper.style.display = 'flex';
+//             // aiValueWrapper.style.alignItems = 'center';
+//             // aiValueWrapper.style.gap = '6px';
+
+//             // // Text node
+//             // const valueText = document.createElement('span');
+//             // valueText.textContent = formatString(attr?.aiValue);
+//             // aiValueWrapper.appendChild(valueText);
+
+//             // // Info icon (Bootstrap Icons)
+//             // if (attr?.reason && attr?.reason.trim() !== '') {
+//             //     const infoIcon = document.createElement('i');
+//             //     infoIcon.className = 'bi bi-info-circle';
+//             //     infoIcon.style.cursor = 'pointer';
+//             //     infoIcon.setAttribute('title', attr?.reason); // Tooltip on hover
+//             //     infoIcon.style.color = '#0d6efd'; // Bootstrap blue
+//             //     aiValueWrapper.appendChild(infoIcon);
+//             // }
+
+//             // aiValueTd.appendChild(aiValueWrapper);
+//             // row.appendChild(aiValueTd);
+
+//             // const aiValueTd = el('td', `ai-value ${attr?.isMatch ? '' : 'mismatch-value'}`);
+//             // aiValueTd.textContent = formatString(attr?.aiValue);
+//             // row.appendChild(aiValueTd);
+
+//             // Dropdown
+//             // const aiTd = el('td', 'attribute-source');
+//             // const select = document.createElement('select');
+//             // select.classList.add('select2-dropdown');
+//             // select.setAttribute('data-attribute', attr?.name);
+            
+//             const aiTd = el('td', 'attribute-source');
+
+//             // Create a multi-select dropdown
+//             const select = document.createElement('select');
+//             select.classList.add('select2-dropdown');
+//             // select.id = 'manually-attributes'; // You may want to make this unique per row
+//             select.name = 'manuallyUpdatedAttributes[]';
+//             // select.setAttribute('aria-labelledby', 'select a attribute for which multiple data required');
+//             select.setAttribute('multiple', 'multiple');
+//             // select.style.width = '100%';
+//             select.setAttribute('data-attribute', attr?.name);
+
+
+//             // Populate options
+//             attr?.possibleValues.forEach(val => {
+//                 const option = document.createElement('option');
+//                 option.value = val;
+//                 option.textContent = val;
+//                 if (val === attr?.aiValue) option.selected = true;
+//                 select.appendChild(option);
+//             });
+
+//             // if (!attr?.isMatch && attr?.aiValue !== 'N/A') {
+//             //     const newOpt = document.createElement('option');
+//             //     newOpt.value = attr?.aiValue;
+//             //     newOpt.textContent = attr?.aiValue; // + " (new)";
+//             //     newOpt.selected = true;
+//             //     select.appendChild(newOpt);
+//             // }
+
+//             aiTd.appendChild(select);
+//             row.appendChild(aiTd);
+
+//             tbody.appendChild(row);
+
+//             // Initialize Select2
+//             jQuery(select).select2({
+//                 tags: true,
+//                 width: 'resolve'
+//             });
+//         });
+
+//         table.appendChild(tbody);
+//         scrollWrapper.appendChild(table);
+//         return scrollWrapper;
+//     }
+
+//     // --- Build and append both tables ---
+//     const leftTable = buildTable(leftAttributes);
+//     const rightTable = buildTable(rightAttributes);
+
+//     tableContainer.appendChild(leftTable);
+//     tableContainer.appendChild(rightTable);
+
+//     section.appendChild(tableContainer);
+//     return section;
+// }
+
+// function renderMandatoryComparisonTable(attributes, title, productType) {
+//     const section = el('div', 'attribute-section');
+
+//     let attributeEntries = [];
+
+//     Object.keys(attributes).forEach(key => {
+//         const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]];
+//         valuesArray.forEach(v => {
+//             const aiValue = v.value || 'N/A';
+//             const originalValue = v.original_value || 'N/A';
+//             const source = v.source || 'N/A';
+
+//             // Find possible values for this attribute from `a`
+//             const attrConfig = attributesFullData.find(item => 
+//                  item.attribute_name === key
+//             );
+            
+//             let possibleValues = [];
+//             if (attrConfig && attrConfig.possible_values) {
+//                 possibleValues = attrConfig.possible_values.split(',').map(s => s.trim());
+//             }
+
+//             // Determine if AI value exists in possible values
+//             const isFoundInPossible = possibleValues.includes(aiValue);
+//             const matchFlag = isFoundInPossible ? true : false;
+
+//             attributeEntries.push({
+//                 name: key,
+//                 aiValue: aiValue,
+//                 possibleValues: possibleValues,
+//                 originalValue: originalValue,
+//                 source: source,
+//                 isMatch: matchFlag
+//             });
+//         });
+//     });
+
+//     const titleEl = el('div', 'section-title');
+//     titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+//     section.appendChild(titleEl);
+
+//     if (attributeEntries.length === 0) {
+//         const msg = el('p', 'no-attributes-message');
+//         msg.textContent = `No ${title.toLowerCase()} found.`;
+//         section.appendChild(msg);
+//         return section;
+//     }
+
+//     const scrollWrapper = el('div', 'attribute-scroll-wrapper');
+//     const table = el('table', 'attribute-detail-table comparison-table');
+
+//     const thead = el('thead');
+//     const headerRow = el('tr');
+
+//     ['Attribute Name', 'AI Generated Value', 'Action'].forEach(text => {
+//         const th = el('th');
+//         th.textContent = text;
+//         headerRow.appendChild(th);
+//     });
+//     thead.appendChild(headerRow);
+//     table.appendChild(thead);
+
+//     const tbody = el('tbody');
+
+//     attributeEntries.forEach(attr => {
+//         const row = el('tr', attr?.isMatch ? 'match-row' : 'mismatch-row');
+
+//         // 1. Attribute Name
+//         const nameTd = el('td', 'attribute-name');
+//         nameTd.textContent = formatString(attr?.name).replace(/_/g, ' ');
+//         row.appendChild(nameTd);
+
+//         // 3. Source Column
+//         const sourceTd = el('td', `ai-value ${attr?.isMatch ? '' : 'mismatch-value'}`);
+//         sourceTd.textContent = formatString(attr?.aiValue);
+//         row.appendChild(sourceTd);
+
+
+//         // 2. AI Value Dropdown (Select2)
+//         const aiTd = el('td', 'attribute-source');
+
+//         const select = document.createElement('select');
+//         select.classList.add('select2-dropdown');
+//         select.setAttribute('data-attribute', attr?.name);
+
+//         // Populate possible values
+//         attr?.possibleValues.forEach(val => {
+//             const option = document.createElement('option');
+//             option.value = val;
+//             option.textContent = val;
+//             if (val === attr?.aiValue) option.selected = true;
+//             select.appendChild(option);
+//         });
+
+//         // If not found in possible values, add it as a new option
+//         if (!attr?.isMatch && attr?.aiValue !== 'N/A') {
+//             const newOpt = document.createElement('option');
+//             newOpt.value = attr?.aiValue;
+//             newOpt.textContent = attr?.aiValue + " (new)";
+//             newOpt.selected = true;
+//             select.appendChild(newOpt);
+//         }
+
+//         aiTd.appendChild(select);
+//         row.appendChild(aiTd);
+
+
+//         tbody.appendChild(row);
+
+//         // Initialize Select2 after element is added
+//         jQuery(select).select2({
+//             tags: true, // allows new values
+//             width: 'resolve'
+//         });
+//     });
+
+//     table.appendChild(tbody);
+//     scrollWrapper.appendChild(table);
+//     section.appendChild(scrollWrapper);
+
+//     return section;
+// }
+
+// function renderMandatoryComparisonTable(attributes, title) {
+//     const section = el('div', 'attribute-section');
+    
+//     let attributeEntries = [];
+
+//     Object.keys(attributes).forEach(key => {
+//         const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]]; 
+//         valuesArray.forEach(v => {
+//             const aiValue = v.value || 'N/A';
+//             const originalValue = v.original_value || 'N/A';
+//             const source = v.source || 'N/A';
+            
+//             // Comparison is case-insensitive and ignores leading/trailing whitespace
+//             const isMatch = (String(aiValue).trim().toLowerCase() === String(originalValue).trim().toLowerCase());
+
+//             attributeEntries.push({
+//                 name: key,
+//                 aiValue: aiValue,
+//                 originalValue: originalValue,
+//                 source: source,
+//                 isMatch: isMatch
+//             });
+//         });
+//     });
+
+//     const titleEl = el('div', 'section-title');
+//     titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+//     section.appendChild(titleEl);
+    
+//     if (attributeEntries.length === 0) {
+//         const msg = el('p', 'no-attributes-message');
+//         msg.textContent = `No ${title.toLowerCase()} found.`;
+//         section.appendChild(msg);
+//         return section;
+//     }
+
+//     // --- SCROLL WRAPPER ADDITION ---
+//     const scrollWrapper = el('div', 'attribute-scroll-wrapper');
+//     const table = el('table', 'attribute-detail-table comparison-table');
+    
+//     const thead = el('thead');
+//     const headerRow = el('tr');
+    
+//     // Updated Headers for Comparison Table
+//     ['Attribute Name', 'AI Generated Value', 'Action'].forEach(text => {
+//         const th = el('th');
+//         th.textContent = text;
+//         headerRow.appendChild(th);
+//     });
+//     thead.appendChild(headerRow);
+//     table.appendChild(thead);
+
+//     const tbody = el('tbody');
+
+//     attributeEntries.forEach(attr => {
+//         // Highlight the entire row in red if the values do not match
+//         const row = el('tr', attr?.isMatch ? 'match-row' : 'mismatch-row'); 
+        
+//         // 1. Attribute Name
+//         const nameTd = el('td', 'attribute-name');
+//         nameTd.textContent = formatString(attr?.name).replace(/_/g, ' '); 
+//         row.appendChild(nameTd);
+
+
+//         // 3. AI Extracted Value
+//         const aiTd = el('td', `ai-value ${attr?.isMatch ? '' : 'mismatch-value'}`);
+//         aiTd.textContent = attr?.aiValue;
+//         row.appendChild(aiTd);
+        
+//         // 4. Source
+//         const sourceTd = el('td', 'attribute-source');
+//         sourceTd.textContent = formatString(attr?.source);
+//         row.appendChild(sourceTd);
+
+//         tbody.appendChild(row);
+//     });
+
+//     table.appendChild(tbody);
+//     scrollWrapper.appendChild(table); // Append table to wrapper
+//     section.appendChild(scrollWrapper); // Append wrapper to section
+//     return section;
+// }
+
+/**
+ * Renders a table for Additional, OCR, or Visual attributes (Name, Value, Source).
+ * @param {Object} attributes - The attribute data.
+ * @param {string} title - The title for the table section.
+ * @returns {HTMLElement} A div containing the table.
+ */
+function renderAttributesAsTable(attributes, title, mandatoryData = null) {
+    const section = el('div', 'attribute-section');
+    let attributeEntries = [];
+
+    // --- STEP 1: Create a Set of all Mandatory Original Values (Normalized) ---
+    // A Set is used for fast lookups. Values are normalized (trimmed, lowercase)
+    const mandatoryOriginalValuesSet = new Set();
+    if (mandatoryData) {
+        Object.values(mandatoryData).forEach(attrArray => {
+            const originalValue = attrArray[0]?.original_value;
+            if (originalValue) {
+                mandatoryOriginalValuesSet.add(String(originalValue).trim().toLowerCase());
+            }
+        });
+    }
+
+    // Helper to extract attribute entries consistently
+    const processAttribute = (key, values) => {
+        const valuesArray = Array.isArray(values) ? values : [values]; 
+        valuesArray.forEach(v => {
+            attributeEntries.push({
+                name: key,
+                value: v.value,
+                source: v.source || 'N/A'
+            });
+        });
+    };
+
+    // Iterate through attributes (OCR/Visual/Additional) and flatten them
+    Object.keys(attributes).forEach(key => {
+        const attribute = attributes[key];
+        
+        if (Array.isArray(attribute)) {
+            processAttribute(key, attribute);
+        } else if (typeof attribute === 'object' && attribute !== null) {
+            // Handle simple { "key": { "value": "X", "source": "Y" } } structure
+            if (attribute.value !== undefined) {
+                 attributeEntries.push({
+                    name: key,
+                    value: attribute.value,
+                    source: attribute.source || 'N/A'
+                });
+            } else {
+                // Handle nested objects
+                Object.keys(attribute).forEach(subKey => {
+                    const subAttribute = attribute[subKey];
+                    if (Array.isArray(subAttribute)) {
+                        processAttribute(`${key} (${subKey.replace(/_/g, ' ')})`, subAttribute);
+                    }
+                });
+            }
+        }
+    });
+
+    const titleEl = el('div', 'section-title');
+    titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+    section.appendChild(titleEl);
+    
+    if (attributeEntries.length === 0) {
+        const msg = el('p', 'no-attributes-message');
+        msg.textContent = `No ${title.toLowerCase()} found.`;
+        section.appendChild(msg);
+        return section;
+    }
+
+    const scrollWrapper = el('div', 'attribute-scroll-wrapper');
+    const table = el('table', 'attribute-detail-table');
+
+    const thead = el('thead');
+    const headerRow = el('tr');
+    
+    ['Attribute Name', 'Value'].forEach(text => {
+        const th = el('th');
+        th.textContent = text;
+        headerRow.appendChild(th);
+    });
+    thead.appendChild(headerRow);
+    table.appendChild(thead);
+
+    const tbody = el('tbody');
+
+    attributeEntries.forEach(attr => {
+        const row = el('tr'); 
+        
+        // --- CORE COMPARISON LOGIC (Global Check) ---
+        let colorClass = '';
+        const hasMandatoryBaseline = mandatoryOriginalValuesSet.size > 0;
+        
+        if (hasMandatoryBaseline) {
+            // Normalize the current attribute's value
+            const currentValueNormalized = String(attr?.value).trim().toLowerCase();
+            
+            // Check if the current value exists ANYWHERE in the mandatory original values set
+            if (mandatoryOriginalValuesSet.has(currentValueNormalized)) {
+                colorClass = 'green-text'; // Found a match in the global mandatory set
+            } else {
+                colorClass = 'red-text'; // Did NOT find a match
+            }
+        }
+        // --- END CORE COMPARISON LOGIC ---
+        
+        const nameTd = el('td', 'attribute-name');
+        nameTd.textContent = formatString(attr?.name).replace(/_/g, ' '); 
+        row.appendChild(nameTd);
+
+        const valueTd = el('td', 'attribute-value');
+        const displayValue = formatString(attr?.value) + ' (' + formatString(attr?.source) + ')';
+        
+        valueTd.textContent = displayValue || 'N/A';
+        
+        // Apply the determined color class to the value cell
+        if (colorClass) {
+            valueTd.classList.add(colorClass);
+        }
+
+        row.appendChild(valueTd);
+        tbody.appendChild(row);
+    });
+
+    table.appendChild(tbody);
+    scrollWrapper.appendChild(table); 
+    section.appendChild(scrollWrapper); 
+    return section;
+}
+// function renderAttributesAsTable(attributes, title, mandatoryData = null) {
+//     const section = el('div', 'attribute-section');
+//     let attributeEntries = [];
+
+//     const processAttribute = (key, values) => {
+//         const valuesArray = Array.isArray(values) ? values : [values]; 
+//         valuesArray.forEach(v => {
+//             attributeEntries.push({
+//                 name: key,
+//                 value: v.value,
+//                 source: v.source || 'N/A'
+//             });
+//         });
+//     };
+
+//     Object.keys(attributes).forEach(key => {
+//         const attribute = attributes[key];
+        
+//         if (Array.isArray(attribute)) {
+//             processAttribute(key, attribute);
+//         } else if (typeof attribute === 'object' && attribute !== null) {
+//             Object.keys(attribute).forEach(subKey => {
+//                 const subAttribute = attribute[subKey];
+//                 if (Array.isArray(subAttribute)) {
+//                     // Combines parent key (e.g., 'size') and sub-key (e.g., 'waist_size')
+//                     processAttribute(`${key} (${subKey.replace(/_/g, ' ')})`, subAttribute);
+//                 }
+//             });
+//         }
+//     });
+
+//     const titleEl = el('div', 'section-title');
+//     titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+//     section.appendChild(titleEl);
+    
+//     if (attributeEntries.length === 0) {
+//         const msg = el('p', 'no-attributes-message');
+//         msg.textContent = `No ${title.toLowerCase()} found.`;
+//         section.appendChild(msg);
+//         return section;
+//     }
+
+//     // --- SCROLL WRAPPER ADDITION ---
+//     const scrollWrapper = el('div', 'attribute-scroll-wrapper');
+//     const table = el('table', 'attribute-detail-table');
+
+//     const thead = el('thead');
+//     const headerRow = el('tr');
+    
+//     ['Attribute Name', 'Value'].forEach(text => {
+//         const th = el('th');
+//         th.textContent = text;
+//         headerRow.appendChild(th);
+//     });
+//     thead.appendChild(headerRow);
+//     table.appendChild(thead);
+
+//     const tbody = el('tbody');
+
+//     attributeEntries.forEach(attr => {
+//         const row = el('tr'); 
+        
+//         const nameTd = el('td', 'attribute-name');
+//         nameTd.textContent = formatString(attr?.name).replace(/_/g, ' '); 
+//         row.appendChild(nameTd);
+
+//         const valueTd = el('td', 'attribute-value');
+//         const displayValue = Array.isArray(attr?.value) 
+//             ? (attr?.value.map(v => formatString(v.value))).join(', ') +'('+ formatString(attr?.source)+ ')' 
+//             : formatString(attr?.value) +'('+ formatString(attr?.source)+ ')';
+//         valueTd.textContent = displayValue || 'N/A';
+//         row.appendChild(valueTd);
+
+//         // const sourceTd = el('td', 'attribute-source');
+//         // sourceTd.textContent = attr?.source || 'Unknown';
+//         // row.appendChild(sourceTd);
+
+//         tbody.appendChild(row);
+//     });
+
+//     table.appendChild(tbody);
+//     scrollWrapper.appendChild(table); // Append table to wrapper
+//     section.appendChild(scrollWrapper); // Append wrapper to section
+//     return section;
+// }
+
+// ------------------------------------------------------------------
+// --- 3. MAIN RENDER FUNCTION (REPLACEMENT) ---
+// ------------------------------------------------------------------
+
+// function renderInlineForTable() {
+//     const api = API_RESPONSE_AI;
+//     const table = $('#tableContainer');
+//     if (!table) return;
+
+//     // Remove existing detail rows
+//     table.querySelectorAll('tr.detail-row').forEach(r => r.remove());
+
+//     PRODUCT_BASE.forEach((p, idx) => {
+//         if (!isProductSelected(p.item_id)) return;
+        
+//         const res = findApiResultForProduct(p, idx, api);
+        
+//         const tbody = table.querySelector('tbody');
+//         const baseRow = tbody ? tbody.querySelector(`#row-${p.id}`) : null; 
+//         if (!baseRow) return;
+
+//         // --- Detail Row Construction ---
+//         const detail = el('tr', 'detail-row');
+//         // td.colSpan must match the number of columns in your main table
+//         const td = el('td'); td.colSpan = 7; 
+//         const content = el('div', 'detail-content-tables');
+
+
+//         // // 1. MANDATORY Attributes Table (NOW USES CARD COMPARISON)
+//         // const mandatorySection = renderMandatoryComparisonCards( // <-- NEW FUNCTION NAME
+//         //     res?.mandatory || {}, 
+//         //     'Mandatory Attributes Comparison'
+//         // );
+//         // content.appendChild(mandatorySection);
+
+//         // // 2. COMBINED Attributes Section (Additional, OCR, Visuals) - REMAINS THE SAME
+//         // content.appendChild(el('hr', 'section-separator'));
+
+//         // 1. MANDATORY Attributes Table (USES COMPARISON FUNCTION)
+//         const mandatoryTable = renderMandatoryComparisonTable(
+//             res?.mandatory || {}, 
+//             'Mandatory Attributes Comparison'
+//         );
+//         content.appendChild(mandatoryTable);
+
+//         // 2. COMBINED Attributes Section (Additional, OCR, Visuals)
+//         content.appendChild(el('hr', 'section-separator')); 
+        
+//         const combinedTitle = el('div', 'section-title');
+//         combinedTitle.innerHTML = '<h2>Additional & AI-Driven Attributes</h2>';
+//         content.appendChild(combinedTitle);
+        
+//         const combinedAttributesContainer = el('div', 'combined-attributes-container');
+        
+//         // Use the general renderer for these sections
+//         const additionalTable = renderAttributesAsTable(
+//             res?.additional || {}, 
+//             'Additional Attributes'
+//         );
+        
+//         const ocrTable = renderAttributesAsTable(
+//             res?.ocr_results?.extracted_attributes || {}, 
+//             'OCR Results'
+//         );
+        
+//         const visualsTable = renderAttributesAsTable(
+//             res?.visual_results?.visual_attributes || {}, 
+//             'Visual Results'
+//         );
+
+//         // Append all sections to the combined container
+//         combinedAttributesContainer.appendChild(additionalTable);
+//         combinedAttributesContainer.appendChild(ocrTable);
+//         combinedAttributesContainer.appendChild(visualsTable);
+//         content.appendChild(combinedAttributesContainer);
+        
+//         // --- Summary Counts ---
+//         const mandCount = Object.keys(res?.mandatory || {}).length;
+//         const addCount = Object.keys(res?.additional || {}).length;
+//         const ocrExtractedCount = Object.keys(res?.ocr_results?.extracted_attributes || {}).length;
+//         const visualExtractedCount = Object.keys(res?.visual_results?.visual_attributes || {}).length;
+        
+//         const counts = el('div', 'attribute-summary-pills');
+//         const c1 = el('span', 'pill primary'); c1.textContent = `Mandatory: ${mandCount}`;
+//         const c2 = el('span', 'pill secondary'); c2.textContent = `Additional: ${addCount}`;
+//         const c3 = el('span', 'pill secondary'); c3.textContent = `OCR Keys: ${ocrExtractedCount}`;
+//         const c4 = el('span', 'pill secondary'); c4.textContent = `Visual Keys: ${visualExtractedCount}`;
+//         counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
+//         content.appendChild(counts);
+        
+//         // Final assembly and insertion
+//         td.appendChild(content); 
+//         detail.appendChild(td);
+//         baseRow.insertAdjacentElement('afterend', detail);
+//     });
+
+//     // Update summary statistics
+//     $('#statTotal').textContent = api.total_products ?? 0;
+//     $('#statOk').textContent = api.successful ?? 0;
+//     $('#statKo').textContent = api.failed ?? 0;
+//     const apiSummary = $('#api-summary');
+//     if (apiSummary) apiSummary.style.display = 'block';
+// }
+
+// function highlightMatches(text, keywords) {
+//     if (!text) return '—';
+//     let highlighted = text;
+//     keywords.forEach(keyword => {
+//         const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape regex
+//         const regex = new RegExp(`(${escapedKeyword})`, 'gi');
+//         highlighted = highlighted.replace(regex, '<span class="highlight-text">$1</span>');
+//     });
+//     return highlighted;
+// }
+
+function highlightMatches(text, keywords) {
+    if (!text) return '—';
+
+    // 1. Process Keywords: Flatten the array and handle comma-separated strings
+    const uniqueKeywords = new Set(); 
+
+    keywords.forEach(compositeKeyword => {
+        // Split by comma, trim whitespace, and add non-empty terms to the Set
+        compositeKeyword.split(',')
+            .map(kw => kw.trim())
+            .filter(kw => kw.length > 0)
+            .forEach(singleKeyword => {
+                // Only add the keyword if it's not just an empty string
+                if (singleKeyword) {
+                    uniqueKeywords.add(singleKeyword);
+                }
+            });
+    });
+
+    let highlighted = text;
+
+    // 2. Apply Highlighting using the clean, unique keywords
+    uniqueKeywords.forEach(keyword => {
+        // We still need to check for length in case something slipped through, 
+        // though the filter and Set should handle most cases.
+        if (keyword.length > 0) {
+            const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape regex special characters
+            
+            // Use 'g' (global) and 'i' (case-insensitive) flags
+            const regex = new RegExp(`(${escapedKeyword})`, 'gi'); 
+            
+            // Use a function as the replacer to ensure we highlight all matches
+            highlighted = highlighted.replace(regex, '<span class="highlight-text">$1</span>');
+        }
+    });
+
+    return highlighted;
+}
+
+// WOrking ONe
+function renderInlineForTable() {
+    const api = API_RESPONSE_AI;
+    const table = $('#tableContainer');
+    if (!table) return;
+
+    // Remove existing detail rows
+    table.querySelectorAll('tr.detail-row').forEach(r => r.remove());
+
+    PRODUCT_BASE.forEach((p, idx) => {
+        if (!isProductSelected(p.item_id)) return;
+        
+        const res = findApiResultForProduct(p, idx, api);
+        
+        const tbody = table.querySelector('tbody');
+        const baseRow = tbody ? tbody.querySelector(`#row-${p.id}`) : null; 
+        if (!baseRow) return;
+
+        // --- Detail Row Construction ---
+        const detail = el('tr', 'detail-row');
+        // td.colSpan must match the number of columns in your main table
+        const td = el('td'); td.colSpan = 6; 
+        const content = el('div', 'detail-content-tables');
+
+        // 1. MANDATORY Attributes Table
+        // 🚨 Note: The color check (Requirement #2) must be implemented inside renderMandatoryComparisonTable. 
+        const mandatoryData = res?.mandatory || {};
+        if (Object.keys(mandatoryData).length > 0) {
+            const mandatoryTable = renderMandatoryComparisonTable(
+                mandatoryData, 
+                'Attributes'
+            );
+            content.appendChild(mandatoryTable);
+        }
+
+        // 2. COMBINED Attributes Section (Additional, OCR, Visuals)
+        const additionalData = res?.additional || {};
+        const ocrData = res?.ocr_results?.extracted_attributes || {};
+        const visualsData = res?.visual_results?.visual_attributes || {};
+        
+        const hasCombinedData = Object.keys(additionalData).length > 0 || 
+                                Object.keys(ocrData).length > 0 || 
+                                Object.keys(visualsData).length > 0;
+
+        if (hasCombinedData) {
+            content.appendChild(el('hr', 'section-separator')); 
+            
+            const combinedTitle = el('div', 'section-title');
+            combinedTitle.innerHTML = '<h2>Additional & AI-Driven Attributes</h2>';
+            // content.appendChild(combinedTitle);
+            
+            const combinedAttributesContainer = el('div', 'combined-attributes-container');
+            
+            // Render Additional Table (Conditional based on data existence)
+            if (Object.keys(additionalData).length > 0) {
+                const additionalTable = renderAttributesAsTable(
+                    additionalData, 
+                    'Additional Attributes',
+                    mandatoryData
+                );
+                // combinedAttributesContainer.appendChild(additionalTable);
+            }
+            
+            // Render OCR Table (Conditional based on data existence)
+            if (Object.keys(ocrData).length > 0) {
+                const ocrTable = renderAttributesAsTable(
+                    ocrData, 
+                    'OCR Results',
+                    mandatoryData
+                );
+                // combinedAttributesContainer.appendChild(ocrTable);
+            }
+            
+            // Render Visuals Table (Conditional based on data existence)
+            if (Object.keys(visualsData).length > 0) {
+                const visualsTable = renderAttributesAsTable(
+                    visualsData, 
+                    'Visual Results',
+                    mandatoryData
+                );
+                // combinedAttributesContainer.appendChild(visualsTable);
+            }
+
+            // content.appendChild(combinedAttributesContainer);
+        }
+        
+        // --- Summary Counts ---
+        const mandCount = Object.keys(mandatoryData).length;
+        const addCount = Object.keys(additionalData).length;
+        const ocrExtractedCount = Object.keys(ocrData).length;
+        const visualExtractedCount = Object.keys(visualsData).length;
+        
+        const counts = el('div', 'attribute-summary-pills');
+        const c1 = el('span', 'pill primary'); c1.textContent = `Mandatory: ${mandCount}`;
+        const c2 = el('span', 'pill secondary'); c2.textContent = `Additional: ${addCount}`;
+        const c3 = el('span', 'pill secondary'); c3.textContent = `OCR Keys: ${ocrExtractedCount}`;
+        const c4 = el('span', 'pill secondary'); c4.textContent = `Visual Keys: ${visualExtractedCount}`;
+        counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
+        // content.appendChild(counts);
+        
+        // Final assembly and insertion
+        td.appendChild(content); 
+        detail.appendChild(td);
+        baseRow.insertAdjacentElement('afterend', detail);
+
+        
+        // Extract mandatory values for highlighting
+        // const mandatoryValues = Object.values(mandatoryData)
+        //     .map(v => v?.value?.toString()?.trim())
+        //     .filter(Boolean);
+
+        // const attrConfig = attributesFullData.find(item => item.attribute_name === key);
+
+        const mandatoryValues = Object.values(mandatoryData)
+            // 1. Use flatMap to iterate over the outer array AND flatten the inner arrays.
+            //    'arr' here is the array like [ { value: 'Pullover', ... } ]
+            .flatMap(arr => 
+                // 2. Map the inner array. We safely assume the first element [0] 
+                //    holds the value we want.
+                arr.map(item => item?.value?.toString()?.trim())
+            )
+            // 3. Filter out any potential undefined/null/empty string results.
+            .filter(Boolean);
+
+        // Highlight in Product Name
+        const nameCell = baseRow.querySelector('td:nth-child(3)');
+        if (nameCell) {
+            nameCell.innerHTML = highlightMatches(nameCell.textContent, mandatoryValues);
+        }
+
+        // Highlight in Short Description
+        const descCell = baseRow.querySelector('td:nth-child(6)');
+        if (descCell) {
+            const shortDescDiv = descCell.querySelector('.short-desc');
+            if (shortDescDiv) {
+                shortDescDiv.innerHTML = highlightMatches(shortDescDiv.innerHTML, mandatoryValues);
+            }
+        }
+
+        const desclongCell = baseRow.querySelector('td:nth-child(6)');
+        if (desclongCell) {
+            const longDescDiv = desclongCell.querySelector('.long-desc');
+            if (longDescDiv) {
+                longDescDiv.innerHTML = highlightMatches(longDescDiv.innerHTML, mandatoryValues);
+            }
+        }
+
+    });
+
+    // Update summary statistics
+    $('#statTotal').textContent = selectedProductsWithAttributes.length ?? api.total_products ?? 0;
+    $('#statOk').textContent = selectedProductsWithAttributes.length ?? api.successful ?? 0;
+    $('#statKo').textContent = api.failed ?? 0;
+    const apiSummary = $('#api-summary');
+    if (apiSummary) apiSummary.style.display = 'block';
+}
+// working one
+function applyHighlightingToTableRow(baseRow, mandatoryValues) {
+    const nameCell = baseRow.querySelector('td:nth-child(3)');
+    if (nameCell) {
+        nameCell.innerHTML = highlightMatches(nameCell.textContent, mandatoryValues);
+    }
+
+    const descCell = baseRow.querySelector('td:nth-child(6)');
+    if (descCell) {
+        const shortDescDiv = descCell.querySelector('.short-desc');
+        if (shortDescDiv) {
+            shortDescDiv.innerHTML = highlightMatches(shortDescDiv.innerHTML, mandatoryValues);
+        }
+    }
+}
+
+function renderInlineAttributes() {
+    if (layoutMode === 'cards') renderInlineForCards(); else renderInlineForTable();
+    // renderInlineForCards(); 
+    // renderInlineForTable();
+}
+
+// --- Main rendering ---
+function renderProducts() {
+    if (layoutMode === 'cards') {
+    $('#cardsContainer').style.display = '';
+    $('#tableContainer').style.display = 'none';
+    // console.log("PRODUCT_BASE",PRODUCT_BASE);
+    renderProductsCards();
+    } else {
+    $('#cardsContainer').style.display = 'none';
+    $('#tableContainer').style.display = '';
+    renderProductsTable();
+    }
+    updateSelectionInfo();
+    renderPagination();               
+
+    // If there is a selection, re-render inline attributes (persist across toggle)
+    if (selectedProductsWithAttributes.length > 0) renderInlineAttributes();
+}
+
+// --- Submit & Reset ---
+function submitAttributes() {
+  // Check the length of the new array
+    if (selectedProductsWithAttributes.length === 0) { 
+        alert('Please select at least one product.'); 
+        return; 
+    } 
+    // if (selectedIds.size === 0) { alert('Please select at least one product.'); return; }
+    // console.log("selectedIds",selectedIds);
+    jQuery('#full-page-loader').show();  
+    // let inputArray = {
+    //       "product_ids" : [...selectedIds]
+    //     }
+    const extractAdditional = document.getElementById('extract_additional').checked;
+    const processImage = document.getElementById('process_image').checked;
+    // const selectedMultiples = document.getElementById('#mandatory-attributes');
+    // const selectedValues = Array.from(selectedMultiples.selectedOptions).map(option => option.value);
+    const selectElement = document.getElementById('mandatory-attributes');
+
+    const selectedValues = Array.from(selectElement.selectedOptions).map(option => option.value);
+
+    // console.log(selectedValues); // Logs an array of selected values
+    // console.log("thresholdValueDisplay",thresholdValueDisplay.value);
+    const threshold = parseFloat(document.getElementById('thresholdRange').value);
+
+
+    // Transform the new state array into the required API format
+    const itemIds = selectedProductsWithAttributes.map(p => p.item_id);
+    
+    // Create the mandatory_attrs map: { item_id: { attr_name: [values] } }
+    // NOTE: The backend API you showed expects a flattened list of "mandatory_attrs"
+    // like: { "color": ["color", "shade"], "size": ["size", "fit"] } 
+    // It seems to ignore the selected product-specific values and uses a general list of synonyms.
+    // Assuming the request needs a general map of *all unique* selected attributes across all selected products:
+    
+    let mandatoryAttrsMap = {};
+    selectedProductsWithAttributes.forEach(product => {
+        // Merge attributes from all selected products
+        Object.assign(mandatoryAttrsMap, product.mandatory_attrs);
+    });
+
+    // If the API expects the complex, product-specific payload from your Q1 example:
+    const payloadForQ1 = selectedProductsWithAttributes.map(p => ({
+        item_id: p.item_id,
+        mandatory_attrs: p.mandatory_attrs
+    }));
+
+    let inputArray = {
+              "products": payloadForQ1,
+              "model": "llama-3.1-8b-instant",
+              "extract_additional": extractAdditional,
+              "process_image": processImage,
+              "multiple": selectedValues,
+              "threshold_abs": 0.6,  // Lower threshold to be more permissive
+            //   "margin": 0.3,  // Larger margin to include more candidates
+            //   "use_adaptive_margin": true,
+            //   "use_semantic_clustering": true
+            }
+    let raw = JSON.stringify(inputArray);
+     fetch('/attr/batch-extract/', {
+        method: 'POST', // or 'POST' if your API expects POST
+        headers: {
+            'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || '',
+            'Content-Type': "application/json"
+        },
+        body: raw
+    })
+    .then(response => response.json())
+    .then(async data => {
+        await delay(5000); // waits for 3 seconds
+        // console.log("response data",data); 
+        API_RESPONSE_AI = data;   
+        renderInlineAttributes();
+        jQuery('#full-page-loader').hide();  
+     });
+}
+
+function resetAll() {
+  selectedProductsWithAttributes = []; // Reset the main array
+    // selectedIds.clear();
+    lastSeen.clear();
+    renderProducts();
+    // Clear summary
+    document.getElementById('statTotal').textContent = '0';
+    document.getElementById('statOk').textContent = '0';
+    document.getElementById('statKo').textContent = '0';
+    $('#api-summary').style.display = 'none';
+
+    // ✅ Clear Select2 selections
+    jQuery('#mandatory-attributes').val(null).trigger('change');
+
+    // ✅ Reset threshold input (and display)
+    const thresholdInput = document.getElementById('thresholdRange');
+    const thresholdDisplay = document.getElementById('thresholdValue');
+
+    thresholdInput.value = '0.65'; // or any default value you prefer
+    if (thresholdDisplay) {
+        thresholdDisplay.textContent = '0.65';
+    }
+}
+
+function setLayout(mode) {
+    layoutMode = mode;
+    const btnCards = document.getElementById('btnCards');
+    const btnTable = document.getElementById('btnTable');
+    if (mode === 'cards') { btnCards.classList.add('active'); btnCards.setAttribute('aria-selected', 'true'); btnTable.classList.remove('active'); btnTable.setAttribute('aria-selected', 'false'); }
+    else { btnTable.classList.add('active'); btnTable.setAttribute('aria-selected', 'true'); btnCards.classList.remove('active'); btnCards.setAttribute('aria-selected', 'false'); }
+    renderProducts();
+}
+
+
+// Upload elements (Bootstrap modal version)
+const uploadModalEl = document.getElementById('uploadModal');
+const dropzone      = document.getElementById('dropzone');
+const uploadFiles   = document.getElementById('uploadFiles');
+const fileInfo      = document.getElementById('fileInfo');
+const uploadBar     = document.getElementById('uploadBar');
+const uploadStatus  = document.getElementById('uploadStatus');
+
+// Reset modal on show
+uploadModalEl.addEventListener('shown.bs.modal', () => {
+  uploadStatus.textContent = '';
+  uploadStatus.className = '';             // clear success/error class
+  uploadBar.style.width = '0%';
+  uploadBar.setAttribute('aria-valuenow', '0');
+  uploadFiles.value = '';
+  uploadFiles.setAttribute('accept', ACCEPT_TYPES);
+  fileInfo.textContent = 'No files selected.';
+});
+
+function describeFiles(list) {
+  if (!list || list.length === 0) { fileInfo.textContent = 'No files selected.'; return; }
+  const names = Array.from(list).map(f => `${f.name} (${Math.round(f.size/1024)} KB)`);
+  fileInfo.textContent = names.join(', ');
+}
+
+// Drag & drop feedback
+['dragenter','dragover'].forEach(evt => {
+  dropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); dropzone.classList.add('drag'); });
+});
+['dragleave','drop'].forEach(evt => {
+  dropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('drag'); });
+});
+
+// Handle drop
+dropzone.addEventListener('drop', e => {
+  uploadFiles.files = e.dataTransfer.files;
+  describeFiles(uploadFiles.files);
+});
+
+// Click to browse
+// dropzone.addEventListener('click', () => uploadFiles.click());
+
+// Picker change
+uploadFiles.addEventListener('change', () => describeFiles(uploadFiles.files));
+
+function startUpload() {
+  const files = uploadFiles.files;
+  if (!files || files.length === 0) { alert('Please select file(s) to upload.'); return; }
+  jQuery('#full-page-loader').show();
+  uploadStatus.textContent = 'Uploading...';
+  uploadStatus.className = ''; // neutral
+  uploadBar.style.width = '0%';
+  uploadBar.setAttribute('aria-valuenow', '0');
+
+  const form = new FormData();
+  Array.from(files).forEach(f => form.append('file', f));
+  // form.append('uploaded_by', 'Vishal'); // example extra field
+
+  const xhr = new XMLHttpRequest();
+  xhr.open('POST', UPLOAD_API_URL, true);
+  // If you need auth:
+  // xhr.setRequestHeader('Authorization', 'Bearer <token>');
+
+  xhr.upload.onprogress = (e) => {
+    if (e.lengthComputable) {
+      const pct = Math.round((e.loaded / e.total) * 100);
+      uploadBar.style.width = pct + '%';
+      uploadBar.setAttribute('aria-valuenow', String(pct));
+    }
+  };
+
+  xhr.onreadystatechange = () => {
+    if (xhr.readyState === 4) {
+      const ok = (xhr.status >= 200 && xhr.status < 300);
+      try {
+        const resp = JSON.parse(xhr.responseText || '{}');
+        uploadStatus.textContent = ok ? (resp.message || 'Upload successful') : (resp.error || `Upload failed (${xhr.status})`);
+      } catch {
+        uploadStatus.textContent = ok ? 'Upload successful' : `Upload failed (${xhr.status})`;
+      }
+      uploadStatus.className = ok ? 'success' : 'error';
+      // Optional: auto-close the modal on success after 1.2s:
+      // if (ok) setTimeout(() => bootstrap.Modal.getInstance(uploadModalEl).hide(), 1200);
+    }
+  };
+
+  xhr.onerror = () => {
+    uploadStatus.textContent = 'Network error during upload.';
+    uploadStatus.className = 'error';
+  };
+
+  xhr.send(form);
+  setTimeout(()=>{
+      jQuery('#uploadModal').modal('hide');
+      window.location.reload();
+  },3000)
+  jQuery('#full-page-loader').hide();
+
+}
+
+// Wire Start button
+document.getElementById('uploadStart').addEventListener('click', startUpload);
+// Cancel button already closes the modal via data-bs-dismiss
+
+
+
+
+// --- Pagination state ---
+let page = 1;
+let pageSize = 50; // default rows per page
+
+function totalPages() {
+  return Math.max(1, Math.ceil(PRODUCT_BASE.length / pageSize));
+}
+
+function clampPage() {
+  page = Math.min(Math.max(1, page), totalPages());
+}
+
+function getCurrentSlice() {
+  clampPage();
+  const start = (page - 1) * pageSize;
+  return PRODUCT_BASE.slice(start, start + pageSize);
+}
+
+function renderPagination() {
+  const bar = document.getElementById('paginationBar');
+  if (!bar) return;
+
+  const tp = totalPages();
+  clampPage();
+
+  bar.innerHTML = `
+    <div class="page-size">
+      <label for="pageSizeSelect">Rows per page</label>
+      <select id="pageSizeSelect">
+        <option value="5"  ${pageSize===5  ? 'selected' : ''}>5</option>
+        <option value="10" ${pageSize===10 ? 'selected' : ''}>10</option>
+        <option value="20" ${pageSize===20 ? 'selected' : ''}>20</option>
+        <option value="50" ${pageSize===50 ? 'selected' : ''}>50</option>
+        <option value="all" ${pageSize>=PRODUCT_BASE.length ? 'selected' : ''}>All</option>
+      </select>
+    </div>
+
+    <div class="pager">
+      <button class="pager-btn" id="prevPage" ${page<=1 ? 'disabled' : ''} aria-label="Previous page">‹</button>
+      <span class="page-info">Page ${page} of ${tp}</span>
+      <button class="pager-btn" id="nextPage" ${page>=tp ? 'disabled' : ''} aria-label="Next page">›</button>
+    </div>
+  `;
+
+  // wire events
+  document.getElementById('prevPage')?.addEventListener('click', () => { if (page > 1) { page--; renderProducts(); } });
+  document.getElementById('nextPage')?.addEventListener('click', () => { if (page < tp) { page++; renderProducts(); } });
+
+  const sel = document.getElementById('pageSizeSelect');
+  if (sel) {
+    sel.addEventListener('change', () => {
+      const val = sel.value;
+      pageSize = (val === 'all') ? PRODUCT_BASE.length : parseInt(val, 10);
+      page = 1;            // reset to first page when size changes
+      renderProducts();
+    });
+  }
+}
+
+// Function to add/remove product from the state and manage its attributes
+function toggleProductSelection(itemId, isChecked, attributes = {}) {
+    const index = selectedProductsWithAttributes.findIndex(p => p.item_id === itemId);
+    // console.log("index",index);
+    if (isChecked) {
+        // If selecting, ensure the product object exists in the array
+        if (index === -1) {
+            selectedProductsWithAttributes.push({
+                item_id: itemId,
+                mandatory_attrs: attributes
+            });
+        } else {
+            // Update attributes if the product is already selected
+            selectedProductsWithAttributes[index].mandatory_attrs = attributes;
+        }
+    } else {
+        // If deselecting, remove the product object from the array
+        if (index !== -1) {
+            selectedProductsWithAttributes.splice(index, 1);
+        }
+    }
+    updateSelectionInfo();
+}
+
+// Function to get the current mandatory attributes for a selected item
+function getSelectedAttributes(itemId) {
+    const productEntry = selectedProductsWithAttributes.find(p => p.item_id === itemId);
+    return productEntry ? productEntry.mandatory_attrs : {};
+}
+
+// Helper to check if a product is selected
+function isProductSelected(itemId) {
+    return selectedProductsWithAttributes.some(p => p.item_id === itemId);
+}
+
+// Helper to check if a specific attribute/value is selected
+function isAttributeValueSelected(itemId, attrName, value) {
+    const attrs = getSelectedAttributes(itemId);
+    const values = attrs[attrName];
+    return values ? values.includes(value) : false; // Default all selected when first loaded
+}
+
+
+
+// $('.attribute-select').select2({
+//     placeholder: 'Select product attributes'
+// });
+
+function getAtributeList(){
+        jQuery('#full-page-loader').show();  
+            try{
+            fetch('/attr/products/attributes', {
+                method: 'GET', // or 'POST' if your API expects POST
+                headers: {
+                    'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || ''
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                // console.log("data",data);
+                attributesFullData = data;
+                let attributesData = data;
+                 // Step 1: Extract unique mandatory attribute names
+                const mandatoryAttributes = [...new Set(
+                    attributesData
+                        .filter(attr => attr?.is_mandatory === "Yes")
+                        .map(attr => attr?.attribute_name)
+                )];
+
+                // Step 2: Populate the select element
+                const $select = jQuery('#mandatory-attributes');
+                $select.append(new Option("Select All", "select_all")); // Add "Select All" option first
+
+                mandatoryAttributes.forEach(attr => {
+                    $select.append(new Option(attr, attr));
+                });
+
+                // Step 3: Initialize Select2 with placeholder
+                // $select.select2({
+                //     placeholder: "Select mandatory attributes",
+                //     allowClear: true
+                // });
+
+                // Step 4: Handle 'Select All' logic
+                $select.on('select2:select', function (e) {
+                    if (e.params.data.id === "select_all") {
+                        // Select all real options except "Select All"
+                        const allOptions = mandatoryAttributes;
+                        $select.val(allOptions).trigger('change');
+                    }
+                });
+
+                jQuery('#full-page-loader').hide();
+            });
+        }catch(err){
+            console.log("err",err);
+            jQuery('#full-page-loader').hide();
+
+        }
+    
+}
+document.addEventListener("DOMContentLoaded", function () {
+    // Update span when range changes
+    thresholdInput.addEventListener('input', function () {
+        // console.log("this.value",this.value);
+        thresholdValueDisplay.textContent = this.value;
+    });
+});
+
+// Get threshold value when needed
+function getThreshold() {
+    // console.log("parseFloat(thresholdInput.value)",parseFloat(thresholdInput.value));
+    return parseFloat(thresholdInput.value);
+}
+
+
+/**
+ * Renders Mandatory attributes using a card-based comparison layout.
+ * Highlights mismatches prominently.
+ * @param {Object} attributes - The mandatory attribute data.
+ * @param {string} title - The title for the section.
+ * @returns {HTMLElement} A div containing the comparison cards.
+ */
+function renderMandatoryComparisonCards(attributes, title) {
+    const section = el('div', 'attribute-section');
+    
+    // --- 1. Flatten Mandatory Attributes ---
+    let attributeEntries = [];
+
+    Object.keys(attributes).forEach(key => {
+        const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]]; 
+        valuesArray.forEach(v => {
+            const aiValue = v.value || 'N/A';
+            const originalValue = v.original_value || 'N/A';
+            
+            // Comparison is case-insensitive and ignores leading/trailing whitespace
+            const isMatch = (String(aiValue).trim().toLowerCase() === String(originalValue).trim().toLowerCase());
+
+            attributeEntries.push({
+                name: key,
+                aiValue: aiValue,
+                originalValue: originalValue,
+                isMatch: isMatch,
+                source: v.source || 'N/A'
+            });
+        });
+    });
+
+    // --- 2. Section Header ---
+    const titleEl = el('div', 'section-title');
+    titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+    section.appendChild(titleEl);
+    
+    if (attributeEntries.length === 0) {
+        const msg = el('p', 'no-attributes-message');
+        msg.textContent = `No ${title.toLowerCase()} found.`;
+        section.appendChild(msg);
+        return section;
+    }
+
+    // --- 3. Card Container ---
+    const cardsContainer = el('div', 'comparison-cards-container');
+
+    attributeEntries.forEach(attr => {
+        // Main Card Element
+        const card = el('div', `comparison-card ${attr?.isMatch ? 'match' : 'mismatch-card'}`);
+
+        // Card Header (Attribute Name)
+        const header = el('div', 'card-header');
+        header.textContent = attr?.name.replace(/_/g, ' ');
+        card.appendChild(header);
+
+        // Content Wrapper
+        const content = el('div', 'card-content');
+
+        // Existing Value Box
+        const originalBox = el('div', 'value-box original-box');
+        originalBox.innerHTML = `
+            <div class="value-label">Manually Identified Value</div>
+            <div class="value-text">${attr?.originalValue}</div>
+        `;
+        content.appendChild(originalBox);
+
+        // AI Value Box
+        const aiBox = el('div', `value-box ai-box ${attr?.isMatch ? 'found-value' : 'mismatch-value'}`);
+        aiBox.innerHTML = `
+            <div class="value-label">AI Generated Value <span class="value-source">(${attr?.source})</span></div>
+            <div class="value-text">${attr?.aiValue}</div>
+        `;
+        content.appendChild(aiBox);
+
+        card.appendChild(content);
+
+        // Mismatch Indicator (only visible on mismatch-card via CSS)
+        if (!attr?.isMatch) {
+            const indicator = el('div', 'mismatch-indicator');
+            // indicator.innerHTML = '❌ MISMATCH';
+            indicator.innerHTML = attr?.isMatch ? '✅ MATCH' : '❌ MISMATCH';
+            card.appendChild(indicator);
+        }
+
+        cardsContainer.appendChild(card);
+    });
+
+    section.appendChild(cardsContainer);
+    return section;
+}
+
+// Example JavaScript (Assuming you have access to API_RESPONSE_AI)
+// document.getElementById('downloadResultBtn').addEventListener('click', () => {
+//     // 1. Convert the data to a JSON string
+//     const jsonString = JSON.stringify(API_RESPONSE_AI, null, 2); 
+    
+//     // 2. Create a Blob from the JSON string
+//     const blob = new Blob([jsonString], { type: 'application/json' });
+    
+//     // 3. Create a temporary URL and link element
+//     const url = URL.createObjectURL(blob);
+//     const a = document.createElement('a');
+    
+//     // 4. Set download attributes
+//     a.href = url;
+//     a.download = 'api_generated_results.json';
+    
+//     // 5. Simulate a click to trigger download
+//     document.body.appendChild(a);
+//     a.click();
+    
+//     // 6. Clean up
+//     document.body.removeChild(a);
+//     URL.revokeObjectURL(url);
+// });
+
+
+/**
+ * Renders Mandatory attributes using a card-based comparison layout.
+ * Highlights mismatches prominently.
+ * @param {Object} attributes - The mandatory attribute data.
+ * @param {string} title - The title for the section (used for the header).
+ * @returns {HTMLElement} A div containing the comparison cards.
+ */
+// function renderMandatoryComparisonCards(attributes, title) {
+//     const section = el('div', 'attribute-section mandatory-comparison-section');
+    
+//     // --- 1. Flatten Mandatory Attributes ---
+//     let attributeEntries = [];
+
+//     Object.keys(attributes).forEach(key => {
+//         const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]]; 
+//         valuesArray.forEach(v => {
+//             const aiValue = v.value || 'N/A';
+//             const originalValue = v.original_value || 'N/A';
+            
+//             const isMatch = (String(aiValue).trim().toLowerCase() === String(originalValue).trim().toLowerCase());
+
+//             attributeEntries.push({
+//                 name: key,
+//                 aiValue: aiValue,
+//                 originalValue: originalValue,
+//                 isMatch: isMatch,
+//                 source: v.source || 'N/A'
+//             });
+//         });
+//     });
+
+//     // --- 2. Section Header ---
+//     const titleEl = el('div', 'section-title');
+//     titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+//     section.appendChild(titleEl);
+    
+//     if (attributeEntries.length === 0) {
+//         const msg = el('p', 'no-attributes-message');
+//         msg.textContent = `No ${title.toLowerCase()} found.`;
+//         section.appendChild(msg);
+//         return section;
+//     }
+
+//     // --- 3. Card Container ---
+//     const cardsContainer = el('div', 'comparison-cards-container');
+
+//     attributeEntries.forEach(attr => {
+//         const card = el('div', `comparison-card ${attr?.isMatch ? 'match' : 'mismatch-card'}`);
+
+//         const header = el('div', 'card-header');
+//         header.textContent = attr?.name.replace(/_/g, ' ');
+//         card.appendChild(header);
+
+//         const content = el('div', 'card-content');
+
+//         // Existing Value Box
+//         const originalBox = el('div', 'value-box original-box');
+//         originalBox.innerHTML = `
+//             <div class="value-label">Manually Identified Value</div>
+//             <div class="value-text">${attr?.originalValue}</div>
+//         `;
+//         content.appendChild(originalBox);
+
+//         // AI Value Box
+//         const aiBox = el('div', `value-box ai-box ${attr?.isMatch ? 'found-value' : 'mismatch-value'}`);
+//         aiBox.innerHTML = `
+//             <div class="value-label">AI Generated Value <span class="value-source">(${attr?.source})</span></div>
+//             <div class="value-text">${attr?.aiValue}</div>
+//         `;
+//         content.appendChild(aiBox);
+
+//         card.appendChild(content);
+
+//         // Mismatch Indicator
+//         if (!attr?.isMatch) {
+//             const indicator = el('div', 'mismatch-indicator');
+//             // indicator.innerHTML = '❌ MISMATCH';
+//             indicator.innerHTML = attr?.isMatch ? '✅ MATCH' : '❌ MISMATCH';
+//             card.appendChild(indicator);
+//         }
+
+//         cardsContainer.appendChild(card);
+//     });
+
+//     section.appendChild(cardsContainer);
+//     return section;
+// }
+
+function renderMandatoryComparisonCards(attributes, title) {
+    const section = el('div', 'attribute-section mandatory-comparison-section');
+    
+    // --- 1. Flatten Mandatory Attributes ---
+    let attributeEntries = [];
+
+    Object.keys(attributes).forEach(key => {
+        const valuesArray = Array.isArray(attributes[key]) ? attributes[key] : [attributes[key]]; 
+        valuesArray.forEach(v => {
+            const aiValue = v.value || 'N/A';
+            const originalValue = v.original_value || 'N/A';
+            
+            const isMatch = (String(aiValue).trim().toLowerCase() === String(originalValue).trim().toLowerCase());
+
+            attributeEntries.push({
+                name: key,
+                aiValue: aiValue,
+                originalValue: originalValue,
+                isMatch: isMatch,
+                source: v.source || 'N/A'
+            });
+        });
+    });
+
+    // --- 2. Section Header ---
+    const titleEl = el('div', 'section-title');
+    titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+    section.appendChild(titleEl);
+    
+    if (attributeEntries.length === 0) {
+        const msg = el('p', 'no-attributes-message');
+        msg.textContent = `No ${title.toLowerCase()} found.`;
+        section.appendChild(msg);
+        return section;
+    }
+
+    // --- 3. Card Container ---
+    const cardsContainer = el('div', 'comparison-cards-container');
+
+    attributeEntries.forEach(attr => {
+        // --- CHANGE 1: Apply 'match-card' or 'mismatch-card' explicitly ---
+        const cardClass = attr?.isMatch ? 'match-card' : 'mismatch-card';
+        const card = el('div', `comparison-card ${cardClass}`);
+
+        const header = el('div', 'card-header');
+        header.textContent = attr?.name.replace(/_/g, ' ');
+        card.appendChild(header);
+
+        const content = el('div', 'card-content');
+
+        // Existing Value Box
+        const originalBox = el('div', 'value-box original-box');
+        originalBox.innerHTML = `
+            <div class="value-label">Manually Identified Value</div>
+            <div class="value-text">${attr?.originalValue}</div>
+        `;
+        content.appendChild(originalBox);
+
+        // AI Value Box
+        // Removed 'found-value' class here as styling should rely on the parent card class
+        const aiBox = el('div', `value-box ai-box ${attr?.isMatch ? '' : 'mismatch-value'}`); 
+        aiBox.innerHTML = `
+            <div class="value-label">AI Generated Value <span class="value-source">(${attr?.source})</span></div>
+            <div class="value-text">${attr?.aiValue}</div>
+        `;
+        content.appendChild(aiBox);
+
+        card.appendChild(content);
+
+        // --- CHANGE 2: Display the indicator for ALL cards, controlling color via CSS ---
+        const indicator = el('div', 'match-status-indicator');
+        indicator.innerHTML = attr?.isMatch ? '✅ MATCH' : '❌ MISMATCH';
+        card.appendChild(indicator);
+
+        cardsContainer.appendChild(card);
+    });
+
+    section.appendChild(cardsContainer);
+    return section;
+}
+
+/**
+ * Renders Additional, OCR, or Visual attributes in a simple card layout.
+ * @param {Object} attributes - The attribute data (Additional, OCR, or Visual).
+ * @param {string} title - The title for the section.
+ * @returns {HTMLElement} A div containing the attribute cards.
+ */
+function renderSimpleAttributeCards(attributes, title) {
+    const section = el('div', 'attribute-section simple-attribute-section');
+    
+    // --- 1. Flatten Attributes ---
+    let attributeEntries = [];
+
+    const processAttribute = (key, values) => {
+        const valuesArray = Array.isArray(values) ? values : [values]; 
+        valuesArray.forEach(v => {
+            attributeEntries.push({
+                name: key,
+                value: v.value,
+                source: v.source || 'N/A'
+            });
+        });
+    };
+
+    Object.keys(attributes).forEach(key => {
+        const attribute = attributes[key];
+        
+        if (Array.isArray(attribute)) {
+            processAttribute(key, attribute);
+        } else if (typeof attribute === 'object' && attribute !== null) {
+            Object.keys(attribute).forEach(subKey => {
+                const subAttribute = attribute[subKey];
+                if (Array.isArray(subAttribute)) {
+                    processAttribute(`${key} (${subKey.replace(/_/g, ' ')})`, subAttribute);
+                }
+            });
+        }
+    });
+
+    // --- 2. Section Header ---
+    const titleEl = el('div', 'section-title');
+    titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
+    section.appendChild(titleEl);
+    
+    if (attributeEntries.length === 0) {
+        const msg = el('p', 'no-attributes-message');
+        msg.textContent = `No ${title.toLowerCase()} found.`;
+        section.appendChild(msg);
+        return section;
+    }
+
+    // --- 3. Card Container ---
+    const cardsContainer = el('div', 'comparison-cards-container simple-cards-container');
+
+    attributeEntries.forEach(attr => {
+        // Simple Card Element
+        const card = el('div', 'simple-card');
+
+        // Card Header (Attribute Name)
+        const header = el('div', 'card-header');
+        header.textContent = formatString(attr?.name).replace(/_/g, ' ');
+        card.appendChild(header);
+
+        // Content Wrapper
+        const content = el('div', 'card-content');
+        
+        // Value Box
+        const valueBox = el('div', 'value-box single-value-box');
+        const displayValue = Array.isArray(attr?.value) 
+            ? (attr?.value.map(v => formatString(v.value))).join(', ') +'('+ formatString(attr?.source)+ ')' 
+            : formatString(attr?.value) +'('+ formatString(attr?.source)+ ')'; 
+            // : attr?.value;
+            
+        valueBox.innerHTML = `
+            <div class="value-label">Extracted Value <span class="value-source">(${formatString(attr?.source)})</span></div>
+            <div class="value-text">${displayValue || 'N/A'}</div>
+        `;
+        content.appendChild(valueBox);
+
+        card.appendChild(content);
+        cardsContainer.appendChild(card);
+    });
+
+    section.appendChild(cardsContainer);
+    return section;
+}
+
+function delay(ms) {
+    return new Promise(resolve => setTimeout(resolve, ms));
+}

+ 42 - 0
title_creator_app/templates/footer.html

@@ -0,0 +1,42 @@
+{% load static %}
+    <footer class="app-footer">
+        <strong>
+            Copyright &copy; 2014-2025&nbsp;
+            <a href="https://www.luminadatamatics.com/" target="_blank" class="text-decoration-none">Lumina
+                Datamatics LTD</a>.
+        </strong>
+        All rights reserved.
+    </footer> 
+</div>
+<!-- Scripts -->
+<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+<script src="{% static 'js/adminlte.js' %}"></script>
+<script>
+        const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
+        const Default = {
+            scrollbarTheme: "os-theme-light",
+            scrollbarAutoHide: "leave",
+            scrollbarClickScroll: true,
+        };
+        document.addEventListener("DOMContentLoaded", function () {
+            const sidebarWrapper = document.querySelector(SELECTOR_SIDEBAR_WRAPPER);
+            if (
+                sidebarWrapper &&
+                typeof OverlayScrollbarsGlobal?.OverlayScrollbars !== "undefined"
+            ) {
+                OverlayScrollbarsGlobal.OverlayScrollbars(sidebarWrapper, {
+                    scrollbars: {
+                        theme: Default.scrollbarTheme,
+                        autoHide: Default.scrollbarAutoHide,
+                        clickScroll: Default.scrollbarClickScroll,
+                    },
+                });
+            }
+        });
+    </script>
+</body>
+</html>

+ 155 - 0
title_creator_app/templates/header.html

@@ -0,0 +1,155 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>title creator</title>
+
+    <!-- Meta -->
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="title" content="title creator">
+
+    <!-- Fonts -->
+    <link rel="stylesheet"
+          href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css">
+
+    <!-- Plugins -->
+    <link rel="stylesheet"
+          href="{% static 'css/overlayscrollbars.min.css' %}">
+    <link rel="stylesheet"
+          href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css">
+    <link rel="stylesheet"
+          href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">
+
+    <!-- AdminLTE -->
+    <link rel="stylesheet" href="{% static 'css/adminlte.css' %}">
+    <link rel="stylesheet" href="{% static 'css/select2-bootstrap4.min.css' %}">
+    <link rel="stylesheet" href="{% static 'css/custom.css' %}">
+
+    <style>
+        .select2-container .select2-search--inline .select2-search__field {
+            top: 3px;
+            font-size: 14px;
+        }
+
+        #full-page-loader {
+            position: fixed;
+            inset: 0;
+            background: rgba(0, 0, 0, 0.6);
+            z-index: 9999;
+            display: none;
+            justify-content: center;
+            align-items: center;
+        }
+
+        #full-page-loader .spinner-border {
+            width: 3rem;
+            height: 3rem;
+        }
+    </style>
+</head>
+
+<body class="layout-fixed sidebar-expand-lg sidebar-mini sidebar-collapse app-loaded">
+<div class="app-wrapper">
+
+<style>
+    .user-avatar {
+        display: inline-flex;
+        justify-content: center;
+        align-items: center;
+        background-color: #007bff;
+        color: #fff;
+        font-weight: 600;
+        border-radius: 50%;
+        text-transform: uppercase;
+    }
+
+    .user-avatar-sm {
+        width: 35px;
+        height: 35px;
+        font-size: 18px;
+    }
+
+    .user-avatar-lg {
+        width: 100px;
+        height: 100px;
+        font-size: 66px;
+    }
+</style>
+
+<nav class="app-header navbar navbar-expand bg-body">
+    <div class="container-fluid">
+
+        <!-- Left Navbar -->
+        <ul class="navbar-nav">
+            <li class="nav-item">
+                <a class="nav-link" data-lte-toggle="sidebar" href="#" role="button">
+                    <i class="bi bi-list"></i>
+                </a>
+            </li>
+        </ul>
+
+        <!-- Right Navbar -->
+        <ul class="navbar-nav ms-auto">
+
+            <!-- Fullscreen -->
+            <li class="nav-item">
+                <a class="nav-link" href="#" data-lte-toggle="fullscreen">
+                    <i data-lte-icon="maximize" class="bi bi-arrows-fullscreen"></i>
+                    <i data-lte-icon="minimize" class="bi bi-fullscreen-exit d-none"></i>
+                </a>
+            </li>
+
+            <!-- User Menu -->
+            <li class="nav-item dropdown user-menu">
+                <a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown">
+
+                    {% if request.session.user_email %}
+                        <span class="user-avatar user-avatar-sm shadow">
+                            {{ request.session.user_email|slice:":1" }}
+                        </span>
+                    {% else %}
+                        <img src="{% static 'images/user2-160x160.jpg' %}"
+                             class="user-image rounded-circle shadow"
+                             alt="User Image">
+                    {% endif %}
+
+                    <span class="d-none d-md-inline">
+                        {{ request.session.user_email }}
+                    </span>
+                </a>
+
+                <ul class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
+
+                    <!-- User Header -->
+                    <li class="user-header text-bg-secondary">
+                        {% if request.session.user_email %}
+                            <span class="user-avatar user-avatar-lg shadow">
+                                {{ request.session.user_email|slice:":1" }}
+                            </span>
+                        {% else %}
+                            <img src="{% static 'images/user2-160x160.jpg' %}"
+                                 class="rounded-circle shadow"
+                                 alt="User Image">
+                        {% endif %}
+
+                        <p>
+                            {{ request.session.user_email }} – Admin
+                        </p>
+                    </li>
+
+                    <!-- User Footer -->
+                    <li class="user-footer">
+                        <a href="#" class="btn btn-default btn-flat">Profile</a>
+                        <a href="{% url 'logout' %}" class="btn btn-default btn-flat float-end">
+                            Sign out
+                        </a>
+                    </li>
+
+                </ul>
+            </li>
+        </ul>
+    </div>
+</nav>
+
+{% include 'sidebar.html' %}

+ 79 - 0
title_creator_app/templates/login.html

@@ -0,0 +1,79 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>Title Creator - Login</title>
+
+    <!-- Latest compiled and minified CSS -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
+    <link rel="stylesheet" href="{% static 'css/login.css' %}">
+</head>
+
+<body>
+    <div class="container">
+        <div class="login">
+            <div class="login-container">
+                <div class="login-left">
+                    <div class="lbox">
+                       
+                        <h2 class="clogo">Content Title Creator</h2>
+                        <img src="{% static 'images/logo.png' %}" alt="Lumina Datamatics" class="llogo">
+                        <!-- <p>© 2022 Lumina Datamatics Ltd. All Rights Reserved.</p> -->
+                    </div>
+                </div>
+                <div class="login-right">
+                    <form action="{% url 'login' %}" method="POST">
+                        {% csrf_token %}
+                        <h2 class="form-signin-heading">Login</h2>
+                        <div class="group">
+                            <input type="text" name="username" id="username">
+                            <label>Email address</label>
+                        </div>
+
+                        <div class="group">
+                            <input type="password" name="password" id="password">
+                            <label>Password</label>
+                        </div>
+                        <div class="group" style="margin-bottom: 30px;">
+                            <button class="btn btn-lg btn-outline-dark btn-block" type="submit">Sign in</button>
+                            <!-- <a href="#" class="forgotPass">Forgot your password?</a> -->
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+
+
+    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
+    <!-- Latest compiled and minified JavaScript -->
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
+    <script>
+        function validateLogin() {
+            if ($("#username").val() == "" || $("#password").val() == "") {
+                $("#missing").fadeTo(2000, 500).slideUp(500, function () {
+                    $("#missing").slideUp(500);
+                });
+                return false;
+            } else if ($("#username").val() != "admin" && $("#password").val() != "admin") {
+                $("#wrong").fadeTo(2000, 500).slideUp(500, function () {
+                    $("#wrong").slideUp(500);
+                });
+                return false;
+            } else if ($("#username").val() == "admin" && $("#password").val() == "admin") {
+                $("#success").fadeTo(2000, 500).slideUp(500, function () {
+                    $("#success").slideUp(500);
+                });
+                return true;
+            }
+        }
+    </script>
+
+</body>
+
+</html>

+ 40 - 0
title_creator_app/templates/sidebar.html

@@ -0,0 +1,40 @@
+{% load static %}
+
+<aside class="app-sidebar shadow">
+    <!-- Sidebar Brand -->
+    <div class="sidebar-brand">
+        <a href="{% url 'home' %}" class="brand-link">
+            <img src="{% static 'images/logo-mini.png' %}" alt="Lumina Datamatics" class="brand-image logo-mini">
+            <span class="brand-text fw-light">
+                <img src="{% static 'images/logo.png' %}" alt="Lumina Datamatics" class="brand-image">
+            </span>
+        </a>
+    </div>
+
+    <!-- Sidebar Wrapper -->
+    <div class="sidebar-wrapper">
+        <nav class="mt-2">
+            <ul class="nav sidebar-menu flex-column" data-lte-toggle="treeview" role="menu" data-accordion="false">
+
+                <!-- Content Scorecard -->
+                <li class="nav-item">
+                    <a href="{% url 'home' %}"
+                       class="nav-link {% if request.resolver_match.url_name == 'home' %}active{% endif %}">
+                        <i class="nav-icon bi bi-upload"></i>
+                        <p>Title Generator</p>
+                    </a>
+                </li>
+
+                <!-- Product Attributes -->
+                <li class="nav-item">
+                    <a href="{% url 'master' %}"
+                       class="nav-link {% if request.resolver_match.url_name == 'master' %}active{% endif %}">
+                        <i class="nav-icon bi bi-gear"></i>
+                        <p>Master</p>
+                    </a>
+                </li>
+
+            </ul>
+        </nav>
+    </div>
+</aside>

+ 412 - 0
title_creator_app/templates/title_creator_index.html

@@ -0,0 +1,412 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Column Mapping</title>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
+<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
+        integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" crossorigin="anonymous">
+    <!--end::Fonts--><!--begin::Third Party Plugin(OverlayScrollbars)-->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/styles/overlayscrollbars.min.css"
+        integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" crossorigin="anonymous">
+    <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Third Party Plugin(Bootstrap Icons)-->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css"
+        integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" crossorigin="anonymous">
+    <!--end::Third Party Plugin(Bootstrap Icons)--><!--begin::Required Plugin(AdminLTE)-->
+    <link rel="stylesheet" href="{% static './css/adminlte.css' %}"><!--end::Required Plugin(AdminLTE)--><!-- apexcharts -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/apexcharts@3.37.1/dist/apexcharts.css"
+        integrity="sha256-4MX+61mt9NVvvuPjUWdUdyfZfxSB1/Rf9WtqRHgG5S0=" crossorigin="anonymous">
+    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
+    <link rel="stylesheet" href="{% static './css/select2-bootstrap4.min.css' %}">
+    <link rel="stylesheet" href="{% static './css/custom.css' %}">
+    <script src="https://cdn.tailwindcss.com"></script>
+<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;800&display=swap" rel="stylesheet">
+<style>
+    /* body { font-family: 'Plus Jakarta Sans', sans-serif; background-color: #f3f4f6; } */
+    .error-text { color: #ef4444; font-style: italic; font-weight: 500; }
+    .success-text { color: #059669; font-weight: 600; }
+</style>
+<style>
+    /* General Container and Layout */
+    body {
+        background-color: #f8f9fa; /* Light grey background */
+    }
+    .mapping-container {
+        max-width: 1200px;
+        margin: 40px auto;
+        background: #ffffff;
+        padding: 30px;
+        border-radius: 12px;
+        box-shadow: 0 8px 25px rgba(0,0,0,0.08);
+    }
+    /* Uploaded Columns (Source) */
+    #uploaded-columns-wrapper {
+        border: 1px solid #dee2e6;
+        border-radius: 6px;
+        padding: 10px;
+        min-height: 200px;
+        background-color: #e9ecef; /* Slightly darker background for source */
+    }
+    .drag-item {
+        padding: 8px 15px;
+        margin: 8px 0;
+        background: #007bff; /* Primary blue for drag items */
+        color: white;
+        border-radius: 4px;
+        cursor: grab;
+        transition: transform 0.1s, box-shadow 0.1s;
+        font-weight: 500;
+        display: inline-block;
+        width: 100%;
+    }
+    .drag-item:hover {
+        background: #0056b3;
+        box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+    }
+    /* System Columns (Destination/Drop Zones) */
+    .drop-zone-container {
+        padding: 15px;
+        border: 1px solid #dee2e6;
+        border-radius: 6px;
+        min-height: 200px;
+    }
+    .drop-zone {
+        min-height: 45px;
+        padding: 10px;
+        margin-bottom: 10px;
+        border: 2px dashed #adb5bd; /* Subtler dashed border */
+        border-radius: 6px;
+        background: #f1f3f5; /* Light grey background for drop zone */
+        font-weight: bold;
+        color: #495057;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        transition: background-color 0.2s, border-color 0.2s;
+    }
+    .drop-zone.hover {
+        border-color: #28a745; /* Green on hover */
+        background: #e6ffed; /* Light green background on hover */
+    }
+    .drop-zone strong {
+        color: #dc3545; /* Red for the mapped column name */
+        font-weight: 600;
+        padding: 4px 8px;
+        background-color: #ffe8e8;
+        border-radius: 4px;
+    }
+    .drop-zone .system-label {
+        color: #495057;
+    }
+    .drop-zone .mapped-content {
+        display: flex;
+        align-items: center;
+    }
+    .drop-zone .mapped-content .reset-map {
+        margin-left: 10px;
+        cursor: pointer;
+        color: #6c757d;
+        font-size: 1.2rem;
+        transition: color 0.2s;
+    }
+    .drop-zone .mapped-content .reset-map:hover {
+        color: #dc3545;
+    }
+    /* Preview Table */
+    .preview-table {
+        margin-top: 30px;
+        border-top: 2px solid #007bff;
+        padding-top: 20px;
+    }
+    .preview-table th {
+        background-color: #007bff;
+        color: white;
+    }
+</style>
+</head>
+<!-- <body> -->
+
+<body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse">
+    <!--begin::App Wrapper-->
+    <div class="app-wrapper"> <!--begin::Header-->
+        {% include 'header.html' %}
+        {% include 'sidebar.html' %}
+<!-- Main -->
+<main class="app-main">
+    <div class="app-content">
+    <div class="max-w-6xl mx-auto">
+        <div class="flex justify-between items-center mb-10">
+            <div id="downloadArea" class="hidden">
+                <a id="downloadBtn" href="#" download class="flex items-center gap-2 bg-green-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-green-700 transition-all shadow-lg">
+                    <i data-lucide="download"></i> Download Modified Excel
+                </a>
+            </div>
+        </div>
+        <div class="mb-10 text-center">
+            <h1 class="text-4xl font-extrabold text-gray-900 mb-2">Title Creator </h1>
+            <p class="text-gray-500">Upload Excel URLs to generate dynamic titles from Data</p>
+        </div>
+
+        <div id="uploadCard" class="bg-white p-8 rounded-3xl shadow-sm border border-gray-200 mb-8 transition-all">
+            <div class="mb-6">
+                <label class="block text-left text-xs font-bold text-gray-400 uppercase tracking-widest mb-2">1. Select Configuration Type</label>
+                <select id="ptDropdown" class="w-full p-4 bg-gray-50 border border-gray-200 rounded-xl font-semibold text-gray-700 outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer">
+                    <option value="" disabled selected>Choose Product Type Configuration...</option>
+                    {% for pt in product_types %}
+                        <option value="{{ pt }}">{{ pt }}</option>
+                    {% endfor %}
+                </select>
+            </div>
+            <label class="block text-left text-xs font-bold text-gray-400 uppercase tracking-widest mb-2">2. Upload Source</label>
+            <div class="flex flex-col items-center justify-center border-2 border-dashed border-gray-300 rounded-2xl p-10 hover:border-blue-500 transition-colors cursor-pointer" onclick="document.getElementById('fileInput').click()">
+                <div class="bg-blue-50 p-4 rounded-full mb-4">
+                    <svg class="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
+                </div>
+                <p id="fileNameDisplay" class="text-lg font-semibold text-gray-700">Click to upload Product URL Excel</p>
+                <input type="file" id="fileInput" class="hidden" accept=".xlsx, .xls">
+            </div>
+            <!-- <button id="processBtn" class="w-full mt-6 bg-gray-900 text-white py-4 rounded-xl font-bold hover:bg-gray-800 disabled:opacity-50 transition-all" disabled>
+                 Direct Process (Wait)
+            </button>
+            <button id="asyncBtn" onclick="startBackgroundTask()" class="w-full mt-6 flex-1 bg-indigo-600 text-white py-4 rounded-xl font-bold hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg" disabled>
+                Run in Background
+            </button> -->
+        <div class="d-flex ">
+                <button id="processBtn" style="width: 50%;" class=" mt-6 bg-gray-900 text-white py-4 rounded-xl font-bold hover:bg-gray-800 disabled:opacity-50 transition-all" disabled>
+                    <!-- Start Uploading & Title Generation -->
+                    Direct Process (Wait)
+                </button>
+                <button id="asyncBtn" onclick="startBackgroundTask()" style="width: 50%;margin-left: 10px;" class=" mt-6 flex-1 bg-indigo-600 text-white py-4 rounded-xl font-bold hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg" disabled>
+                    Run in Background
+                </button>
+            </div>
+        </div>
+
+        <div id="statusCard" class="hidden bg-white p-8 rounded-3xl shadow-sm border border-indigo-200 mb-8 text-center">
+            <div id="statusIcon" class="mb-4">
+                <div class="w-12 h-12 border-4 border-indigo-600 border-t-transparent rounded-full animate-spin mx-auto"></div>
+            </div>
+            <h3 id="statusTitle" class="text-xl font-bold text-gray-900">Background Task Started</h3>
+            <p id="statusFile" class="text-gray-500 text-sm mt-1"></p>
+            <div class="mt-6 p-4 bg-indigo-50 rounded-xl">
+                <p id="statusMsg" class="text-indigo-700 font-semibold italic">Waiting for server response...</p>
+            </div>
+            <p class="text-xs text-gray-400 mt-4">You can stay on this page; we will show the download link once finished.</p>
+        </div>
+
+        <div id="resultsCard" class="hidden bg-white rounded-3xl shadow-xl border border-gray-100 overflow-hidden">
+            <div class="p-6 border-b border-gray-100 flex justify-between items-center">
+                <h2 class="text-xl font-bold text-gray-800">New Title Results</h2>
+                <span id="counter" class="bg-blue-100 text-blue-700 px-3 py-1 rounded-full text-xs font-bold uppercase">0 Items</span>
+            </div>
+            <div class="overflow-x-auto">
+                <table class="w-full text-left">
+                    <thead class="bg-gray-50 text-gray-400 text-[10px] uppercase tracking-widest font-bold">
+                        <tr>
+                            <th class="px-6 py-4">#</th>
+                            <th class="px-6 py-4">Source URL</th>
+                            <th class="px-6 py-4">Generated Dynamic Title</th>
+                            <th class="px-6 py-4">Status</th>
+                        </tr>
+                    </thead>
+                    <tbody id="tableBody" class="divide-y divide-gray-100 text-sm">
+                        </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+
+    <div id="loader" class="hidden fixed inset-0 bg-white/80 backdrop-blur-md z-50 flex flex-col items-center justify-center">
+        <div class="w-16 h-16 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mb-4"></div>
+        <p class="text-gray-900 font-bold text-xl">Working On Title Creation...</p>
+        <p class="text-gray-500">This may take a moment depending on the URL count</p>
+    </div>
+</div>
+    <script>
+        const fileInput = document.getElementById('fileInput');
+        const ptDropdown = document.getElementById('ptDropdown');
+        const processBtn = document.getElementById('processBtn');
+        const fileNameDisplay = document.getElementById('fileNameDisplay');
+        const loader = document.getElementById('loader');
+        const asyncBtn = document.getElementById('asyncBtn');
+
+        // Enable button only if both PT is selected and File is chosen
+        const validateForm = () => {
+            const isValid = fileInput.files.length > 0 && ptDropdown.value !== "";
+            processBtn.disabled = !isValid;
+            asyncBtn.disabled = !isValid; // Enable the new button too
+            // processBtn.disabled = !(fileInput.files.length > 0 && ptDropdown.value !== "");
+        };
+
+        fileInput.onchange = () => {
+            if(fileInput.files.length > 0) {
+                fileNameDisplay.innerText = fileInput.files[0].name;
+                fileNameDisplay.classList.add('text-blue-600');
+            }
+            validateForm();
+        };
+
+        // fileInput.onchange = () => { processBtn.disabled = !fileInput.files.length; };
+
+        ptDropdown.onchange = validateForm;
+
+        processBtn.onclick = async () => {
+            const formData = new FormData();
+            formData.append('file', fileInput.files[0]);
+            formData.append('product_type', ptDropdown.value); // Sending selected PT
+            loader.classList.remove('hidden');
+
+            try {
+                const response = await fetch('/title-creator/upload/', { // Hits the same view
+                    method: 'POST',
+                    body: formData,
+                    headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                });
+                const data = await response.json();
+
+                if (data.success) {
+                    renderTable(data.results);
+                    document.getElementById('downloadBtn').href = data.download_url;
+                    document.getElementById('downloadArea').classList.remove('hidden');
+                    // document.getElementById('dropZone').classList.add('hidden');
+                } else {
+                    alert(data.error);
+                }
+            } catch (err) {
+                console.log("err",err);
+                alert("An error occurred during processing.");
+            } finally {
+                loader.classList.add('hidden');
+            }
+        };
+
+        function renderTable(results) {
+            document.getElementById('uploadCard').classList.add('hidden');
+            document.getElementById('resultsCard').classList.remove('hidden');
+            document.getElementById('counter').innerText = `${results.length} Items`;
+
+            const tbody = document.getElementById('tableBody');
+            tbody.innerHTML = results.map(res => {
+                const isError = res.new_title.includes("Could not found");
+                return `
+                    <tr class="hover:bg-gray-50 transition-colors">
+                        <td class="px-6 py-4 font-bold text-gray-400">${res.id}</td>
+                        <td class="px-6 py-4">
+                            <a href="${res.url}" target="_blank" class="text-blue-500 hover:underline truncate block max-w-xs">${res.url}</a>
+                        </td>
+                        <td class="px-6 py-4 ${isError ? 'error-text' : 'success-text'}">
+                            ${res.new_title}
+                        </td>
+                        <td class="px-6 py-4 text-xs font-bold uppercase ${res.status === 'Success' ? 'text-green-500' : 'text-red-500'}">
+                            ${res.status}
+                        </td>
+                    </tr>
+                `;
+            }).join('');
+        }
+
+        // Add these to your existing validateForm function
+        // const asyncBtn = document.getElementById('asyncBtn');
+        // const validateForm = () => {
+        //     const isValid = fileInput.files.length > 0 && ptDropdown.value !== "";
+        //     processBtn.disabled = !isValid;
+        //     asyncBtn.disabled = !isValid; // Enable the new button too
+        // };
+
+        async function startBackgroundTask() {
+            const formData = new FormData();
+            formData.append('file', fileInput.files[0]);
+            formData.append('product_type', ptDropdown.value);
+
+            // Toggle UI
+            document.getElementById('uploadCard').classList.add('hidden');
+            document.getElementById('statusCard').classList.remove('hidden');
+            document.getElementById('statusFile').innerText = `File: ${fileInput.files[0].name}`;
+
+            try {
+                const response = await fetch('/title-creator/upload-async/', { // New Endpoint
+                    method: 'POST',
+                    body: formData,
+                    headers: {'X-CSRFToken': '{{ csrf_token }}'}
+                });
+                const data = await response.json();
+
+                if (data.task_id) {
+                    pollStatus(data.task_id);
+                }
+            } catch (err) {
+                alert("Failed to start background task.");
+            }
+        }
+
+        function pollStatus(taskId) {
+            const msg = document.getElementById('statusMsg');
+            const interval = setInterval(async () => {
+                try {
+                    const response = await fetch(`/check-status/${taskId}/`);
+                    const data = await response.json();
+
+                    if (data.status === 'COMPLETED') {
+                        clearInterval(interval);
+                        document.getElementById('statusIcon').innerHTML = "✅";
+                        document.getElementById('statusTitle').innerText = "Process Complete!";
+                        msg.innerHTML = `<a href="${data.download_url}" class="text-white bg-green-600 px-6 py-2 rounded-lg inline-block">Download Ready: ${data.file_name}</a>`;
+                    } else if (data.status === 'FAILED') {
+                        clearInterval(interval);
+                        msg.innerText = "Error: Processing failed.";
+                        msg.classList.add('text-red-600');
+                    } else {
+                        msg.innerText = "Task in progress... still scraping URLs.";
+                    }
+                } catch (e) {
+                    console.error("Polling error", e);
+                }
+            }, 4000); // Poll every 4 seconds
+        }
+    </script>
+ </main>
+   {% include 'footer.html' %}
+
+    </div> 
+
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"
+        integrity="sha256-H2VM7BKda+v2Z4+DRy69uknwxjyDRhszjXFhsL4gD3w=" crossorigin="anonymous"></script>
+    <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Required Plugin(popperjs for Bootstrap 5)-->
+    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
+        integrity="sha256-whL0tQWoY1Ku1iskqPFvmZ+CHsvmRWx/PIoEvIeWh4I=" crossorigin="anonymous"></script>
+    <!--end::Required Plugin(popperjs for Bootstrap 5)--><!--begin::Required Plugin(Bootstrap 5)-->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
+        integrity="sha256-YMa+wAM6QkVyz999odX7lPRxkoYAan8suedu4k2Zur8=" crossorigin="anonymous"></script>
+    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+    <!--end::Required Plugin(Bootstrap 5)--><!--begin::Required Plugin(AdminLTE)-->
+    <script src="{% static './js/adminlte.js' %}"></script>
+    <!--end::Required Plugin(AdminLTE)--><!--begin::OverlayScrollbars Configure-->
+    <script>
+        const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
+        const Default = {
+            scrollbarTheme: "os-theme-light",
+            scrollbarAutoHide: "leave",
+            scrollbarClickScroll: true,
+        };
+        document.addEventListener("DOMContentLoaded", function () {
+            const sidebarWrapper = document.querySelector(SELECTOR_SIDEBAR_WRAPPER);
+            if (
+                sidebarWrapper &&
+                typeof OverlayScrollbarsGlobal?.OverlayScrollbars !== "undefined"
+            ) {
+                OverlayScrollbarsGlobal.OverlayScrollbars(sidebarWrapper, {
+                    scrollbars: {
+                        theme: Default.scrollbarTheme,
+                        autoHide: Default.scrollbarAutoHide,
+                        clickScroll: Default.scrollbarClickScroll,
+                    },
+                });
+            }
+        });
+    </script>
+</body>
+</html>

+ 514 - 0
title_creator_app/templates/title_creator_master.html

@@ -0,0 +1,514 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Column Mapping</title>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
+<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
+        integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" crossorigin="anonymous">
+    <!--end::Fonts--><!--begin::Third Party Plugin(OverlayScrollbars)-->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/styles/overlayscrollbars.min.css"
+        integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" crossorigin="anonymous">
+    <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Third Party Plugin(Bootstrap Icons)-->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css"
+        integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" crossorigin="anonymous">
+    <!--end::Third Party Plugin(Bootstrap Icons)--><!--begin::Required Plugin(AdminLTE)-->
+    <link rel="stylesheet" href="{% static './css/adminlte.css' %}"><!--end::Required Plugin(AdminLTE)--><!-- apexcharts -->
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/apexcharts@3.37.1/dist/apexcharts.css"
+        integrity="sha256-4MX+61mt9NVvvuPjUWdUdyfZfxSB1/Rf9WtqRHgG5S0=" crossorigin="anonymous">
+    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
+    <link rel="stylesheet" href="{% static './css/select2-bootstrap4.min.css' %}">
+    <link rel="stylesheet" href="{% static './css/custom.css' %}">
+    <script src="https://cdn.tailwindcss.com"></script>
+<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;800&display=swap" rel="stylesheet">
+<style>
+    /* body { font-family: 'Plus Jakarta Sans', sans-serif; background-color: #f3f4f6; } */
+    .error-text { color: #ef4444; font-style: italic; font-weight: 500; }
+    .success-text { color: #059669; font-weight: 600; }
+</style>
+<script src="https://cdn.tailwindcss.com"></script>
+    <script src="https://unpkg.com/lucide@latest"></script>
+    <style>
+        /* Custom scrollbar for a cleaner look */
+        .custom-scrollbar::-webkit-scrollbar { width: 5px; }
+        .custom-scrollbar::-webkit-scrollbar-track { background: #f1f1f1; }
+        .custom-scrollbar::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 10px; }
+        .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: #cbd5e1; }
+    </style>
+<style>
+    /* General Container and Layout */
+    body {
+        background-color: #f8f9fa; /* Light grey background */
+    }
+    .mapping-container {
+        max-width: 1200px;
+        margin: 40px auto;
+        background: #ffffff;
+        padding: 30px;
+        border-radius: 12px;
+        box-shadow: 0 8px 25px rgba(0,0,0,0.08);
+    }
+    /* Uploaded Columns (Source) */
+    #uploaded-columns-wrapper {
+        border: 1px solid #dee2e6;
+        border-radius: 6px;
+        padding: 10px;
+        min-height: 200px;
+        background-color: #e9ecef; /* Slightly darker background for source */
+    }
+    .drag-item {
+        padding: 8px 15px;
+        margin: 8px 0;
+        background: #007bff; /* Primary blue for drag items */
+        color: white;
+        border-radius: 4px;
+        cursor: grab;
+        transition: transform 0.1s, box-shadow 0.1s;
+        font-weight: 500;
+        display: inline-block;
+        width: 100%;
+    }
+    .drag-item:hover {
+        background: #0056b3;
+        box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+    }
+    /* System Columns (Destination/Drop Zones) */
+    .drop-zone-container {
+        padding: 15px;
+        border: 1px solid #dee2e6;
+        border-radius: 6px;
+        min-height: 200px;
+    }
+    .drop-zone {
+        min-height: 45px;
+        padding: 10px;
+        margin-bottom: 10px;
+        border: 2px dashed #adb5bd; /* Subtler dashed border */
+        border-radius: 6px;
+        background: #f1f3f5; /* Light grey background for drop zone */
+        font-weight: bold;
+        color: #495057;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        transition: background-color 0.2s, border-color 0.2s;
+    }
+    .drop-zone.hover {
+        border-color: #28a745; /* Green on hover */
+        background: #e6ffed; /* Light green background on hover */
+    }
+    .drop-zone strong {
+        color: #dc3545; /* Red for the mapped column name */
+        font-weight: 600;
+        padding: 4px 8px;
+        background-color: #ffe8e8;
+        border-radius: 4px;
+    }
+    .drop-zone .system-label {
+        color: #495057;
+    }
+    .drop-zone .mapped-content {
+        display: flex;
+        align-items: center;
+    }
+    .drop-zone .mapped-content .reset-map {
+        margin-left: 10px;
+        cursor: pointer;
+        color: #6c757d;
+        font-size: 1.2rem;
+        transition: color 0.2s;
+    }
+    .drop-zone .mapped-content .reset-map:hover {
+        color: #dc3545;
+    }
+    /* Preview Table */
+    .preview-table {
+        margin-top: 30px;
+        border-top: 2px solid #007bff;
+        padding-top: 20px;
+    }
+    .preview-table th {
+        background-color: #007bff;
+        color: white;
+    }
+</style>
+</head>
+<!-- <body> -->
+
+<body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse">
+    <!--begin::App Wrapper-->
+    <div class="app-wrapper"> <!--begin::Header-->
+        {% include 'header.html' %}
+        {% include 'sidebar.html' %}    
+<main class="app-main">
+    <div class="app-content">
+        <div class="max-w-6xl mx-auto space-y-8">
+            <h1 class="text-3xl font-black text-slate-800">Master Setup</h1>
+
+            <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
+                
+                <!-- <div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden flex flex-col">
+                    <div class="p-6 border-b bg-slate-50 flex justify-between items-center">
+                        <h2 class="font-bold text-slate-700">Attribute Master</h2>
+                    </div>
+                    
+                    <form method="POST" class="p-6 bg-indigo-50/50 border-b flex gap-3">
+                        {% csrf_token %}
+                        <input type="hidden" name="action" value="add_attribute">
+                        <input type="text" name="attr_name" placeholder="Attribute Name..." class="flex-1 p-2 border rounded-lg text-sm" required>
+                        <label class="flex items-center gap-2 text-xs font-bold text-slate-500">
+                            <input type="checkbox" name="is_mandatory"> Mandatory
+                        </label>
+                        <button class="bg-indigo-600 text-white px-4 py-2 rounded-lg text-xs font-bold hover:bg-indigo-700">Add</button>
+                    </form>
+
+                    <div class="p-6 space-y-3 max-h-[500px] overflow-y-auto custom-scrollbar">
+                        {% for attr in attributes %}
+                        <div class="flex items-center justify-between p-3 border rounded-xl group hover:border-indigo-300">
+                            <span class="font-semibold text-slate-700">{{ attr.name }} 
+                                {% if attr.is_mandatory %}<span class="ml-2 text-[10px] bg-red-100 text-red-600 px-2 py-0.5 rounded-full uppercase">Mandatory</span>{% endif %}
+                            </span>
+                            <form method="POST" onsubmit="return confirm('Delete this attribute?')">
+                                {% csrf_token %}
+                                <input type="hidden" name="action" value="delete_attribute">
+                                <input type="hidden" name="id" value="{{ attr.id }}">
+                                <button class="text-slate-300 hover:text-red-500 transition-colors"><i data-lucide="trash-2" class="w-4 h-4"></i></button>
+                            </form>
+                        </div>
+                        {% endfor %}
+                    </div>
+                </div> -->
+                <div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden flex flex-col">
+                    <div class="p-6 border-b bg-slate-50 flex justify-between items-center">
+                        <h2 class="font-bold text-slate-700">Attribute Master</h2>
+                    </div>
+                    
+                    <form method="POST" class="p-6 bg-indigo-50/50 border-b flex gap-3">
+                        {% csrf_token %}
+                        <input type="hidden" name="action" value="add_attribute">
+                        <input type="text" name="attr_name" placeholder="Attribute Name..." class="flex-1 p-2 border rounded-lg text-sm" required>
+                        <label class="flex items-center gap-2 text-xs font-bold text-slate-500">
+                            <input type="checkbox" name="is_mandatory"> Mandatory
+                        </label>
+                        <button class="bg-indigo-600 text-white px-4 py-2 rounded-lg text-xs font-bold hover:bg-indigo-700">Add</button>
+                    </form>
+
+                    <div id="attributeList" class="p-6 space-y-3 min-h-[400px]">
+                        {% for attr in attributes %}
+                        <div class="attr-card flex items-center justify-between p-3 border rounded-xl group hover:border-indigo-300">
+                            <span class="font-semibold text-slate-700">{{ attr.name }} 
+                                {% if attr.is_mandatory %}<span class="ml-2 text-[10px] bg-red-100 text-red-600 px-2 py-0.5 rounded-full uppercase">Mandatory</span>{% endif %}
+                            </span>
+                            <form method="POST" onsubmit="return confirm('Delete?')">
+                                {% csrf_token %}
+                                <input type="hidden" name="action" value="delete_attribute">
+                                <input type="hidden" name="id" value="{{ attr.id }}">
+                                <button class="text-slate-300 hover:text-red-500 transition-colors"><i data-lucide="trash-2" class="w-4 h-4"></i></button>
+                            </form>
+                        </div>
+                        {% endfor %}
+                    </div>
+
+                    <div class="p-4 border-t border-slate-100 flex items-center justify-between bg-slate-50/50">
+                        <span id="attr-page-info" class="text-[10px] text-slate-500 font-bold uppercase">Page 1</span>
+                        <div class="flex gap-2">
+                            <button onclick="changeAttrPage(-1)" id="attrPrevBtn" class="p-1.5 border rounded-lg hover:bg-white disabled:opacity-30"><i data-lucide="chevron-left" class="w-4 h-4"></i></button>
+                            <button onclick="changeAttrPage(1)" id="attrNextBtn" class="p-1.5 border rounded-lg hover:bg-white disabled:opacity-30"><i data-lucide="chevron-right" class="w-4 h-4"></i></button>
+                        </div>
+                    </div>
+                </div>          
+                <div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden">
+                    <div class="p-6 border-b bg-slate-50">
+                        <h2 class="font-bold text-slate-700">Title Mapping (Product Types)</h2>
+                    </div>
+
+                    <form id="mapping-form" method="POST" class="p-6 bg-emerald-50/50 border-b space-y-3">
+                        {% csrf_token %}
+                        <input type="hidden" name="action" value="add_mapping">
+                        <input type="hidden" name="edit_id" id="edit_id">
+                        
+                        <input type="text" name="pt_name" id="pt_name" placeholder="Product Type Name..." class="w-full p-2 border rounded-lg text-sm" required>
+                        
+                        <div class="space-y-2">    
+                            <label class="text-xs font-bold text-slate-500 uppercase">Sequence (Drag to reorder)</label>
+                            <div id="drop-zone" class="min-h-[45px] p-2 border-2 border-dashed border-emerald-200 rounded-lg bg-white flex flex-wrap gap-2"></div>
+                            <input type="hidden" name="sequence" id="sequence-input" required>
+
+                            <div class="space-y-1">
+                                <p class="text-[10px] text-slate-400 font-bold">CLICK TO ADD:</p>
+                                <div class="flex flex-wrap gap-2 max-h-32 overflow-y-auto custom-scrollbar p-1">
+                                    {% for attr in attributes %}
+                                    <button type="button" onclick="addAttribute('{{ attr.name }}')" class="attr-pill px-3 py-1 bg-slate-100 hover:bg-indigo-100 text-slate-600 rounded-full text-xs border border-slate-200">
+                                        + {{ attr.name }}
+                                    </button>
+                                    {% endfor %}
+                                </div>
+                            </div>
+                        </div>
+
+                        <button type="submit" id="submit-btn" class="w-full bg-emerald-600 text-white py-2 rounded-lg text-xs font-bold hover:bg-emerald-700 transition-colors">
+                            Create New Mapping
+                        </button>
+                        <button type="button" id="cancel-edit" class="hidden w-full text-xs text-slate-400 underline mt-2" onclick="resetForm()">
+                            Cancel Edit
+                        </button>
+                    </form>
+
+                    <div class="p-6 space-y-4">
+                        <div class="relative">
+                            <i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>
+                            <input type="text" id="mappingSearch" placeholder="Search product types..." 
+                                class="w-full pl-10 pr-4 py-2 border rounded-xl text-sm focus:ring-2 focus:ring-emerald-500 outline-none">
+                        </div>
+
+                        <div id="mappingList" class="space-y-4">
+                            {% for mapping in mappings %}
+                            <div class="mapping-card p-4 border rounded-xl bg-slate-50 hover:border-emerald-300 transition-colors" data-type="{{ mapping.product_type|lower }}">
+                                <div class="flex justify-between items-start mb-2">
+                                    <h4 class="font-black text-slate-800 text-sm">{{ mapping.product_type }}</h4>
+                                    <div class="flex gap-2">
+                                        <button onclick="editMapping('{{ mapping.id }}', '{{ mapping.product_type }}', '{{ mapping.format_sequence }}')" 
+                                                class="p-1.5 text-slate-400 hover:text-emerald-600 hover:bg-emerald-50 rounded-lg">
+                                            <i data-lucide="pencil" class="w-4 h-4"></i>
+                                        </button>
+                                        <form method="POST" onsubmit="return confirm('Delete?')" class="inline">
+                                            {% csrf_token %}
+                                            <input type="hidden" name="action" value="delete_mapping">
+                                            <input type="hidden" name="id" value="{{ mapping.id }}">
+                                            <button class="p-1.5 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded-lg"><i data-lucide="trash-2" class="w-4 h-4"></i></button>
+                                        </form>
+                                    </div>
+                                </div>
+                                <div class="flex flex-wrap gap-1">
+                                    {% for part in mapping.get_sequence_list %}
+                                    <span class="text-[10px] bg-white border border-slate-200 px-2 py-1 rounded text-slate-500 font-mono">{{ part }}</span>
+                                    {% endfor %}
+                                </div>
+                            </div>
+                            {% endfor %}
+                        </div>
+
+                        <div id="pagination-controls" class="flex items-center justify-between pt-4 border-t border-slate-100">
+                            <span id="page-info" class="text-xs text-slate-500 font-medium">Page 1 of 1</span>
+                            <div class="flex gap-2">
+                                <button onclick="prevPage()" id="prevBtn" class="p-2 border rounded-lg hover:bg-slate-50 disabled:opacity-30 disabled:cursor-not-allowed"><i data-lucide="chevron-left" class="w-4 h-4"></i></button>
+                                <button onclick="nextPage()" id="nextBtn" class="p-2 border rounded-lg hover:bg-slate-50 disabled:opacity-30 disabled:cursor-not-allowed"><i data-lucide="chevron-right" class="w-4 h-4"></i></button>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <script>
+        lucide.createIcons();
+        
+        // --- PAGINATION LOGIC ---
+        let currentPage = 1;
+        const itemsPerPage = 5;
+        let filteredCards = [];
+
+        function updatePagination() {
+            const allCards = Array.from(document.querySelectorAll('.mapping-card'));
+            // Only paginate cards that are NOT hidden by search
+            filteredCards = allCards.filter(card => card.getAttribute('data-search-hidden') !== 'true');
+            
+            const totalPages = Math.ceil(filteredCards.length / itemsPerPage) || 1;
+            if (currentPage > totalPages) currentPage = totalPages;
+
+            const start = (currentPage - 1) * itemsPerPage;
+            const end = start + itemsPerPage;
+
+            filteredCards.forEach((card, index) => {
+                if (index >= start && index < end) {
+                    card.classList.remove('hidden');
+                } else {
+                    card.classList.add('hidden');
+                }
+            });
+
+            document.getElementById('page-info').innerText = `Page ${currentPage} of ${totalPages}`;
+            document.getElementById('prevBtn').disabled = currentPage === 1;
+            document.getElementById('nextBtn').disabled = currentPage === totalPages;
+        }
+
+        function nextPage() { currentPage++; updatePagination(); }
+        function prevPage() { currentPage--; updatePagination(); }
+
+        // --- LIVE SEARCH LOGIC (Updated for Pagination) ---
+        document.getElementById('mappingSearch').addEventListener('input', function(e) {
+            const term = e.target.value.toLowerCase();
+            const cards = document.querySelectorAll('.mapping-card');
+            
+            cards.forEach(card => {
+                const title = card.dataset.type;
+                if (title.includes(term)) {
+                    card.setAttribute('data-search-hidden', 'false');
+                } else {
+                    card.setAttribute('data-search-hidden', 'true');
+                    card.classList.add('hidden');
+                }
+            });
+            currentPage = 1; // Reset to page 1 on search
+            updatePagination();
+        });
+
+        // Initialize pagination on load
+        window.addEventListener('DOMContentLoaded', updatePagination);
+
+        // --- EXISTING DRAG & DROP / EDIT LOGIC ---
+        const dropZone = document.getElementById('drop-zone');
+        const hiddenInput = document.getElementById('sequence-input');
+
+        function addAttribute(name) {
+            if ([...dropZone.querySelectorAll('.sort-item')].some(el => el.dataset.name === name)) return;
+            const item = document.createElement('div');
+            item.className = 'sort-item flex items-center gap-2 bg-emerald-600 text-white px-3 py-1 rounded-md text-xs cursor-move shadow-sm';
+            item.draggable = true;
+            item.dataset.name = name;
+            item.innerHTML = `<span>${name}</span><button type="button" onclick="this.parentElement.remove(); updateSequence();" class="hover:text-red-200"><i data-lucide="x" class="w-3 h-3"></i></button>`;
+            dropZone.appendChild(item);
+            lucide.createIcons();
+            addDragEvents(item);
+            updateSequence();
+        }
+
+        function updateSequence() {
+            const items = [...dropZone.querySelectorAll('.sort-item')];
+            hiddenInput.value = items.map(item => item.dataset.name).join(',');
+        }
+
+        function editMapping(id, name, sequence) {
+            const form = document.getElementById('mapping-form');
+            document.getElementById('cancel-edit').classList.remove('hidden');
+            form.querySelector('input[name="action"]').value = 'update_mapping';
+            document.getElementById('edit_id').value = id;
+            document.getElementById('pt_name').value = name;
+            
+            const submitBtn = document.getElementById('submit-btn');
+            submitBtn.innerText = "Update Mapping";
+            submitBtn.classList.replace('bg-emerald-600', 'bg-orange-500');
+            
+            dropZone.innerHTML = ''; 
+            if (sequence && sequence !== 'None') {
+                sequence.split(',').forEach(part => {
+                    if(part.trim()) addAttribute(part.trim());
+                });
+            }
+            updateSequence();
+            form.scrollIntoView({ behavior: 'smooth' });
+        }
+
+        function resetForm() {
+            const form = document.getElementById('mapping-form');
+            form.querySelector('input[name="action"]').value = 'add_mapping';
+            document.getElementById('edit_id').value = '';
+            document.getElementById('pt_name').value = '';
+            dropZone.innerHTML = '';
+            updateSequence();
+            document.getElementById('cancel-edit').classList.add('hidden');
+            const submitBtn = document.getElementById('submit-btn');
+            submitBtn.innerText = "Create New Mapping";
+            submitBtn.classList.replace('bg-orange-500', 'bg-emerald-600');
+        }
+
+        // Add standard drag events here (same as your previous script)
+        let draggedItem = null;
+        function addDragEvents(item) {
+            item.addEventListener('dragstart', () => { draggedItem = item; setTimeout(() => item.classList.add('opacity-50'), 0); });
+            item.addEventListener('dragend', () => { item.classList.remove('opacity-50'); draggedItem = null; updateSequence(); });
+            item.addEventListener('dragover', e => { e.preventDefault(); const afterElement = getDragAfterElement(dropZone, e.clientX); if (afterElement == null) dropZone.appendChild(draggedItem); else dropZone.insertBefore(draggedItem, afterElement); });
+        }
+        function getDragAfterElement(container, x) {
+            const draggableElements = [...container.querySelectorAll('.sort-item:not(.opacity-50)')];
+            return draggableElements.reduce((closest, child) => {
+                const box = child.getBoundingClientRect();
+                const offset = x - box.left - box.width / 2;
+                if (offset < 0 && offset > closest.offset) return { offset: offset, element: child };
+                else return closest;
+            }, { offset: Number.NEGATIVE_INFINITY }).element;
+        }
+
+        // --- ATTRIBUTE PAGINATION ---
+        let currentAttrPage = 1;
+        const attrsPerPage = 8; // Shows 8 attributes per page
+
+        function updateAttrPagination() {
+            const allAttrs = Array.from(document.querySelectorAll('.attr-card'));
+            const totalPages = Math.ceil(allAttrs.length / attrsPerPage) || 1;
+            
+            if (currentAttrPage > totalPages) currentAttrPage = totalPages;
+
+            const start = (currentAttrPage - 1) * attrsPerPage;
+            const end = start + attrsPerPage;
+
+            allAttrs.forEach((attr, index) => {
+                if (index >= start && index < end) {
+                    attr.classList.remove('hidden');
+                } else {
+                    attr.classList.add('hidden');
+                }
+            });
+
+            document.getElementById('attr-page-info').innerText = `Page ${currentAttrPage} of ${totalPages}`;
+            document.getElementById('attrPrevBtn').disabled = currentAttrPage === 1;
+            document.getElementById('attrNextBtn').disabled = currentAttrPage === totalPages;
+        }
+
+        function changeAttrPage(direction) {
+            currentAttrPage += direction;
+            updateAttrPagination();
+        }
+
+        // Update the DOMContentLoaded listener to include the new pagination
+        window.addEventListener('DOMContentLoaded', () => {
+            updatePagination();      // For Product Mappings
+            updateAttrPagination();  // For Attributes
+        });
+    </script>
+ </main>
+  {% include 'footer.html' %}
+
+    </div> 
+
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"
+        integrity="sha256-H2VM7BKda+v2Z4+DRy69uknwxjyDRhszjXFhsL4gD3w=" crossorigin="anonymous"></script>
+    <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Required Plugin(popperjs for Bootstrap 5)-->
+    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
+        integrity="sha256-whL0tQWoY1Ku1iskqPFvmZ+CHsvmRWx/PIoEvIeWh4I=" crossorigin="anonymous"></script>
+    <!--end::Required Plugin(popperjs for Bootstrap 5)--><!--begin::Required Plugin(Bootstrap 5)-->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
+        integrity="sha256-YMa+wAM6QkVyz999odX7lPRxkoYAan8suedu4k2Zur8=" crossorigin="anonymous"></script>
+    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+    <!--end::Required Plugin(Bootstrap 5)--><!--begin::Required Plugin(AdminLTE)-->
+    <script src="{% static './js/adminlte.js' %}"></script>
+    <!--end::Required Plugin(AdminLTE)--><!--begin::OverlayScrollbars Configure-->
+    <script>
+        const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
+        const Default = {
+            scrollbarTheme: "os-theme-light",
+            scrollbarAutoHide: "leave",
+            scrollbarClickScroll: true,
+        };
+        document.addEventListener("DOMContentLoaded", function () {
+            const sidebarWrapper = document.querySelector(SELECTOR_SIDEBAR_WRAPPER);
+            if (
+                sidebarWrapper &&
+                typeof OverlayScrollbarsGlobal?.OverlayScrollbars !== "undefined"
+            ) {
+                OverlayScrollbarsGlobal.OverlayScrollbars(sidebarWrapper, {
+                    scrollbars: {
+                        theme: Default.scrollbarTheme,
+                        autoHide: Default.scrollbarAutoHide,
+                        clickScroll: Default.scrollbarClickScroll,
+                    },
+                });
+            }
+        });
+    </script>
+</body>
+</html>

+ 3 - 0
title_creator_app/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 15 - 0
title_creator_app/urls.py

@@ -0,0 +1,15 @@
+from django.contrib import admin
+from django.urls import path
+from . import views
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns = [
+    # path('', views.login_view, name='login'),
+    # path('login/', views.logout_view, name='logout'),
+    path('upload/', views.title_creator_view, name='title_creator_home'),
+    path('upload-async/', views.title_creator_async_view, name='title_creator_async'), # New
+    path('check-status/<str:task_id>/', views.check_status, name='check_status'), # Status checker
+    path('master/', views.master_config_view, name='title_creator_master'),
+    path('api/save-config/', views.save_config_api, name='save_config_api')
+]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

+ 425 - 0
title_creator_app/views.py

@@ -0,0 +1,425 @@
+import os
+import json
+import time
+import requests
+import uuid
+import threading
+import pandas as pd
+from bs4 import BeautifulSoup
+from django.shortcuts import get_object_or_404, redirect, render
+from django.core.files.storage import FileSystemStorage
+from django.http import JsonResponse
+from .models import TitleMapping, AttributeMaster,ProcessingTask # <--- THIS FIXES THE ERROR
+from django.conf import settings
+import cloudscraper
+from django.contrib import messages
+from django.contrib.auth import authenticate, login, logout
+# from django.contrib.auth.decorators import login_required
+from .decorators import login_required
+from django.contrib.auth.hashers import make_password
+
+
+# To login
+def login_view(request):
+    if request.method == "POST":
+        email = request.POST.get("username")
+        password = request.POST.get("password")
+        print("Email: ", email)
+        print("Password: ", password)
+
+        # Authenticate the user
+        user = authenticate(request, username=email, password=password)
+        print("user",user)
+        
+        if user is not None:
+            print("User authenticated successfully.")
+            login(request, user)
+            request.session['user_email'] = user.email
+
+            # request.session = user
+            # request.session['full_name'] = f"{user.firstName} {user.lastName or ''}".strip()
+            # # Store both human-readable role and code
+            # request.session['role'] = user.get_role_display()   # 'Super Admin', 'Admin', 'RTA'
+            # request.session['role_code'] = user.role            # '0', '1', '2'
+            # request.session['joining_date'] = user.createdDate.strftime("%b, %Y")
+            # request.session['userId'] = user.userId
+
+            # 📌 Store client_id if user has a client associated
+            # request.session['client_id'] = user.client.clientId if user.client else None
+
+            return redirect('title_creator_home')
+        else:
+            print("Invalid credentials.")
+            messages.error(request, "Invalid email or password.")
+            return redirect('login')
+    print("Rendering login page.")
+    return render(request, 'login.html')
+
+
+# To logout
+@login_required
+def logout_view(request):
+    logout(request)
+    messages.success(request, "You have been logged out successfully.")
+    return redirect('login')
+
+@login_required
+def master_config_view(request):
+    if request.method == 'POST':
+        action = request.POST.get('action')
+        
+        # Part 1: Add New Attribute
+        if action == 'add_attribute':
+            name = request.POST.get('attr_name')
+            is_m = request.POST.get('is_mandatory') == 'on'
+            if name:
+                AttributeMaster.objects.get_or_create(name=name.strip(), defaults={'is_mandatory': is_m})
+        
+        # Part 2: Add New Title Mapping (Product Type)
+        # --- MAPPING ACTIONS (CREATE & UPDATE) ---
+        elif action in ['add_mapping', 'update_mapping']:
+            pt = request.POST.get('pt_name')
+            seq = request.POST.get('sequence')
+            edit_id = request.POST.get('edit_id')
+
+            if action == 'update_mapping' and edit_id:
+                # Update existing
+                mapping = get_object_or_404(TitleMapping, id=edit_id)
+                mapping.product_type = pt.strip()
+                mapping.format_sequence = seq
+                mapping.save()
+            else:
+                # Create new (using get_or_create to prevent exact duplicates)
+                if pt:
+                    TitleMapping.objects.get_or_create(
+                        product_type=pt.strip(), 
+                        defaults={'format_sequence': seq}
+                    )
+
+        # --- MAPPING DELETE ---
+        elif action == 'delete_mapping':
+            mapping_id = request.POST.get('id')
+            TitleMapping.objects.filter(id=mapping_id).delete()
+        
+        # Part 3: Delete functionality
+        elif action == 'delete_attribute':
+            AttributeMaster.objects.filter(id=request.POST.get('id')).delete()
+        
+        return redirect('title_creator_master')
+
+    # GET: Load all data
+    context = {
+        'attributes': AttributeMaster.objects.all().order_by('name'),
+        'mappings': TitleMapping.objects.all().order_by('product_type'),
+    }
+    return render(request, 'title_creator_master.html', context)
+
+def save_config_api(request):
+    if request.method == 'POST':
+        try:
+            data = json.loads(request.body)
+            
+            # Update Mandatory Attributes
+            # Expected data: { "mandatory_ids": [1, 3, 5] }
+            AttributeMaster.objects.all().update(is_mandatory=False)
+            AttributeMaster.objects.filter(id__in=data.get('mandatory_ids', [])).update(is_mandatory=True)
+            
+            # Update Title Sequences
+            # Expected data: { "mappings": [{"id": 1, "sequence": "Brand,Color"}] }
+            for m in data.get('mappings', []):
+                TitleMapping.objects.filter(id=m['id']).update(format_sequence=m['sequence'])
+                
+            return JsonResponse({'success': True})
+        except Exception as e:
+            return JsonResponse({'success': False, 'error': str(e)})
+
+def extract_title_or_error(product,selected_pt):
+    # 1. Identify Product Type from JSON to fetch the correct Mapping
+    pt_name = selected_pt
+    try:
+        mapping = TitleMapping.objects.get(product_type=pt_name)
+        config_sequence = mapping.get_sequence_list()
+    except TitleMapping.DoesNotExist:
+        return f"No Title Configuration found for Product Type: {pt_name}"
+
+    # 2. Get Mandatory list from DB
+    mandatory_fields = list(AttributeMaster.objects.filter(is_mandatory=True).values_list('name', flat=True))
+
+    # 3. Data Extraction (Your logic)
+    extracted_data = {
+        "Brand": product.get("brand"),
+        "Product Type": pt_name
+    }
+    dimensions = {}
+
+    for group in product.get("attributeGroups", []):
+        for attr in group.get("attributes", []):
+            desc = attr.get("attributeDesc")
+            value = attr.get("attributeValue")
+            
+            if desc in ["Door Type", "Capacity", "Color"]:
+                extracted_data[desc] = value
+            elif desc in ["Width", "Depth", "Height"]:
+                dimensions[desc] = value
+
+    if {"Width", "Depth", "Height"}.issubset(dimensions):
+        extracted_data["Dimensions"] = f'{dimensions["Width"]} x {dimensions["Depth"]} x {dimensions["Height"]}'
+
+    # 4. Build Title and Check Mandatory Rules from DB
+    final_title_parts = []
+    missing_mandatory = []
+
+    for attr_name in config_sequence:
+        val = extracted_data.get(attr_name)
+        
+        if not val or str(val).strip() == "":
+            # If DB says it's mandatory, track the error
+            if attr_name in mandatory_fields:
+                missing_mandatory.append(attr_name)
+            continue 
+            
+        final_title_parts.append(str(val))
+
+    # 5. Result
+    if missing_mandatory:
+        return f"Could not found {', '.join(missing_mandatory)} on Product Details page"
+    
+    return " ".join(final_title_parts)
+
+def construct_dynamic_title(raw_data,selected_pt):
+    try:
+        product = raw_data.get("props", {}).get("pageProps", {}).get("product", {})
+        if not product: return "Product data not found"
+        
+        return extract_title_or_error(product,selected_pt).strip()
+    except Exception:
+        return "Could not found attribute name on product details page"
+
+@login_required
+def title_creator_view(request):
+    if request.method == 'POST' and request.FILES.get('file'):
+        excel_file = request.FILES['file']
+        selected_pt = request.POST.get('product_type')
+        fs = FileSystemStorage()
+        filename = fs.save(excel_file.name, excel_file)
+        file_path = fs.path(filename)
+
+        try:
+            # 1. Read Excel
+            df = pd.read_excel(file_path)
+            
+            # 2. Add the NEW COLUMN if it doesn't exist
+            if 'New_Generated_Title' not in df.columns:
+                df['New_Generated_Title'] = ""
+
+            headers = {"User-Agent": "Mozilla/5.0"}
+            results_for_ui = []
+
+            # 3. Process each row
+            for index, row in df.iterrows():
+                url = row.get('URL') # Assumes your excel has a 'URL' column
+                new_title = ""
+
+                if pd.notna(url):
+                    try:
+                        resp = requests.get(url, headers=headers, timeout=10)
+                        soup = BeautifulSoup(resp.content, 'html.parser')
+                        script_tag = soup.find('script', id='__NEXT_DATA__')
+                        
+                        if script_tag:
+                            raw_data = json.loads(script_tag.string)
+                            new_title = construct_dynamic_title(raw_data,selected_pt)
+                        else:
+                            new_title = "Could not found attribute name on product details page"
+                    except:
+                        new_title = "Could not found attribute name on product details page"
+                else:
+                    new_title = "URL Missing"
+
+                # Update the DataFrame column for this row
+                df.at[index, 'New_Generated_Title'] = new_title
+                
+                results_for_ui.append({
+                    "id" : index + 1,
+                    "url": url,
+                    "new_title": new_title,
+                    "status": True
+                })
+                time.sleep(1) # Safety delay
+
+            # 4. Save the modified Excel to a new path
+            output_filename = f"processed_{excel_file.name}"
+            output_path = os.path.join(fs.location, output_filename)
+            df.to_excel(output_path, index=False)
+
+            return JsonResponse({
+                'success': True,
+                'results': results_for_ui,
+                'download_url': fs.url(output_filename)
+            })
+
+        finally:
+            if os.path.exists(file_path): os.remove(file_path)
+
+    # GET request: Fetch all product types for the dropdown
+    product_types = TitleMapping.objects.all().values_list('product_type', flat=True)
+    return render(request, 'title_creator_index.html', {'product_types': product_types})
+    # return render(request, 'title_creator_index.html')
+
+# def process_excel_task(file_path, selected_pt, task_id):
+#     task = ProcessingTask.objects.get(task_id=task_id)
+#     try:
+#         df = pd.read_excel(file_path)
+#         if 'New_Generated_Title' not in df.columns:
+#             df['New_Generated_Title'] = ""
+
+#         headers = {"User-Agent": "Mozilla/5.0"}
+
+#         for index, row in df.iterrows():
+#             url = row.get('URL')
+#             # ... [Insert your existing BeautifulSoup/Scraping Logic Here] ...
+#             # Example:
+#             # new_title = construct_dynamic_title(raw_data, selected_pt)
+#             # df.at[index, 'New_Generated_Title'] = new_title
+#             time.sleep(1) 
+
+#         # Save Final File
+#         output_filename = f"completed_{task.original_filename}"
+#         from django.conf import settings
+#         output_path = os.path.join(settings.MEDIA_ROOT, output_filename)
+#         df.to_excel(output_path, index=False)
+
+#         # Update Task Status
+#         task.status = 'COMPLETED'
+#         task.download_url = f"{settings.MEDIA_URL}{output_filename}"
+#         task.save()
+
+#     except Exception as e:
+#         task.status = 'FAILED'
+#         task.save()
+#         print(f"Error: {e}")
+#     finally:
+#         if os.path.exists(file_path):
+#             os.remove(file_path)
+
+def process_excel_task(file_path, selected_pt, task_id):
+    # Retrieve the task record from the database
+    scraper = cloudscraper.create_scraper() # This replaces requests.get
+    task = ProcessingTask.objects.get(task_id=task_id)
+    
+    try:
+        # 1. Read Excel
+        df = pd.read_excel(file_path)
+        
+        # 2. Add the NEW COLUMN if it doesn't exist
+        if 'New_Generated_Title' not in df.columns:
+            df['New_Generated_Title'] = ""
+
+        headers = {"User-Agent": "Mozilla/5.0"}
+
+        # 3. Process each row
+        for index, row in df.iterrows():
+            url = row.get('URL')
+            new_title = ""
+
+            if pd.notna(url):
+                try:
+                    # Scraping logic
+                    # resp = scraper.get(url, timeout=15)
+                    resp = requests.get(url, headers=headers, timeout=10)
+                    if resp.status_code == 200:
+                        soup = BeautifulSoup(resp.content, 'html.parser')
+                        script_tag = soup.find('script', id='__NEXT_DATA__')
+                        
+                        if script_tag:
+                            try:
+                                raw_data = json.loads(script_tag.string)
+                                # Calling your dynamic title helper
+                                new_title = construct_dynamic_title(raw_data, selected_pt)
+                            except Exception:
+                                new_title = "Data Parsing Error"
+                        else:
+                            new_title = "Could not found attribute name on product details page"
+                    else:
+                        new_title = f"HTTP Error: {resp.status_code}"
+                except Exception:
+                    new_title = "Request Failed (Timeout/Connection)"
+            else:
+                new_title = "URL Missing"
+
+            # Update the DataFrame
+            df.at[index, 'New_Generated_Title'] = new_title
+            
+            # Optional: Sleep to prevent getting blocked by the server
+            time.sleep(1)
+
+        # 4. Save the modified Excel to the MEDIA folder
+        output_filename = f"completed_{task_id}_{task.original_filename}"
+        
+        # Ensure media directory exists
+        if not os.path.exists(settings.MEDIA_ROOT):
+            os.makedirs(settings.MEDIA_ROOT)
+            
+        output_path = os.path.join(settings.MEDIA_ROOT, output_filename)
+        df.to_excel(output_path, index=False)
+
+        # 5. Final Status Update
+        task.status = 'COMPLETED'
+        # Construct the URL for the frontend to download
+        task.download_url = f"{settings.MEDIA_URL}{output_filename}"
+        task.save()
+
+    except Exception as e:
+        print(f"Critical Task Failure: {e}")
+        task.status = 'FAILED'
+        task.save()
+        
+    finally:
+        # 6. Cleanup the temporary uploaded file
+        if os.path.exists(file_path):
+            os.remove(file_path)
+
+@login_required
+def title_creator_async_view(request):
+    if request.method == 'POST' and request.FILES.get('file'):
+        excel_file = request.FILES['file']
+        selected_pt = request.POST.get('product_type')
+        
+        # 1. Save file temporarily
+        fs = FileSystemStorage()
+        filename = fs.save(f"temp_{uuid.uuid4().hex}_{excel_file.name}", excel_file)
+        file_path = fs.path(filename)
+
+        # 2. Create Task Record
+        task_id = str(uuid.uuid4())
+        ProcessingTask.objects.create(
+            task_id=task_id,
+            original_filename=excel_file.name,
+            status='PENDING'
+        )
+
+        # 3. Start Background Thread
+        thread = threading.Thread(
+            target=process_excel_task, 
+            args=(file_path, selected_pt, task_id)
+        )
+        thread.start()
+
+        return JsonResponse({
+            'status': 'started',
+            'task_id': task_id,
+            'message': 'File is processing in the background.'
+        })
+    
+    return JsonResponse({'error': 'Invalid request'}, status=400)
+
+# 2. This view is called repeatedly by pollStatus() in your JS
+def check_status(request, task_id):
+    # Look up the task in the database
+    task = get_object_or_404(ProcessingTask, task_id=task_id)
+    
+    return JsonResponse({
+        'status': task.status,               # 'PENDING', 'COMPLETED', or 'FAILED'
+        'file_name': task.original_filename,
+        'download_url': task.download_url    # This will be null until status is COMPLETED
+    })

Деякі файли не було показано, через те що забагато файлів було змінено