evaluate this and explain this  app: <html><head><base href="">

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Comic Strip Creator</title>
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
  <!-- Include Interact.js for draggable dialogues -->
  <script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
  <!-- Include html2canvas for capturing the final image -->
  <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
  <style>
    /* Root Variables for Consistent Theming */
    :root {
      --primary-color: #6B48FF;
      --secondary-color: #FFB648;
      --accent-color: #FF6B6B;
      --background-color: #F0F2F5;
      --card-bg: #FFFFFF;
      --text-primary: #2C3E50;
      --text-secondary: #7F8C8D;
      --success: #2ECC71;
      --error: #E74C3C;
      --shadow: rgba(0, 0, 0, 0.1);
      --border-radius: 8px;
      --transition-speed: 0.3s;
    }

    /* Global Styles */
    body {
      font-family: 'Poppins', sans-serif;
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
      background: var(--background-color);
      color: var(--text-primary);
      line-height: 1.6;
    }

    h1, h2 {
      color: var(--primary-color);
      text-align: center;
      margin-bottom: 20px;
    }

    h1 {
      font-size: 2.5em;
      position: relative;
    }

    h1::after {
      content: '';
      display: block;
      width: 60px;
      height: 4px;
      background: var(--secondary-color);
      margin: 10px auto;
      border-radius: 2px;
    }

    /* Input Section Styling */
    .input-section {
      background: var(--card-bg);
      padding: 30px;
      border-radius: 16px;
      box-shadow: 0 8px 24px var(--shadow);
      margin-bottom: 30px;
      transition: transform var(--transition-speed) ease;
    }

    .input-section:hover {
      transform: translateY(-2px);
    }

    .character-inputs {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      gap: 30px;
      margin-bottom: 20px;
    }

    .input-group {
      margin-bottom: 20px;
      position: relative;
    }

    label {
      display: block;
      margin-bottom: 8px;
      font-weight: 600;
      color: var(--text-primary);
      font-size: 0.95em;
    }

    input, textarea, select {
      width: 100%;
      padding: 12px;
      border: 2px solid #E0E7FF;
      border-radius: var(--border-radius);
      font-family: 'Poppins', sans-serif;
      transition: border-color var(--transition-speed) ease, box-shadow var(--transition-speed) ease;
      font-size: 1em;
    }

    input:focus, textarea:focus, select:focus {
      outline: none;
      border-color: var(--primary-color);
      box-shadow: 0 0 0 3px rgba(107, 72, 255, 0.1);
    }

    /* Buttons Styling */
    .button-group {
      display: flex;
      flex-wrap: wrap;
      gap: 15px;
      justify-content: center;
      margin-top: 20px;
    }

    button {
      background: var(--primary-color);
      color: white;
      padding: 12px 24px;
      border: none;
      border-radius: var(--border-radius);
      cursor: pointer;
      font-size: 16px;
      font-weight: 600;
      transition: background var(--transition-speed) ease, transform var(--transition-speed) ease, box-shadow var(--transition-speed) ease;
      text-transform: uppercase;
      letter-spacing: 1px;
    }

    button:hover {
      background: #5536FF;
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(107, 72, 255, 0.2);
    }

    #generate-combined-image {
      background: var(--secondary-color);
    }

    #generate-combined-image:hover {
      background: #FF9F29;
    }

    #generate-dialogues {
      background: #3498DB;
    }

    #generate-dialogues:hover {
      background: #2980B9;
    }

    /* Feedback Messages */
    .error-message, .success-message {
      padding: 15px;
      border-radius: var(--border-radius);
      margin: 15px 0;
      display: none;
      font-size: 0.95em;
    }

    .error-message {
      color: var(--error);
      background: #FFF5F5;
      border-left: 4px solid var(--error);
    }

    .success-message {
      color: var(--success);
      background: #F0FFF0;
      border-left: 4px solid var(--success);
    }

    /* Loading Indicators */
    .loading {
      display: none;
      text-align: center;
      padding: 20px;
      color: var(--primary-color);
      font-weight: 600;
      position: relative;
    }

    .loading::before {
      content: '';
      display: block;
      width: 40px;
      height: 40px;
      margin: 0 auto 10px;
      border: 4px solid var(--primary-color);
      border-top: 4px solid transparent;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

    .loading::after {
      content: attr(data-message);
      display: block;
      font-size: 14px;
      margin-top: 8px;
    }

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

    /* Story and Image Containers */
    #story-preview, #combined-image-container {
      background: var(--card-bg);
      padding: 30px;
      border-radius: 16px;
      margin: 30px 0;
      box-shadow: 0 8px 24px var(--shadow);
      display: none;
      animation: fadeIn 0.5s ease;
    }

    #story-preview p {
      white-space: pre-wrap;
    }

    #combined-comic-image {
      width: 100%;
      max-width: 1024px;
      border: 3px solid var(--primary-color);
      border-radius: 12px;
      transition: transform var(--transition-speed) ease;
    }

    #combined-comic-image:hover {
      transform: scale(1.02);
    }

    /* Download Button */
    #download-image {
      background: var(--success);
      margin-top: 15px;
    }

    #download-image:hover {
      background: #28a745;
    }

    /* Dialogue Bubbles */
    .dialogue-bubble {
      position: absolute;
      min-width: 100px;
      max-width: 250px;
      padding: 10px;
      background: rgba(255, 255, 255, 0.9);
      border: 2px solid var(--primary-color);
      border-radius: 12px;
      cursor: move;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
      user-select: text;
      font-size: 0.95em;
      transform-origin: top left;
    }

    .dialogue-bubble::after {
      content: '';
      position: absolute;
      bottom: -10px;
      left: 20px;
      border-width: 10px 10px 0;
      border-style: solid;
      border-color: rgba(255, 255, 255, 0.9) transparent transparent transparent;
    }

    .dialogue-bubble.rotate-180::after {
      top: -10px;
      bottom: auto;
      left: 20px;
      transform: rotate(180deg);
    }

    .dialogue-bubble.rotate-90::after {
      top: 20px;
      left: -10px;
      transform: rotate(90deg);
    }

    .dialogue-bubble.rotate-270::after {
      top: 20px;
      right: -10px;
      transform: rotate(270deg);
    }

    .dialogue-bubble .delete-button {
      position: absolute;
      top: -10px;
      right: -10px;
      background: var(--error);
      color: white;
      border: none;
      border-radius: 50%;
      width: 20px;
      height: 20px;
      cursor: pointer;
      font-size: 14px;
      line-height: 18px;
      text-align: center;
      padding: 0;
    }

    .dialogue-bubble .rotate-button {
      position: absolute;
      bottom: -10px;
      right: -10px;
      background: var(--secondary-color);
      color: white;
      border: none;
      border-radius: 50%;
      width: 20px;
      height: 20px;
      cursor: pointer;
      font-size: 14px;
      line-height: 18px;
      text-align: center;
      padding: 0;
    }

    .dialogue-bubble.selected {
      border-color: var(--accent-color);
      box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.2);
    }

    /* Responsive Design */
    @media (max-width: 768px) {
      body {
        padding: 10px;
      }

      .input-section {
        padding: 20px;
      }

      h1 {
        font-size: 2em;
      }

      button {
        width: 100%;
      }

      .dialogue-bubble {
        max-width: 120px;
        padding: 8px;
        font-size: 0.9em;
      }
    }
  </style>
