I want you to act as a UX/UI developer. I will provide some details and codes about the design of my web app, website or other digital product, and it will be your job to come up with creative ways to improve its UI and user experience. 

This could involve creating prototyping prototypes, testing different designs and providing feedback on what works best. 

My first request is " Design A modern and sleek UI using only black and white colours. "

My code: <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Light Novel Library</title>

  <!-- Preconnect and Load Fonts Efficiently -->
  <link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">

  <!-- External Libraries -->
  <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/dexie@3.2.5/dist/dexie.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.js"></script>
  <link href="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.css" rel="stylesheet">

  <!-- Stylesheet -->
  <style>
    /* Root Variables */
    :root {
      --primary-color: #1e3a8a;
      --secondary-color: #ffffff;
      --background-color: #f0f4f8;
      --card-background: #ffffff;
      --text-primary: #1f2937;
      --text-secondary: #6b7280;
      --shadow-sm: 0 1px 3px rgba(0,0,0,0.12);
      --shadow-md: 0 4px 6px rgba(0,0,0,0.1);
      --shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
      --radius-sm: 0.375rem;
      --radius-md: 0.5rem;
      --radius-lg: 1rem;
      --transition-duration: 0.3s;
      --max-width: 1280px;
      --header-height: 80px; /* Define header height for layout adjustments */
    }

    /* Reset and Base Styles */
    *, ::before, ::after {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      font-family: 'Inter', sans-serif;
      background-color: var(--background-color);
      color: var(--text-primary);
      line-height: 1.6;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
    }

    /* Main Content Styles */
    .main {
      flex: 1;
      max-width: var(--max-width);
      margin: 2rem auto;
      padding: 0 1rem;
    }

    /* Search and Refresh Styles */
    .search-container {
      margin-bottom: 2rem;
      display: flex;
      justify-content: space-between;
      align-items: center;
      flex-wrap: wrap;
    }

    .search-input {
      flex: 1;
      min-width: 250px;
      padding: 0.75rem 1.5rem;
      font-size: 1rem;
      border: 2px solid var(--primary-color);
      border-radius: var(--radius-md);
      outline: none;
      transition: border-color var(--transition-duration) ease;
      background-color: var(--secondary-color);
      color: var(--text-primary);
    }

    .search-input:focus {
      border-color: var(--text-secondary);
    }

    .refresh-button {
      padding: 0.75rem 1.5rem;
      font-size: 1rem;
      border: none;
      border-radius: var(--radius-md);
      background-color: var(--primary-color);
      color: var(--secondary-color);
      cursor: pointer;
      transition: background-color var(--transition-duration) ease;
      margin-left: 1rem;
      margin-top: 1rem;
    }

    .refresh-button:hover {
      background-color: var(--text-secondary);
    }

    /* Grid Styles */
    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
      gap: 2rem;
    }

    /* Card Styles */
    .card {
      background: var(--card-background);
      border-radius: var(--radius-md);
      overflow: hidden;
      box-shadow: var(--shadow-sm);
      transition: transform var(--transition-duration) ease, box-shadow var(--transition-duration) ease;
      cursor: pointer;
      display: flex;
      flex-direction: column;
      height: 100%;
      opacity: 0; /* For AOS animation */
      animation: fadeInCard 0.5s forwards;
    }

    .card:hover {
      transform: translateY(-5px);
      box-shadow: var(--shadow-lg);
    }

    @keyframes fadeInCard {
      to { opacity: 1; }
    }

    .card__image-container {
      position: relative;
      width: 100%;
      padding-top: 140%; /* 5:7 aspect ratio */
      background: #e0e0e0;
      overflow: hidden;
    }

    .card__image {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
      /* Lazy loading for better performance */
      loading: lazy;
    }

    .card__content {
      padding: 1rem;
      flex-grow: 1;
      display: flex;
      flex-direction: column;
      justify-content: center;
    }

    .card__title {
      font-size: 1.1rem;
      font-weight: 600;
      color: var(--text-primary);
      text-align: center;
      overflow: hidden;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-line-clamp: 3;
      -webkit-box-orient: vertical;
    }

    /* Loader Styles */
    .loader {
      display: none; /* Initially hidden */
      justify-content: center;
      align-items: center;
      height: 200px;
      color: var(--text-secondary);
    }

    .loader.visible {
      display: flex; /* Show when visible */
    }

    .loader__spinner {
      width: 50px;
      height: 50px;
      border: 5px solid #f3f3f3;
      border-top: 5px solid var(--primary-color);
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

    /* Error Message Styles */
    .error-message {
      text-align: center;
      color: #ff0000;
      padding: 1rem;
      background: #ffe6e6;
      border-radius: var(--radius-sm);
      margin: 1rem 0;
      opacity: 1;
      transition: opacity 0.5s ease-out;
    }

    .error-message.fade-out {
      opacity: 0;
    }

    /* Modal Styles */
    .modal {
      display: none;
      position: fixed;
      z-index: 2000;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      overflow: auto;
      background-color: rgba(0,0,0,0.7);
      justify-content: center;
      align-items: center;
      padding: 1rem;
    }

    .modal-content {
      background-color: var(--secondary-color);
      padding: 2rem;
      border-radius: var(--radius-lg);
      max-width: 700px;
      width: 100%;
      position: relative;
      box-shadow: var(--shadow-lg);
      animation: fadeInModal 0.3s ease;
    }

    @keyframes fadeInModal {
      from { opacity: 0; transform: translateY(-20px); }
      to { opacity: 1; transform: translateY(0); }
    }

    .close {
      position: absolute;
      top: 1rem;
      right: 1.5rem;
      color: var(--text-secondary);
      font-size: 2rem;
      font-weight: bold;
      cursor: pointer;
      transition: color var(--transition-duration) ease;
      background: none;
      border: none;
      font-family: inherit;
    }

    .close:hover,
    .close:focus {
      color: var(--text-primary);
    }

    .modal__title {
      font-size: 1.75rem;
      font-weight: 700;
      margin-bottom: 1rem;
      color: var(--text-primary);
    }

    .modal__link {
      display: inline-block;
      padding: 0.75rem 1.5rem;
      background-color: var(--primary-color);
      color: var(--secondary-color);
      text-decoration: none;
      border-radius: var(--radius-md);
      font-weight: 600;
      transition: background-color var(--transition-duration) ease;
    }

    .modal__link:hover {
      background-color: var(--text-secondary);
    }

    /* Sentinel Element for Infinite Scroll */
    .sentinel {
      height: 1px;
    }

    /* Animations */
    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }

    /* Responsive Styles */
    @media (max-width: 768px) {
      .grid {
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
        gap: 1.5rem;
      }

      .card__title {
        font-size: 1rem;
      }
    }

    @media (max-width: 480px) {
      .grid {
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
        gap: 1rem;
      }
    }

    /* Progress Bar Styles */
    .progress-container {
      width: 100%;
      background-color: #f3f3f3;
      border-radius: var(--radius-md);
      margin: 1rem 0;
      display: none; /* Initially hidden */
    }

    .progress-bar {
      height: 10px;
      width: 0%;
      background-color: var(--primary-color);
      border-radius: var(--radius-md);
      transition: width 0.3s ease;
    }

    .progress-text {
      text-align: center;
      margin-top: 0.5rem;
      font-size: 0.9rem;
      color: var(--text-primary);
      display: none; /* Initially hidden */
    }
  </style>
