evaluate this app: <script>
    class ImageCache {
      constructor() {
        this.cache = new Map();
      }

      async getImage(url) {
        if (this.cache.has(url)) {
          return this.cache.get(url);
        }

        try {
          const imageData = await this.loadImage(url);
          this.cache.set(url, imageData);
          return imageData;
        } catch (error) {
          throw new Error(`Failed to load image: ${error.message}`);
        }
      }

      async loadImage(url) {
        return new Promise((resolve, reject) => {
          const img = new Image();
          img.crossOrigin = 'anonymous';
          img.onload = () => {
            const canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            resolve({
              dataUrl: canvas.toDataURL('image/jpeg'),
              width: img.width,
              height: img.height,
              element: img
            });
          };
          img.onerror = () => reject(new Error('Image load failed'));
          img.src = url;
        });
      }

      clear() {
        this.cache.clear();
      }
    }

    class PollinationsAPI {
      static retryDelay = 1000; // ms
      static maxRetries = 5;
      static requestQueue = [];
      static isProcessingQueue = false;
      static rateLimitDelay = 1000; // 1 second

      static async fetchWithRetry(url, options = {}, retries = this.maxRetries) {
        try {
          const response = await fetch(url, options);
          if (response.status === 429) {
            throw new Error('RATE_LIMIT');
          }
          if (!response.ok) {
            throw new Error('FETCH_ERROR');
          }
          return response;
        } catch (error) {
          if (retries === 0) throw error;
          if (error.message === 'RATE_LIMIT') {
            await new Promise(res => setTimeout(res, this.rateLimitDelay));
          } else {
            await new Promise(res => setTimeout(res, this.retryDelay * (this.maxRetries - retries + 1)));
          }
          return this.fetchWithRetry(url, options, retries - 1);
        }
      }

      static async queueRequest(requestFn) {
        return new Promise((resolve, reject) => {
          this.requestQueue.push({ requestFn, resolve, reject });
          if (!this.isProcessingQueue) this.processQueue();
        });
      }

      static async processQueue() {
        if (this.requestQueue.length === 0) {
          this.isProcessingQueue = false;
          return;
        }
        this.isProcessingQueue = true;
        const { requestFn, resolve, reject } = this.requestQueue.shift();
        try {
          const result = await requestFn();
          resolve(result);
        } catch (error) {
          reject(error);
        }
        await new Promise(res => setTimeout(res, this.rateLimitDelay));
        this.processQueue();
      }

      static async generateText(prompt) {
        return this.queueRequest(async () => {
          try {
            const url = `https://text.pollinations.ai/${encodeURIComponent(prompt)}`;
            const response = await this.fetchWithRetry(url);
            return response.text();
          } catch (error) {
            if (error.message === 'RATE_LIMIT') {
              throw new Error('Rate limit exceeded. Please try again later.');
            }
            throw new Error('Failed to generate text.');
          }
        });
      }

      static async generateImage(prompt, settings = {}) {
        return this.queueRequest(async () => {
          const baseUrl = 'https://image.pollinations.ai/prompt/';
          const params = new URLSearchParams({
            width: settings.width || 1024,
            height: settings.height || 576,
            seed: settings.seed || Math.floor(Math.random() * 1000000),
            nologo: 'true',
            model: settings.model || 'flux',
            private: 'true',
            t: Date.now()
          });

          const encodedPrompt = encodeURIComponent(prompt);
          const url = `${baseUrl}${encodedPrompt}?${params}`;

          try {
            const response = await this.fetchWithRetry(url);
            return response.url;
          } catch (error) {
            if (error.message === 'RATE_LIMIT') {
              throw new Error('Rate limit exceeded. Please try again later.');
            }
            throw new Error('Failed to generate image.');
          }
        });
      }
    }

    class LyricalStorybookCreator {
      constructor() {
        this.currentPage = 0;
        this.pages = [];
        this.isGenerating = false;
        this.selectedStyle = null;
        this.darkMode = true;

        // New property for song title input
        this.songTitleInput = document.getElementById('songTitle');

        // New property for image model selection
        this.imageModelSelect = document.getElementById('imageModel');

        // New property for image caching
        this.imageCache = new ImageCache();

        // UI Elements
        this.lyricsInput = document.getElementById('lyrics');
        this.themeInput = document.getElementById('theme');
        this.artStyleSelect = document.getElementById('artStyle');
        this.colorPaletteSelect = document.getElementById('colorPalette');
        this.pageLayoutSelect = document.querySelector('.layout-controls .layout-select');
        this.fontFamilySelect = document.getElementById('fontFamily');
        this.generateBtn = document.getElementById('generateBtn');
        this.resetBtn = document.getElementById('resetBtn');
        this.previewArea = document.getElementById('previewArea');
        this.messageBox = document.getElementById('messageBox');
        this.paginationControls = document.getElementById('paginationControls');
        this.prevPageBtn = document.getElementById('prevPageBtn');
        this.nextPageBtn = document.getElementById('nextPageBtn');
        this.pageIndicator = document.getElementById('pageIndicator');
        this.modeToggle = document.getElementById('modeToggle');

        // Initially hide layout controls
        this.layoutControls = document.querySelector('.layout-controls');
        this.layoutControls.style.display = 'none';

        this.bindEvents();
      }

      bindEvents() {
        this.generateBtn.addEventListener('click', () => this.generateStorybook());
        this.resetBtn.addEventListener('click', () => this.resetApp());
        this.prevPageBtn.addEventListener('click', () => this.navigatePage(-1));
        this.nextPageBtn.addEventListener('click', () => this.navigatePage(1));
        this.pageLayoutSelect.addEventListener('change', () => {
          if (this.pages.length > 0) {
            this.updatePreview();
          }
        });
        this.fontFamilySelect.addEventListener('change', () => {
          if (this.pages.length > 0) {
            this.updatePreview();
          }
        });
        this.modeToggle.addEventListener('change', () => this.toggleTheme());

        // Swipe Navigation for Mobile
        this.previewArea.addEventListener('touchstart', this.handleTouchStart.bind(this), false);
        this.previewArea.addEventListener('touchend', this.handleTouchEnd.bind(this), false);
        this.xDown = null;
      }

      handleTouchStart(evt) {
        const firstTouch = evt.touches[0];
        this.xDown = firstTouch.clientX;
      }

      handleTouchEnd(evt) {
        if (!this.xDown) return;
        const xUp = evt.changedTouches[0].clientX;
        const deltaX = this.xDown - xUp;
        const threshold = 50; // Required min distance for swipe

        if (deltaX > threshold) {
          this.navigatePage(1);
        } else if (deltaX < -threshold) {
          this.navigatePage(-1);
        }
        this.xDown = null;
      }

      showMessage(message, type = 'success') {
        this.messageBox.textContent = message;
        this.messageBox.className = `message ${type}`;
        this.messageBox.style.display = 'block';
        setTimeout(() => {
          this.messageBox.style.display = 'none';
        }, 4000);
      }

      async generateStorybook() {
        if (this.isGenerating) return;

        // Hide layout controls during generation
        this.layoutControls.style.display = 'none';

        // Set default page layout to single
        this.selectedStyle = {
          artStyle: this.artStyleSelect.value,
          colorPalette: this.colorPaletteSelect.value,
          pageLayout: 'single'
        };

        const lyrics = this.lyricsInput.value.trim();
        const theme = this.themeInput.value.trim();
        const title = this.songTitleInput.value.trim(); // Capture title

        if (!lyrics || !theme || !title) {
          this.showMessage('Please enter song title, lyrics, and theme.', 'error');
          return;
        }

        this.isGenerating = true;
        this.generateBtn.classList.add('generating');
        this.generateBtn.disabled = true;

        this.previewArea.innerHTML = `
          <div class="loading-container">
            <div class="progress-container">
              <div class="progress-bar" id="generationProgress"></div>
            </div>
            <div class="progress-text" id="progressText">
              Preparing to generate your storybook...
            </div>
          </div>
        `;

        const progressBar = document.querySelector('#generationProgress');
        const progressText = document.querySelector('#progressText');
        const rawSegments = lyrics.split('\n').filter(line => line.trim() !== '');
        const maxPages = rawSegments.length <= 12 ? 2 : 3; // Use 2 pages for shorter lyrics, 3 for longer
        const segmentsPerPage = Math.ceil(rawSegments.length / maxPages);
        const segments = [];

        for (let i = 0; i < rawSegments.length; i += segmentsPerPage) {
          segments.push(rawSegments.slice(i, i + segmentsPerPage).join(' '));
        }

        this.pages = [];
        const totalSteps = segments.length;
        let currentStep = 0;

        try {
          for (let i = 0; i < segments.length && i < maxPages; i++) {
            currentStep++;
            const segment = segments[i];
            const progress = Math.round((currentStep / totalSteps) * 100);
            progressBar.style.width = `${progress}%`;
            progressText.textContent = `Creating page ${currentStep} of ${totalSteps} (${progress}% complete)`;

            // Generate scene description
            const sceneDescription = await PollinationsAPI.generateText(`
              Create a vivid, detailed description for a storybook illustration. 
              Context: These song lyrics: "${segment}"
              Theme: ${theme}
              Style: ${this.selectedStyle.artStyle}
              Consider:
              - The emotional tone and mood
              - Key symbols and metaphors
              - Color palette and lighting
              - Character expressions and poses
              - Environmental details and setting
              - Composition and focal points
              Describe the scene in rich, artistic detail.
            `);

            // Generate image prompt
            const imagePrompt = await this.buildImagePrompt(this.selectedStyle.artStyle, this.selectedStyle.colorPalette, theme, sceneDescription);
            const imageData = await this.imageCache.getImage(await PollinationsAPI.generateImage(imagePrompt, { width: 1024, height: 576, model: this.imageModelSelect.value }));

            this.pages.push({
              lyrics: segment,
              description: sceneDescription,
              imageUrl: imageData.dataUrl
            });

            this.showMessage(`Page ${i + 1} of ${segments.length} generated.`, 'success');
          }

          this.currentPage = 0;

          // After successful generation, show the layout controls
          this.layoutControls.style.display = 'flex';
          this.updatePreview();
        } catch (error) {
          console.error(error);
          this.showMessage(error.message, 'error');
          this.previewArea.innerHTML = `<p>Error generating storybook: ${error.message}</p>`;
          // Keep layout controls hidden on error
          this.layoutControls.style.display = 'none';
        } finally {
          this.isGenerating = false;
          this.generateBtn.classList.remove('generating');
          this.generateBtn.disabled = false;
        }
      }

      async buildImagePrompt(artStyle, colorPalette, theme, sceneDescription) {
        // Base quality parameters
        const qualityParams = "masterpiece quality, highly detailed, sharp focus, professional lighting";

        // Color palette prompts
        const colorPalettes = {
          'vibrant': "vibrant saturated colors, bold color combinations",
          'pastel': "soft pastel colors, gentle muted tones",
          'monochrome': "monochromatic color scheme, variations in tone",
          'earthy': "natural earth tones, organic color palette",
          'neon': "bright neon colors, electric hues",
          'vintage': "faded vintage colors, retro color palette",
          'cool': "cool color temperature, blues, purples",
          'warm': "warm color temperature, reds, oranges",
          'jewel': "rich jewel tones, deep saturated colors",
          'grayscale': "black and white, grayscale values"
        };

        const selectedPalette = colorPalettes[colorPalette] || colorPalettes['vibrant'];

        // Style-specific prompts
        const stylePrompts = {
          'whimsical': "whimsical and playful illustration, magical atmosphere, enchanted storybook style",
          'photorealistic': "hyperrealistic detailed illustration, cinematic lighting, dramatic atmosphere",
          'sketch': "detailed hand-drawn sketch, artistic linework, expressive strokes",
          'pencil': "detailed pencil drawing, fine graphite shading, delicate linework",
          'vintage': "classic vintage storybook art, traditional illustration style",
          'cartoon': "animated storybook style, expressive characters, bold colors",
          'exaggerated': "stylized artistic illustration, bold and dynamic composition",
          'watercolor': "delicate watercolor illustration, soft color transitions",
          'acrylic': "rich acrylic painting style, bold brushstrokes",
          'digital': "modern digital art, clean refined illustration",
          'pop': "pop art illustration style, bold colors, graphic elements",
          'woodcut': "traditional woodcut print style, bold contrasting lines"
        };

        // Composition elements
        const composition = [
          "balanced composition",
          "attention to detail",
          selectedPalette,
          "dynamic lighting",
          "emotional storytelling through visuals"
        ].join(", ");

        // Build final prompt
        return `${stylePrompts[artStyle]}, 
          ${sceneDescription},
          ${theme},
          ${selectedPalette},
          storybook illustration style,
          ${composition},
          ${qualityParams},
          suitable for narrative illustration,
          story-driven artwork,
          emotional depth,
          artistic excellence,
          trending on artstation`;
      }

      updatePreview() {
        if (this.pages.length === 0) {
          this.previewArea.innerHTML = '<p>No pages to display. Generate your storybook first.</p>';
          this.paginationControls.style.display = 'none';
          return;
        }

        const page = this.pages[this.currentPage];
        const layoutClass = (() => {
          switch(this.pageLayoutSelect.value) {
            case 'spread': return 'spread-layout';
            default: return '';
          }
        })();

        const fontFamily = this.fontFamilySelect.value;

        // Create the preview section container
        const previewContent = document.createElement('div');
        previewContent.innerHTML = `
          <div class="storybook-page ${layoutClass}">
            <img src="${page.imageUrl}" alt="Story Illustration">
            <div class="page-text" style="font-family: ${fontFamily};">
              <p>${page.lyrics}</p>
            </div>
          </div>
          
          <div class="export-controls">
            <button class="btn btn-primary" id="exportPdfBtn">Export as PDF</button>
            <button class="btn btn-secondary" id="exportEpubBtn">Export as EPUB</button>
          </div>
        `;

        // Clear and update preview area
        this.previewArea.innerHTML = '';
        this.previewArea.appendChild(previewContent);

        // Get buttons AFTER they are added to DOM
        const exportPdfBtn = document.getElementById('exportPdfBtn');
        const exportEpubBtn = document.getElementById('exportEpubBtn');

        // Setup export buttons
        if (exportPdfBtn) {
          exportPdfBtn.addEventListener('click', () => this.exportPDF());
        }
        if (exportEpubBtn) {
          exportEpubBtn.addEventListener('click', () => this.exportEpub());
        }

        // Show export controls
        const exportControls = previewContent.querySelector('.export-controls');
        exportControls.style.display = 'flex';

        // Update pagination controls
        this.pageIndicator.textContent = `Page ${this.currentPage + 1} of ${this.pages.length}`;
        this.paginationControls.style.display = 'flex';
        this.prevPageBtn.disabled = this.currentPage === 0;
        this.nextPageBtn.disabled = this.currentPage === this.pages.length - 1;
      }

      navigatePage(direction) {
        const newPage = this.currentPage + direction;
        if (newPage >= 0 && newPage < this.pages.length) {
          this.currentPage = newPage;
          this.updatePreview();
        }
      }

      async exportPDF() {
        if (this.pages.length === 0) {
          this.showMessage('No storybook to export. Please generate it first.', 'error');
          return;
        }

        // Get reference to the export button directly from DOM
        const exportPdfBtn = document.getElementById('exportPdfBtn');
        if (!exportPdfBtn) {
          this.showMessage('Export button not found. Please try again.', 'error');
          return;
        }

        exportPdfBtn.classList.add('generating');
        exportPdfBtn.disabled = true;
        this.showMessage('Exporting your storybook as PDF...', 'success');

        try {
          const songTitle = this.songTitleInput.value; // Capture the title outside the header function

          const docDefinition = {
            content: [],
            pageSize: 'A4',
            pageMargins: [40, 70, 40, 60], // Increased top margin for title space
            pageOrientation: this.pageLayoutSelect.value === 'spread' ? 'landscape' : 'portrait', // Force landscape for spread
            defaultStyle: {
              font: 'Roboto',
              fontSize: 14, // Updated from 12
              lineHeight: 1.8, // Updated from 1.6
              color: document.body.classList.contains('dark-mode') ? '#fff9eb' : '#2c3e50' // Match site text color
            },
            background: function(currentPage, pageSize) {
              return {
                canvas: [
                  {
                    type: 'rect',
                    x: 0,
                    y: 0,
                    w: pageSize.width,
                    h: pageSize.height,
                    color: document.body.classList.contains('dark-mode') ? '#2a1b3d' : '#f8f4e8' // Match preview section colors
                  }
                ]
              };
            },
            fonts: {
              Roboto: {
                normal: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Regular.ttf',
                bold: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Medium.ttf',
                italics: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Italic.ttf',
                bolditalics: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-MediumItalic.ttf'
              },
              // For custom fonts, we'll map them to Roboto as fallback
              'Playfair Display': {
                normal: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Regular.ttf',
                bold: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Medium.ttf'
              },
              'EB Garamond': {
                normal: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Regular.ttf',
                bold: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Medium.ttf'
              }
            },
            // Add header with song title only on the first page
            header: function(currentPage) {
              if (currentPage === 1) {
                return {
                  text: `${songTitle || 'Lyrical Storybook'}`,
                  alignment: 'center',
                  fontSize: 18,
                  bold: true,
                  margin: [0, 20, 0, 20],
                  color: document.body.classList.contains('dark-mode') ? '#fff9eb' : '#2c3e50'
                };
              }
              return null;
            },
            footer: function(currentPage, pageCount) {
              return {
                text: `Page ${currentPage} of ${pageCount}`,
                alignment: 'right',
                margin: [0, 0, 40, 0],
                fontSize: 10,
                italics: true,
                color: document.body.classList.contains('dark-mode') ? '#e2d9f3' : '#34495e' // Match site secondary text color
              };
            }
          };

          // Simplify getTextStyling()
          const getTextStyling = () => {
            const textColor = document.body.classList.contains('dark-mode') ? '#fff9eb' : '#2c3e50';
            return {
              font: 'Roboto',
              fontSize: 14,
              lineHeight: 1.8,
              color: textColor
            };
          };

          const addPageBreak = () => {
            docDefinition.content.push({ text: '', pageBreak: 'after' });
          };

          for (let i = 0; i < this.pages.length; i++) {
            const page = this.pages[i];
            const imgData = await this.imageCache.getImage(page.imageUrl);
            const textStyling = getTextStyling();

            switch(this.pageLayoutSelect.value) {
              case 'spread':
                docDefinition.content.push({
                  columns: [
                    {
                      width: '48%', // Slightly wider column for image
                      stack: [{
                        image: imgData.dataUrl,
                        fit: [400, 600], // Larger max dimensions for better quality
                        alignment: 'center',
                        margin: [0, 10, 20, 10] // Adjusted margins: [left, top, right, bottom]
                      }]
                    },
                    {
                      width: '48%', // Matching width for text
                      stack: [{
                        text: page.lyrics,
                        ...textStyling,
                        alignment: 'left',
                        margin: [20, 30, 0, 10] // More top margin to align with image center
                      }]
                    }
                  ],
                  columnGap: 20, // Reduced gap between columns
                  margin: [20, 30, 20, 30] // Increased outer margins for better page balance
                });
                break;

              default:
                docDefinition.content.push({
                  stack: [
                    {
                      image: imgData.dataUrl,
                      width: 400,
                      alignment: 'center',
                      margin: [0, 0, 0, 20]
                    },
                    {
                      text: page.lyrics,
                      ...textStyling,
                      alignment: 'center',
                      margin: [40, 0, 40, 20]
                    }
                  ],
                  margin: [0, 20, 0, 20]
                });
            }

            if (i < this.pages.length - 1) {
              addPageBreak();
            }
          }

          pdfMake.createPdf(docDefinition).download('Lyrical_Storybook.pdf');
          this.showMessage('Storybook exported successfully!', 'success');

        } catch (error) {
          console.error(error);
          this.showMessage('Failed to export PDF. Please try again.', 'error');
        } finally {
          exportPdfBtn.classList.remove('generating');
          exportPdfBtn.disabled = false;
        }
      }

      async getImageData(url) {
        try {
          return await this.imageCache.getImage(url);
        } catch (error) {
          console.error('Image load error:', error);
          throw error;
        }
      }

      async exportEpub() {
        if (this.pages.length === 0) {
          this.showMessage('No storybook to export. Please generate it first.', 'error');
          return;
        }

        const exportEpubBtn = document.getElementById('exportEpubBtn');
        if (!exportEpubBtn) {
          this.showMessage('Export button not found. Please try again.', 'error');
          return;
        }

        exportEpubBtn.classList.add('generating');
        exportEpubBtn.disabled = true;
        this.showMessage('Exporting your storybook as EPUB...', 'success');

        try {
          const zip = new JSZip();

          // 1. Add mimetype file (must be the first file and uncompressed)
          zip.file('mimetype', 'application/epub+zip', { compression: 'STORE' });

          // 2. Create META-INF/container.xml
          const containerXml = `<?xml version="1.0" encoding="UTF-8"?>
          <container version="1.0" xmlns="http://www.idpf.org/2007/opf">
            <rootfiles>
              <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>
            </rootfiles>
          </container>`;
          zip.folder('META-INF').file('container.xml', containerXml);

          // 3. Create OEBPS/content.opf
          const manifestItems = [
            `<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>`
          ];

          this.pages.forEach((page, index) => {
            manifestItems.push(`<item id="page${index}" href="page${index + 1}.xhtml" media-type="application/xhtml+xml"/>`);
            manifestItems.push(`<item id="css" href="styles.css" media-type="text/css"/>`);
          });

          const contentOpf = `<?xml version="1.0" encoding="UTF-8"?>
          <package version="3.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="book-id">
            <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
              <dc:title>${this.songTitleInput.value || 'Lyrical Storybook'}</dc:title>
              <dc:language>en</dc:language>
              <dc:identifier id="book-id">urn:uuid:${this.generateUUID()}</dc:identifier>
            </metadata>
            <manifest>
              ${manifestItems.join('\n        ')}
            </manifest>
            <spine toc="ncx">
              ${this.pages.map((_, index) => `<itemref idref="page${index}"/>`).join('\n        ')}
            </spine>
          </package>`;

          zip.folder('OEBPS').file('content.opf', contentOpf);

          // 4. Create OEBPS/toc.ncx
          const tocNcx = `<?xml version="1.0" encoding="UTF-8"?>
          <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
            <head>
              <meta name="dtb:uid" content="urn:uuid:${this.generateUUID()}"/>
              <meta name="dtb:depth" content="1"/>
              <meta name="dtb:totalPageCount" content="0"/>
              <meta name="dtb:maxPageNumber" content="0"/>
            </head>
            <docTitle>
              <text>${this.songTitleInput.value || 'Lyrical Storybook'}</text>
            </docTitle>
            <navMap>
              ${this.pages.map((page, index) => `
              <navPoint id="navPoint-${index + 1}" playOrder="${index + 1}">
                <navLabel>
                  <text>Page ${index + 1}</text>
                </navLabel>
                <content src="page${index + 1}.xhtml"/>
              </navPoint>`).join('')}
            </navMap>
          </ncx>`;
          zip.folder('OEBPS').file('toc.ncx', tocNcx);

          // 5. Create OEBPS/styles.css
          const stylesCss = `
          body {
            font-family: ${this.fontFamilySelect.value}, sans-serif;
            margin: 20px;
            background-color: ${document.body.classList.contains('dark-mode') ? '#2a1b3d' : '#f8f4e8'};
            color: ${document.body.classList.contains('dark-mode') ? '#fff9eb' : '#2c3e50'};
          }
          img {
            max-width: 100%;
            height: auto;
            display: block;
            margin: 0 auto 20px;
          }
          p {
            font-size: 14px;
            line-height: 1.8;
          }
          `;
          zip.folder('OEBPS').file('styles.css', stylesCss);

          // 6. Create OEBPS/pageX.xhtml for each page
          for (let i = 0; i < this.pages.length; i++) {
            const page = this.pages[i];
            const pageXhtml = `<?xml version="1.0" encoding="UTF-8"?>
            <!DOCTYPE html>
            <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
              <title>Page ${i + 1}</title>
              <link rel="stylesheet" type="text/css" href="styles.css"/>
            </head>
            <body>
              <div class="page">
                <img src="images/image${i + 1}.jpg" alt="Story Illustration"/>
                <p>${this.escapeHtml(page.lyrics)}</p>
              </div>
            </body>
            </html>`;
            zip.folder('OEBPS').file(`page${i + 1}.xhtml`, pageXhtml);

            // 7. Add images to OEBPS/images/
            const imgData = page.imageUrl.split(',')[1]; // Assuming data URL
            zip.folder('OEBPS/images').file(`image${i + 1}.jpg`, imgData, { base64: true });
          }

          // Generate the EPUB file
          const content = await zip.generateAsync({ type: 'blob' });
          saveAs(content, `${this.songTitleInput.value || 'Lyrical_Storybook'}.epub`);

          this.showMessage('Storybook exported successfully as EPUB!', 'success');
        } catch (error) {
          console.error(error);
          this.showMessage('Failed to export EPUB. Please try again.', 'error');
        } finally {
          exportEpubBtn.classList.remove('generating');
          exportEpubBtn.disabled = false;
        }
      }

      // Utility function to generate UUID
      generateUUID() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
          const r = Math.random() * 16 | 0,
                v = c === 'x' ? r : (r & 0x3 | 0x8);
          return v.toString(16);
        });
      }

      // Utility function to escape HTML
      escapeHtml(text) {
        const map = {
          '&': '&amp;',
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#039;'
        };
        return text.replace(/[&<>"']/g, function(m) { return map[m]; });
      }

      resetApp() {
        // Clear Inputs
        this.lyricsInput.value = '';
        this.themeInput.value = '';
        this.songTitleInput.value = '';
        this.artStyleSelect.value = 'woodcut';
        this.colorPaletteSelect.value = 'vibrant';
        this.imageModelSelect.value = 'flux';
        this.pageLayoutSelect.value = 'single';
        this.fontFamilySelect.value = 'Lato';

        // Reset pages and current page
        this.pages = [];
        this.currentPage = 0;

        // Clear preview area and hide controls
        this.previewArea.innerHTML = '<p>Preview will appear here once you generate your storybook.</p>';
        this.paginationControls.style.display = 'none';
        this.layoutControls.style.display = 'none';

        // Clear any messages
        this.messageBox.style.display = 'none';

        // Clear image cache
        this.imageCache.clear();

        // Reset generating state
        this.isGenerating = false;
        this.generateBtn.classList.remove('generating');
        this.generateBtn.disabled = false;

        // Hide export controls if they exist
        const exportControls = document.querySelector('.export-controls');
        if (exportControls) {
          exportControls.style.display = 'none';
        }

        // Show confirmation message
        this.showMessage('All fields have been reset', 'success');
      }

      toggleTheme() {
        this.darkMode = !this.darkMode;
        document.body.classList.toggle('light-mode', !this.darkMode);
        document.body.classList.toggle('dark-mode', this.darkMode);
      }
    }

    // Initialize the Application on DOM Content Loaded
    document.addEventListener('DOMContentLoaded', () => {
      window.storybookCreator = new LyricalStorybookCreator();
    });
  </script>