</head>
<body>
  <main>
    <!-- Input Section -->
    <section class="input-section" aria-labelledby="app-title">
      <h1 id="app-title">Comic Strip Creator</h1>
      
      <form id="comic-form" novalidate>
        <div class="character-inputs">
          <!-- Character 1 Inputs -->
          <div class="character-1">
            <h2>Character 1</h2>
            <div class="input-group">
              <label for="char1-name">Name:</label>
              <input type="text" id="char1-name" name="char1-name" required placeholder="Enter character name" aria-required="true">
            </div>
            <div class="input-group">
              <label for="char1-description">Description:</label>
              <textarea id="char1-description" name="char1-description" rows="4" required placeholder="Describe your character's appearance and personality" aria-required="true"></textarea>
            </div>
          </div>
          
          <!-- Character 2 Inputs -->
          <div class="character-2">
            <h2>Character 2</h2>
            <div class="input-group">
              <label for="char2-name">Name:</label>
              <input type="text" id="char2-name" name="char2-name" required placeholder="Enter character name" aria-required="true">
            </div>
            <div class="input-group">
              <label for="char2-description">Description:</label>
              <textarea id="char2-description" name="char2-description" rows="4" required placeholder="Describe your character's appearance and personality" aria-required="true"></textarea>
            </div>
          </div>
        </div>
    
        <!-- Story Outline -->
        <div class="input-group">
          <label for="story-outline">Story Outline:</label>
          <textarea id="story-outline" name="story-outline" rows="4" required placeholder="Write your story outline here" aria-required="true"></textarea>
        </div>
    
        <!-- Scene Description -->
        <div class="input-group">
          <label for="scene-description">Scene Description:</label>
          <textarea id="scene-description" name="scene-description" rows="4" required placeholder="Describe the scene and setting" aria-required="true"></textarea>
        </div>
    
        <!-- Art Style Selection -->
        <div class="input-group">
          <label for="art-style">Art Style:</label>
          <select id="art-style" name="art-style" required aria-required="true">
            <option value="" disabled selected>Select an art style</option>
            <option value="manga">Manga</option>
            <option value="comic">Comic Book</option>
            <option value="realistic">Realistic</option>
            <option value="cartoon">Cartoon</option>
            <option value="watercolor">Watercolor</option>
            <option value="whimsical">Whimsical Storybook</option>
            <option value="pop">Pop Art</option>
            <option value="sketch">Detailed Sketch</option>
          </select>
        </div>
    
        <!-- Feedback Messages -->
        <div id="error-message" class="error-message" role="alert"></div>
        <div id="success-message" class="success-message" role="status"></div>
    
        <!-- Buttons -->
        <div class="button-group">
          <button type="button" id="generate-story">Generate Story</button>
          <button type="button" id="generate-combined-image" style="display: none;">Generate Comic Strip</button>
          <button type="button" id="generate-dialogues" style="display: none;">Generate Dialogues</button>
        </div>
      </form>
    </section>
    
    <!-- Story Preview -->
    <section id="story-preview" aria-live="polite">
      <h2>Generated Story Panels:</h2>
      <p></p>
    </section>
    
    <!-- Combined Comic Image -->
    <section id="combined-image-container" aria-live="polite">
      <h2>Your Comic Strip:</h2>
      <img id="combined-comic-image" src="" alt="Combined Comic Strip">
      <button id="download-image">Download Comic Strip</button>
    </section>
    
    <!-- Loading Indicators -->
    <div id="loading-story" class="loading" data-message="Creating your story..."></div>
    <div id="loading-image" class="loading" data-message="Crafting your comic panels..."></div>
    <div id="loading-dialogues" class="loading" data-message="Generating dialogues..."></div>
    
  </main>

  <script>
    // Check if html2canvas is loaded
    if (typeof html2canvas === 'undefined') {
      console.error('html2canvas library not loaded');
      // Fallback to load the library dynamically if needed
      const script = document.createElement('script');
      script.src = 'https://html2canvas.hertzen.com/dist/html2canvas.min.js';
      script.onload = () => console.log('html2canvas loaded dynamically');
      document.head.appendChild(script);
    }

    // Utility Function for Displaying Messages
    const displayMessage = (element, message, type) => {
      element.textContent = message;
      element.style.display = 'block';
      element.className = type === 'error' ? 'error-message' : 'success-message';
      setTimeout(() => {
        element.style.display = 'none';
      }, 5000);
    };

    // Validate Form Inputs
    const validateForm = () => {
      const form = document.getElementById('comic-form');
      const requiredFields = form.querySelectorAll('[required]');
      let isValid = true;
      requiredFields.forEach(field => {
        if (!field.value.trim()) {
          isValid = false;
          field.classList.add('input-error');
        } else {
          field.classList.remove('input-error');
        }
      });
      return isValid;
    };

    // Fetch API Wrapper with Error Handling
    const fetchAPI = async (url, options) => {
      try {
        const response = await fetch(url, options);
        if (!response.ok) throw new Error(`Error: ${response.status} ${response.statusText}`);
        return response;
      } catch (error) {
        throw error;
      }
    };

    // Generate Story Function
    const generateStory = async () => {
      const errorMessage = document.getElementById('error-message');
      const successMessage = document.getElementById('success-message');
      const loading = document.getElementById('loading-story');
      const storyPreview = document.getElementById('story-preview');
      const generateImageBtn = document.getElementById('generate-combined-image');
      const generateDialoguesBtn = document.getElementById('generate-dialogues');

      errorMessage.style.display = 'none';
      successMessage.style.display = 'none';
      storyPreview.style.display = 'none';
      generateImageBtn.style.display = 'none';
      generateDialoguesBtn.style.display = 'none';

      if (!validateForm()) {
        displayMessage(errorMessage, 'Please fill in all required fields correctly.', 'error');
        return;
      }

      loading.style.display = 'block';

      try {
        const char1Name = document.getElementById('char1-name').value.trim();
        const char1Desc = document.getElementById('char1-description').value.trim();
        const char2Name = document.getElementById('char2-name').value.trim();
        const char2Desc = document.getElementById('char2-description').value.trim();
        const storyOutline = document.getElementById('story-outline').value.trim();
        const sceneDesc = document.getElementById('scene-description').value.trim();
        const artStyle = document.getElementById('art-style').value;

        const prompt = `Create a 4-panel comic strip story with characters: ${char1Name} (${char1Desc}) and ${char2Name} (${char2Desc}). Story outline: ${storyOutline}. Scene: ${sceneDesc}. Style: ${artStyle}. Break it into 4 distinct scenes without dialogue.`;

        const response = await fetchAPI('https://text.pollinations.ai/', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            messages: [
              { role: 'system', content: 'You are a story strip writer. Create visual scene brief descriptions without dialogue.' },
              { role: 'user', content: prompt }
            ],
            model: 'openai',
            jsonMode: false
          }),
        });

        const data = await response.text();

        // Display the generated story
        storyPreview.querySelector('p').innerHTML = data.replace(/\n/g, '<br>');
        storyPreview.style.display = 'block';
        generateImageBtn.style.display = 'inline-block';
        window.storyData = data; // Store story data globally
        displayMessage(successMessage, 'Story generated successfully!', 'success');
      } catch (error) {
        displayMessage(errorMessage, error.message || 'Failed to generate story. Please try again.', 'error');
        console.error('Error:', error);
      } finally {
        loading.style.display = 'none';
      }
    };

    // Generate Combined Image Function
    const generateCombinedImage = () => {
      const loadingImage = document.getElementById('loading-image');
      const combinedImageContainer = document.getElementById('combined-image-container');
      const combinedComicImage = document.getElementById('combined-comic-image');
      const errorMessage = document.getElementById('error-message');
      const successMessage = document.getElementById('success-message');
      const generateDialoguesBtn = document.getElementById('generate-dialogues');

      errorMessage.style.display = 'none';
      successMessage.style.display = 'none';
      combinedImageContainer.style.display = 'none';
      generateDialoguesBtn.style.display = 'none';

      try {
        const artStyle = document.getElementById('art-style').value;
        const char1Name = document.getElementById('char1-name').value.trim();
        const char2Name = document.getElementById('char2-name').value.trim();

        const scenes = window.storyData.split('\n').filter(scene => scene.trim()).slice(0, 4);
        if (scenes.length < 4) {
          throw new Error('Insufficient number of scenes to create a 4-panel story.');
        }

        const prompt = `Create a single image containing a 4-panel strip arranged in a 2x2 grid. Each panel should depict the following scenes featuring ${char1Name} and ${char2Name} in the ${artStyle} style:

Panel 1: ${scenes[0]}
Panel 2: ${scenes[1]}
Panel 3: ${scenes[2]}
Panel 4: ${scenes[3]}

Ensure consistent character designs, no text and dialogues and seamless transitions between panels, maintaining the ${artStyle} aesthetic throughout the story strip.`;

        const encodedPrompt = encodeURIComponent(prompt);
        const imageUrl = `https://image.pollinations.ai/prompt/${encodedPrompt}?model=flux&width=1024&height=1024&nologo=true&private=true`;

        // Display the loading indicator
        loadingImage.style.display = 'block';

        // Once the image is loaded, display it
        combinedComicImage.onload = () => {
          loadingImage.style.display = 'none';
          combinedImageContainer.style.display = 'block';
          generateDialoguesBtn.style.display = 'inline-block';
          displayMessage(successMessage, 'Comic strip generated successfully!', 'success');
        };

        combinedComicImage.onerror = () => {
          loadingImage.style.display = 'none';
          displayMessage(errorMessage, 'Failed to load the comic strip image. Please try again.', 'error');
        };

        // Set the image source
        combinedComicImage.src = imageUrl;

      } catch (error) {
        displayMessage(errorMessage, error.message || 'Failed to generate the combined comic image.', 'error');
        console.error('Error generating combined image:', error);
      }
    };

    // Generate Dialogues Function
    const generateDialogues = async () => {
      const errorMessage = document.getElementById('error-message');
      const successMessage = document.getElementById('success-message');
      const loading = document.getElementById('loading-dialogues');
      const dialogues = [];
      
      errorMessage.style.display = 'none';
      
      try {
        loading.style.display = 'block';
        
        // Extract necessary data
        const storyData = window.storyData; // Assuming this contains the generated story
        const panels = window.storyData.split('\n').filter(scene => scene.trim()).slice(0, 4);
        
        // Generate dialogues for each panel
        for (let i = 0; i < panels.length; i++) {
          const prompt = `Generate a concise dialogue for Panel ${i + 1}: ${panels[i]}`;
          
          const response = await fetchAPI('https://text.pollinations.ai/', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              messages: [
                { role: 'system', content: 'You are a dialogue writer for story strips. Generate concise and contextually appropriate dialogues.' },
                { role: 'user', content: prompt }
              ],
              model: 'openai',
              jsonMode: true
            }),
          });
          
          const data = await response.text();
          dialogues.push(data.trim());
        }
        
        // Store dialogues globally
        window.dialogueData = dialogues;
        
        // Render dialogues on the comic strip
        renderDialogues(dialogues);
        displayMessage(successMessage, 'Dialogues generated successfully!', 'success');
        
      } catch (error) {
        displayMessage(errorMessage, error.message || 'Failed to generate dialogues. Please try again.', 'error');
        console.error('Error:', error);
      } finally {
        loading.style.display = 'none';
      }
    };

    // Render Dialogues Function
    const renderDialogues = (dialogues) => {
      const combinedComicImage = document.getElementById('combined-comic-image');
      
      // Ensure the image container is positioned relatively to place absolute elements inside
      const container = document.getElementById('combined-image-container');
      container.style.position = 'relative';
      
      // Clear existing dialogue bubbles if any
      const existingBubbles = container.querySelectorAll('.dialogue-bubble');
      existingBubbles.forEach(bubble => bubble.remove());
      
      // Create dialogue bubbles for each panel
      dialogues.forEach((dialogue, index) => {
        const bubble = document.createElement('div');
        bubble.className = 'dialogue-bubble';
        bubble.contentEditable = 'true';
        bubble.innerText = dialogue;
        
        // Add Delete and Rotate Buttons
        const deleteBtn = document.createElement('button');
        deleteBtn.className = 'delete-button';
        deleteBtn.innerHTML = '&times;';
        deleteBtn.title = 'Delete Bubble';
        bubble.appendChild(deleteBtn);
        
        const rotateBtn = document.createElement('button');
        rotateBtn.className = 'rotate-button';
        rotateBtn.innerHTML = '&#8635;'; // Rotate symbol
        rotateBtn.title = 'Rotate Bubble';
        bubble.appendChild(rotateBtn);
        
        // Positioning logic based on panel
        // Assuming panels are laid out in a 2x2 grid
        // Adjust positions as needed
        switch(index) {
          case 0:
            bubble.style.top = '10%';
            bubble.style.left = '10%';
            break;
          case 1:
            bubble.style.top = '10%';
            bubble.style.right = '10%';
            break;
          case 2:
            bubble.style.bottom = '10%';
            bubble.style.left = '10%';
            break;
          case 3:
            bubble.style.bottom = '10%';
            bubble.style.right = '10%';
            break;
          default:
            bubble.style.top = '10%';
            bubble.style.left = '10%';
        }
        
        container.appendChild(bubble);
        
        // Make the bubble draggable, resizable, and rotatable
        makeInteractive(bubble);
        
        // Event Listener for Delete Button
        deleteBtn.addEventListener('click', () => {
          bubble.remove();
        });
        
        // Event Listener for Rotate Button
        rotateBtn.addEventListener('click', () => {
          rotateBubble(bubble);
        });
        
        // Optional: Select bubble on click for additional styling or actions
        bubble.addEventListener('click', () => {
          // Remove 'selected' class from all bubbles
          document.querySelectorAll('.dialogue-bubble').forEach(b => b.classList.remove('selected'));
          // Add 'selected' class to the clicked bubble
          bubble.classList.add('selected');
        });
      });
    };

    // Make Interactive Function using Interact.js
    const makeInteractive = (element) => {
      interact(element)
        .draggable({
          inertia: true,
          modifiers: [
            interact.modifiers.restrictRect({
              restriction: 'parent',
              endOnly: true
            })
          ],
          autoScroll: true,
          listeners: {
            move: dragMoveListener,
          }
        })
        .resizable({
          edges: { left: true, right: true, bottom: true, top: true },
          inertia: true,
          modifiers: [
            interact.modifiers.restrictSize({
              min: { width: 80, height: 40 },
              max: { width: 400, height: 200 }
            })
          ],
          listeners: {
            move: resizeMoveListener
          }
        })
        .gesturable({
          listeners: {
            move: rotateMoveListener
          }
        });
    };

    // Drag Move Listener
    const dragMoveListener = (event) => {
      const target = event.target;
      // Keep the dragged position in the data attribute
      const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
      const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

      // Translate the element
      target.style.transform = `translate(${x}px, ${y}px) rotate(${getRotation(target)}deg)`;
      
      // Update the position attributes
      target.setAttribute('data-x', x);
      target.setAttribute('data-y', y);
    };

    // Resize Move Listener
    const resizeMoveListener = (event) => {
      const target = event.target;
      
      // Update the element's style
      let { width, height } = event.rect;
      target.style.width = `${width}px`;
      target.style.height = `${height}px`;
      
      // Adjust the transform to keep the element's top-left corner in place
      const x = (parseFloat(target.getAttribute('data-x')) || 0);
      const y = (parseFloat(target.getAttribute('data-y')) || 0);
      target.style.transform = `translate(${x}px, ${y}px) rotate(${getRotation(target)}deg)`;
      
      // Optionally, update any internal properties or sizes
    };

    // Rotate Move Listener
    const rotateMoveListener = (event) => {
      const target = event.target;
      const currentRotation = getRotation(target);
      const newRotation = currentRotation + event.da;
      setRotation(target, newRotation);
    };

    // Get current rotation of an element
    const getRotation = (element) => {
      const style = window.getComputedStyle(element, null);
      const transform = style.getPropertyValue('transform');
      if (transform === 'none') return 0;
      const matrix = transform.match(/^matrix\((.+)\)$/);
      if (matrix) {
        const values = matrix[1].split(', ');
        const a = parseFloat(values[0]);
        const b = parseFloat(values[1]);
        const angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
        return angle;
      }
      return 0;
    };

    // Set rotation of an element
    const setRotation = (element, angle) => {
      element.style.transform = `translate(${element.getAttribute('data-x') || 0}px, ${element.getAttribute('data-y') || 0}px) rotate(${angle}deg)`;
    };

    // Rotate Bubble Function (on rotate button click)
    const rotateBubble = (element) => {
      const currentRotation = getRotation(element);
      const newRotation = currentRotation + 90; // Rotate by 90 degrees each click
      setRotation(element, newRotation);

      // Adjust arrow direction based on rotation
      if ((newRotation / 90) % 4 === 0) {
        element.classList.remove('rotate-90', 'rotate-180', 'rotate-270');
      } else if ((newRotation / 90) % 4 === 1) {
        element.classList.remove('rotate-180', 'rotate-270');
        element.classList.add('rotate-90');
      } else if ((newRotation / 90) % 4 === 2) {
        element.classList.remove('rotate-90', 'rotate-270');
        element.classList.add('rotate-180');
      } else if ((newRotation / 90) % 4 === 3) {
        element.classList.remove('rotate-90', 'rotate-180');
        element.classList.add('rotate-270');
      }
    };

    // Update the downloadImageWithDialogues function to handle loading state
    const downloadImageWithDialogues = async () => {
      const container = document.getElementById('combined-image-container');
      const loadingMessage = document.getElementById('loading-image');
      const errorMessage = document.getElementById('error-message');
      
      try {
        loadingMessage.style.display = 'block';
        loadingMessage.setAttribute('data-message', 'Preparing download...');
        
        // Ensure html2canvas is available
        if (typeof html2canvas === 'undefined') {
          throw new Error('html2canvas library not loaded');
        }
        
        const canvas = await html2canvas(container, {
          useCORS: true,
          allowTaint: true,
          logging: false,
          backgroundColor: null
        });
        
        const link = document.createElement('a');
        link.href = canvas.toDataURL('image/png');
        link.download = 'comic_strip_with_dialogues.png';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        
      } catch (error) {
        console.error('Download error:', error);
        displayMessage(errorMessage, 'Failed to download the comic strip. Please try again.', 'error');
      } finally {
        loadingMessage.style.display = 'none';
      }
    };

    // Event Listeners
    document.getElementById('generate-story').addEventListener('click', generateStory);
    document.getElementById('generate-combined-image').addEventListener('click', generateCombinedImage);
    document.getElementById('generate-dialogues').addEventListener('click', generateDialogues);
    document.getElementById('download-image').addEventListener('click', downloadImageWithDialogues);
  </script>
</body>
</html>