</head>
<body>
  <!-- Main Content -->
  <main class="main" role="main">
    <!-- Search Bar and Refresh Button -->
    <div class="search-container">
      <input type="text" id="search-input" class="search-input" placeholder="Search light novels..." aria-label="Search light novels">
      <button id="refresh-button" class="refresh-button" aria-label="Refresh Data">Refresh Data</button>
    </div>

    <!-- Content Grid -->
    <div id="content" class="grid" aria-live="polite">
      <!-- Initial Loader -->
      <div class="loader" id="loader" role="status" aria-label="Loading novels">
        <div class="loader__spinner"></div>
      </div>
    </div>

    <!-- Sentinel Element for Infinite Scroll -->
    <div class="sentinel" id="sentinel"></div>

    <!-- Progress Bar -->
    <div class="progress-container" id="progress-container">
      <div class="progress-bar" id="progress-bar"></div>
    </div>
    <div class="progress-text" id="progress-text">0%</div>
  </main>

  <!-- Modal -->
  <div id="modal" class="modal" aria-hidden="true" role="dialog" aria-labelledby="modal-title">
    <div class="modal-content">
      <button class="close" aria-label="Close modal">&times;</button>
      <h2 id="modal-title" class="modal__title"></h2>
      <a id="modal-link" class="modal__link" href="#" target="_blank" rel="noopener noreferrer">Read PDF</a>
    </div>
  </div>

  <!-- JavaScript -->
  <script>
    'use strict';
    (function($) {
      /**
       * Initialize Dexie Database with Unique Constraints on name, coverUrl, and pdfUrl
       */
      const db = new Dexie('MonochromeNovelsDB');
      db.version(30).stores({
        novels: '++id, &name, &coverUrl, &pdfUrl, timestamp' // Unique indexes on name, coverUrl, pdfUrl
      });

      // Constants
      const PAGE_SIZE = 20; // Number of novels to load per page
      let currentPage = 0; // Current page index
      let isLoading = false; // Flag to prevent multiple simultaneous loads
      let hasMore = true; // Flag to determine if more novels are available
      let currentSearchTerm = ''; // Current search term
      let totalNovels = 0; // Total number of novels to be fetched
      let loadedNovels = 0; // Number of novels loaded so far

      const PLACEHOLDER_IMAGE = 'https://via.placeholder.com/500x700?text=No+Image'; // Placeholder for missing images

      /**
       * Debounce Function to Limit Function Execution Rate
       * @param {Function} func
       * @param {number} delay
       * @returns {Function}
       */
      const debounce = (func, delay) => {
        let debounceTimer;
        return function(...args) {
          const context = this;
          clearTimeout(debounceTimer);
          debounceTimer = setTimeout(() => func.apply(context, args), delay);
        }
      };

      /**
       * Utility: Delay Function
       * @param {number} ms
       * @returns {Promise}
       */
      const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

      /**
       * Utility: Retry Operation with Exponential Backoff (3 Attempts)
       * @param {Function} operation
       * @param {number} attempts
       * @param {number} delayDuration
       * @returns {Promise}
       */
      const retryOperation = async (operation, attempts = 3, delayDuration = 1500) => {
        for (let i = 0; i < attempts; i++) {
          try {
            return await operation();
          } catch (error) {
            if (i === attempts - 1) throw error;
            await delay(delayDuration * Math.pow(2, i)); // Exponential backoff
          }
        }
      };

      /**
       * Initialize Intersection Observer for Infinite Scroll using Sentinel Element
       */
      const setupSentinelObserver = () => {
        const sentinel = $('#sentinel').get(0);
        if (!('IntersectionObserver' in window)) {
          // Fallback if IntersectionObserver is not supported
          $(window).on('scroll', () => {
            if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100 && hasMore && !isLoading) {
              loadNextPage();
            }
          });
          return;
        }

        const sentinelObserver = new IntersectionObserver((entries) => {
          entries.forEach(entry => {
            if (entry.isIntersecting && hasMore && !isLoading) {
              loadNextPage();
            }
          });
        }, {
          rootMargin: '100px',
          threshold: 0
        });
        sentinelObserver.observe(sentinel);
      };

      /**
       * Load Next Page of Novels from IndexedDB
       */
      const loadNextPage = async () => {
        if (isLoading) return; // Prevent multiple simultaneous loads
        isLoading = true;
        showLoader();

        try {
          let novels;

          if (currentSearchTerm === '') {
            novels = await db.novels
              .orderBy('name') // Order by name for alphabetical display
              .offset(currentPage * PAGE_SIZE)
              .limit(PAGE_SIZE)
              .toArray();
          } else {
            novels = await db.novels
              .where('name')
              .startsWithIgnoreCase(currentSearchTerm)
              .offset(currentPage * PAGE_SIZE)
              .limit(PAGE_SIZE)
              .toArray();
          }

          if (novels.length < PAGE_SIZE) {
            hasMore = false; // No more novels to load
          }

          if (novels.length > 0) {
            appendNovelsToDisplay(novels);
            currentPage++;
          } else {
            if (currentPage === 0) {
              showTemporaryError('No novels found for the given search term.', 5000);
            }
          }
        } catch (error) {
          console.error('Error loading novels:', error);
          showTemporaryError('An error occurred while loading novels.', 5000);
        } finally {
          hideLoader();
          isLoading = false;
        }
      };

      /**
       * Show Loader
       */
      const showLoader = () => {
        $('#loader').addClass('visible');
      };

      /**
       * Hide Loader
       */
      const hideLoader = () => {
        $('#loader').removeClass('visible');
      };

      /**
       * Display Error Message Temporarily
       * @param {string} message
       * @param {number} duration - Duration in milliseconds
       */
      const showTemporaryError = (message, duration = 5000) => {
        const $content = $('#content');
        // Remove existing error messages
        $content.find('.error-message').remove();

        const $errorDiv = $('<div>', {
          class: 'error-message',
          role: 'alert',
          text: message
        });

        $content.prepend($errorDiv);

        // Fade out after specified duration
        setTimeout(() => {
          $errorDiv.addClass('fade-out');
          // Remove from DOM after fade-out transition
          setTimeout(() => {
            $errorDiv.remove();
          }, 500);
        }, duration);
      };

      /**
       * Append Novels to Display
       * @param {Array} novels
       */
      const appendNovelsToDisplay = novels => {
        const $content = $('#content');
        const $fragment = $(document.createDocumentFragment());

        novels.forEach(novel => {
          const $card = createNovelCard(novel);
          $fragment.append($card);
        });

        $content.append($fragment);
        AOS.refresh(); // Refresh AOS to apply animations to new elements
      };

      /**
       * Create Novel Card Element using jQuery
       * @param {Object} novel
       * @returns {jQuery}
       */
      const createNovelCard = (novel) => {
        const $card = $('<div>', {
          class: 'card',
          tabindex: '0',
          role: 'button',
          'aria-labelledby': `novel-title-${encodeURIComponent(novel.name)}`,
          'data-aos': 'fade-up'
        });

        const $imageContainer = $('<div>', { class: 'card__image-container' });
        const $image = $('<img>', {
          class: 'card__image',
          src: novel.coverUrl || PLACEHOLDER_IMAGE,
          alt: `Cover of ${novel.name}`,
          loading: 'lazy'
        }).on('error', function() {
          $(this).attr('src', PLACEHOLDER_IMAGE);
        });

        $imageContainer.append($image);
        $card.append($imageContainer);

        const $content = $('<div>', { class: 'card__content' });
        const $title = $('<h2>', {
          id: `novel-title-${encodeURIComponent(novel.name)}`,
          class: 'card__title',
          html: novel.name
        });

        $content.append($title);
        $card.append($content);

        // Event Listener to Open Modal
        $card.on('click keypress', (e) => {
          if (e.type === 'click' || (e.type === 'keypress' && (e.key === 'Enter' || e.key === ' '))) {
            e.preventDefault();
            showModal(novel);
          }
        });

        return $card;
      };

      /**
       * Utility: Escape HTML to Prevent XSS
       * @param {string} str
       * @returns {string}
       */
      const escapeHTML = (str) => {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
      };

      /**
       * Modal Functionality
       */
      const $modal = $('#modal');
      const $modalTitle = $('#modal-title');
      const $modalLink = $('#modal-link');
      const $closeBtn = $('.close');

      /**
       * Show Modal with Novel Details
       * @param {Object} novel
       */
      const showModal = (novel) => {
        $modalTitle.text(novel.name);
        $modalLink.attr('href', novel.pdfUrl);
        $modal.fadeIn(200).attr('aria-hidden', 'false');
        $('body').css('overflow', 'hidden'); // Prevent background scrolling
        $closeBtn.focus(); // Focus on close button for accessibility
      };

      /**
       * Close Modal
       */
      const closeModal = () => {
        $modal.fadeOut(200).attr('aria-hidden', 'true');
        $('body').css('overflow', 'auto');
      };

      $closeBtn.on('click', closeModal);

      $(window).on('click', (event) => {
        if ($(event.target).is($modal)) {
          closeModal();
        }
      });

      $(window).on('keydown', (event) => {
        if (event.key === 'Escape' && $modal.is(':visible')) {
          closeModal();
        }
      });

      /**
       * Search Functionality
       */
      const $searchInput = $('#search-input');

      const handleSearch = debounce(async () => {
        currentSearchTerm = $searchInput.val().trim();
        currentPage = 0;
        hasMore = true;

        const $content = $('#content');
        $content.empty(); // Clear existing content

        // Reload the first page with the new search term
        await loadNextPage();
      }, 300); // 300ms debounce delay

      $searchInput.on('input', handleSearch);

      /**
       * Refresh Data Functionality
       */
      const $refreshButton = $('#refresh-button');
      $refreshButton.on('click', async () => {
        const confirmation = confirm('Are you sure you want to refresh the data? This may take a few moments.');
        if (!confirmation) return;

        isLoading = true;
        showLoader();
        hasMore = true;
        currentPage = 0;

        try {
          // Clear existing data
          await db.novels.clear();

          // Fetch and parse fresh data
          await fetchAndStoreNovels();

          // Reload the first page of novels
          $('#content').empty(); // Clear existing content

          await loadNextPage();
        } catch (error) {
          console.error('Error refreshing data:', error);
          showTemporaryError('Failed to refresh data. Please try again later.', 5000);
        } finally {
          hideLoader();
          isLoading = false;
        }
      });

      /**
       * Initialize Application
       */
      const initializeApp = async () => {
        try {
          // Check if any novels are already stored in IndexedDB
          const count = await db.novels.count();
          if (count === 0) {
            // If not, fetch from the target website
            await fetchAndStoreNovels();
          } else {
            // Validate existing data
            const novels = await db.novels.toArray();
            if (!validateNovels(novels)) {
              await db.novels.clear();
              await fetchAndStoreNovels();
            }
          }

          // Load the first page of novels
          await loadNextPage();
        } catch (error) {
          console.error('Error initializing application:', error);
          showTemporaryError('Failed to load novels. Please try refreshing the page.', 5000);
        }
      };

      /**
       * Fetch and Store Novels
       */
      const fetchAndStoreNovels = async () => {
        try {
          const html = await retryOperation(() => fetchHTML('https://jnovels.com/top-light-novels-to-read/'));
          let novels = parseHTML(html);

          if (novels.length === 0) {
            throw new Error('No novels found on the target page.');
          }

          totalNovels = novels.length;
          loadedNovels = 0;
          showProgress();
          updateProgress(0);

          // Bulk upsert novels to IndexedDB
          await db.novels.bulkPut(novels, {
            allKeys: true,
            process: (novel, add) => {
              add(novel);
              loadedNovels++;
              updateProgress(loadedNovels / totalNovels * 100);
            }
          });

          console.log('Novels fetched and stored successfully:', novels.length);

          // Clear fetched HTML content
          clearFetchedHTML();
        } catch (error) {
          console.error('Error fetching or storing novels:', error);
          showTemporaryError('Failed to fetch and store novels. Please try again later.', 5000);
        } finally {
          hideProgress();
        }
      };

      /**
       * Fetch HTML Content with Content-Type Validation via CORS Proxy
       * @param {string} url
       * @returns {Promise<string>}
       */
      const fetchHTML = async (url) => {
        const proxyUrl = `https://api.codetabs.com/v1/proxy?quest=${encodeURIComponent(url)}`;
        try {
          const response = await fetch(proxyUrl);
          
          if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
          }

          const htmlContent = await response.text();
          
          if (!htmlContent || typeof htmlContent !== 'string') {
            throw new Error('Fetched content is invalid or empty.');
          }

          return htmlContent;
        } catch (error) {
          console.error('Error fetching HTML:', error);
          throw error; // Re-throw to be handled by caller
        }
      };

      /**
       * Parse Fetched HTML to Extract Novel Data Based on Provided Schema
       * @param {string} html
       * @returns {Array<Object>}
       */
      const parseHTML = (html) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        const novels = [];
        const uniqueNovels = new Set();

        // Select all h3 tags as they contain the novel names and links
        const h3Elements = doc.querySelectorAll('h3');

        h3Elements.forEach((h3Element) => {
          try {
            const aTag = h3Element.querySelector('a');
            const spanTag = h3Element.querySelector('span');

            if (!aTag || !spanTag) return; // Skip if either tag is missing

            const name = spanTag.textContent.trim();
            const pdfLink = aTag.getAttribute('href').trim();

            // The cover image is inside the following p tag
            const pElement = h3Element.nextElementSibling;
            if (!pElement) return; // Skip if p tag is missing

            const imgTag = pElement.querySelector('img');
            if (!imgTag) return; // Skip if img tag is missing

            let coverUrl = imgTag.getAttribute('src') || imgTag.getAttribute('data-src');
            if (!coverUrl) return; // Skip if cover URL is missing

            // Handle different URL formats
            if (coverUrl.startsWith('//')) {
              coverUrl = `https:${coverUrl}`;
            } else if (coverUrl.startsWith('/')) {
              coverUrl = `https://jnovels.com${coverUrl}`;
            } else if (!coverUrl.startsWith('http')) {
              // Handle relative URLs without leading '/'
              coverUrl = `https://jnovels.com/${coverUrl}`;
            }

            // Validate extracted data
            if (name && pdfLink && coverUrl) {
              const novelKey = `${name}-${coverUrl}-${pdfLink}`;
              if (!uniqueNovels.has(novelKey)) {
                uniqueNovels.add(novelKey);
                novels.push({
                  name: escapeHTML(name),
                  coverUrl: escapeHTML(coverUrl),
                  pdfUrl: escapeHTML(pdfLink),
                  timestamp: Date.now()
                });
              }
            } else {
              console.warn('Incomplete novel data found and skipped:', { name, pdfLink, coverUrl });
            }
          } catch (error) {
            // Skip any malformed entries
            console.error('Error parsing a novel entry:', error);
          }
        });

        return novels;
      };

      /**
       * Clear Fetched HTML Content
       */
      const clearFetchedHTML = () => {
        // Clear any fetched or cached HTML content
        // This is a placeholder function. You may need to implement specific logic to clear fetched HTML content.
        console.log('Clearing fetched HTML content...');
      };

      /**
       * Clear Cache on Before Unload
       */
      const clearCache = () => {
        caches.keys().then(cacheNames => {
          cacheNames.forEach(cacheName => {
            if (cacheName !== 'MonochromeNovelsDB') {
              caches.delete(cacheName);
            }
          });
        });
      };

      window.addEventListener('beforeunload', clearCache);
      document.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'hidden') {
          clearCache();
        }
      });

      /**
       * Update Progress Bar
       * @param {number} percentage
       */
      const updateProgress = (percentage) => {
        $('#progress-bar').css('width', `${percentage}%`);
        $('#progress-text').text(`${Math.round(percentage)}%`);
      };

      /**
       * Show Progress Bar and Text
       */
      const showProgress = () => {
        $('#progress-container').show();
        $('#progress-text').show();
      };

      /**
       * Hide Progress Bar and Text
       */
      const hideProgress = () => {
        $('#progress-container').hide();
        $('#progress-text').hide();
      };

      /**
       * Validate Novels
       * @param {Array<Object>} novels
       * @returns {boolean}
       */
      const validateNovels = (novels) => {
        for (const novel of novels) {
          if (!novel.name || !novel.coverUrl || !novel.pdfUrl || !novel.timestamp) {
            return false;
          }
        }
        return true;
      };

      /**
       * Initialize AOS and Start the Application once the DOM is fully loaded
       */
      $(document).ready(() => {
        // Initialize AOS
        AOS.init({
          duration: 800,
          easing: 'ease-in-out',
          once: true,
        });

        setupSentinelObserver();
        initializeApp();
      });

    })(jQuery);
  </script>
</body>
</html>

Don't use frameworks like react, angular, etc.

Use only free and light weight resources.

Send the fully working full codes.