favorite-colors-grid<template>
  <div
    class="add-background-page"
    @dragenter.prevent="handleDragEnter"
    @dragover.prevent="handleDragOver"
    @dragleave="handleDragLeave"
    @drop.prevent="handleDrop"
  >
    <div v-if="isDragging" class="global-drag-overlay">
      <div class="overlay-content">
        <p>{{ t('Drop image anywhere to add background') }}</p>
      </div>
    </div>
    <input
      ref="fileInputRef"
      type="file"
      accept="image/*"
      style="display: none"
      @change="handleFileSelect"
    />
    
    <div class="page-header" :class="{ 'has-sidebar': uploadedImage }">
      <h2>{{ t('Add Background') }}</h2>
      <!-- Color Shades in Header Center -->
      <div v-if="uploadedImage" class="header-color-shades">
        <div
          v-for="(shade, index) in colorShades"
          :key="index"
          class="shade-item"
          :class="{ 'active': backgroundColor && shade === backgroundColor }"
          :style="{ backgroundColor: shade }"
          @click="selectShade(shade)"
          :title="shade"
        >
        </div>
      </div>
      <div v-if="uploadedImage" class="toolbar-actions">
        <button
          v-if="resultImage"
          class="toolbar-btn"
          @click="handleDownload"
          :title="t('Download')"
        >
          <i class="fa fa-download"></i>
        </button>
        <button
          v-if="resultImage"
          class="toolbar-btn"
          @click="clearBackground"
          :title="t('Clear Background')"
        >
          <i class="fa fa-eraser"></i>
        </button>
        <button 
          class="toolbar-btn"
          @click="resetUpload"
          :title="t('Change Image')"
        >
          <i class="fa fa-refresh"></i>
        </button>
        <button 
          class="toolbar-btn mobile-color-picker-btn"
          @click="toggleMobileColorPicker"
          :title="t('Color Picker')"
        >
          <i class="fa fa-palette"></i>
        </button>
      </div>
    </div>

    <div class="page-content" :class="{ 'has-sidebar': uploadedImage }">
      <div class="tool-container">
        <div class="main-area">
          <div class="upload-section">
            <div
              v-if="!uploadedImage"
              class="upload-area"
              :class="{ 'dragging': isDragging }"
            >
              <div class="upload-content">
                <button
                  type="button"
                  class="upload-btn"
                  @click="triggerFileInput"
                  :disabled="processing"
                >
                  <i class="fa fa-upload"></i>
                  <span>{{ t('Upload Image') }}</span>
                </button>
                
                <div class="divider">
                  <span>{{ t('or') }}</span>
                </div>
                
                <div class="url-input-wrapper" @click.stop>
                  <input
                    ref="urlInputRef"
                    v-model="imageUrl"
                    type="text"
                    class="url-input"
                    :placeholder="t('Paste image URL here')"
                    @keyup.enter="handleUrlSubmit"
                    @paste="handleUrlPaste"
                    :disabled="processing"
                  />
                </div>
                
                <p class="upload-hint">{{ t('Drag and drop your image anywhere, or paste image directly') }}</p>
                <p class="upload-format-hint">{{ t('Supports JPG, PNG, WebP formats') }}</p>
              </div>
              <div v-if="processing" class="upload-processing-overlay">
                <div class="spinner"></div>
                <p>{{ t('Processing...') }}</p>
              </div>
            </div>

            <div v-else class="preview-section">
              <div class="canvas-container" v-show="uploadedImage">
                <canvas 
                  ref="canvasRef" 
                  class="preview-canvas"
                ></canvas>
                <div v-if="processing" class="canvas-processing-overlay">
                  <div class="spinner"></div>
                  <p>{{ t('Applying background...') }}</p>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div v-if="historyList.length > 0 || uploadedImage" class="history-bar">
          <div class="history-scroll-container">
            <button 
              type="button"
              class="history-item add-button"
              @click.stop="triggerFileInput"
              :title="t('Add New Image')"
            >
              <i class="fa fa-plus"></i>
            </button>

            <div
              v-for="(item, index) in historyList"
              :key="item.id"
              class="history-item"
              :class="{ 'active': currentHistoryIndex === index }"
              @click="selectHistoryItem(index)"
            >
              <div class="history-thumbnail">
                <img :src="getHistoryThumbnailUrl(item)" alt="History" />
                <button
                  type="button"
                  class="history-delete-btn"
                  @click.stop="removeHistoryItem(index)"
                  :title="t('Delete')"
                  :aria-label="t('Delete')"
                ></button>
              </div>
            </div>

            <div
              v-if="uploadedImage && resultImage && currentHistoryIndex === -1"
              class="history-item active"
            >
              <div class="history-thumbnail">
                <img :src="resultImageUrl" alt="Current" />
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Mobile Bottom Sheet Color Picker -->
    <transition name="slide-up">
      <div 
        v-if="uploadedImage && showMobileColorPicker" 
        class="mobile-color-sheet-overlay"
        @click="closeMobileColorPicker"
      >
        <div 
          class="mobile-color-sheet"
          @click.stop
        >
          <div class="sheet-handle"></div>
          <div class="sheet-header">
            <h4>{{ t('Background Color') }}</h4>
            <button 
              class="sheet-close-btn"
              @click="closeMobileColorPicker"
              :title="t('Close')"
            >
              <i class="fa fa-times"></i>
            </button>
          </div>
          <div class="sheet-content">
            <div class="color-picker-section">
              <div class="color-picker-container">
                <input
                  v-model="backgroundColor"
                  type="color"
                  class="sidebar-color-picker"
                  @input="onColorChange"
                />
                <div class="hex-input-wrapper">
                  <input
                    v-model="backgroundColor"
                    type="text"
                    class="sidebar-hex-input"
                    placeholder=""
                    @input="onHexInputChange"
                    @blur="onHexInputBlur"
                  />
                  <button
                    class="add-favorite-btn"
                    @click="addToFavorites"
                    :title="t('Add to favorites')"
                    :disabled="favoriteColors.length >= MAX_FAVORITE_COLORS"
                  >
                    <i class="fa fa-star"></i>
                  </button>
                </div>
              </div>
            </div>
            
            <div v-if="favoriteColors.length > 0" class="favorite-colors-section">
              <h4 class="section-title">{{ t('Favorite Colors') }}</h4>
              <div class="favorite-colors-grid">
                <div
                  v-for="(color, index) in favoriteColors"
                  :key="index"
                  class="favorite-color-item"
                  :class="{ 'active': color === backgroundColor }"
                  :style="{ backgroundColor: color }"
                  @click="selectCommonColor(color)"
                  :title="color"
                >
                  <button
                    type="button"
                    class="remove-favorite-btn"
                    @click.stop="removeFavoriteColor(color)"
                    :title="t('Remove from favorites')"
                    :aria-label="t('Remove from favorites')"
                  ></button>
                </div>
              </div>
            </div>
            
            <div class="common-colors-section">
              <h4 class="section-title">{{ t('Common Colors') }}</h4>
              <div class="common-colors-grid">
                <div
                  v-for="(color, index) in commonColors"
                  :key="index"
                  class="common-color-item"
                  :class="{ 'active': color === backgroundColor }"
                  :style="{ backgroundColor: color }"
                  @click="selectCommonColor(color)"
                  :title="color"
                >
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </transition>

    <!-- Mobile Bottom Sheet Color Picker -->
    <transition name="slide-up">
      <div 
        v-if="uploadedImage && showMobileColorPicker" 
        class="mobile-color-sheet-overlay"
        @click="closeMobileColorPicker"
      >
        <div 
          class="mobile-color-sheet"
          @click.stop
        >
          <div class="sheet-handle"></div>
          <div class="sheet-header">
            <h4>{{ t('Background Settings') }}</h4>
            <button 
              class="sheet-close-btn"
              @click="closeMobileColorPicker"
              :title="t('Close')"
            >
              <i class="fa fa-times"></i>
            </button>
          </div>
          <div class="sheet-content">
            <n-tabs v-model:value="activeTab" type="line" animated>
              <n-tab-pane name="color" :tab="t('Color')">
                <div class="color-picker-section">
                  <div class="color-picker-container">
                    <!-- Show checkerboard when background is cleared -->
                    <div 
                      v-if="!backgroundColor" 
                      class="sidebar-color-picker-empty"
                      @click="triggerColorPicker"
                    >
                    </div>
                    <input
                      v-if="backgroundColor"
                      v-model="backgroundColor"
                      type="color"
                      class="sidebar-color-picker"
                      @input="onColorChange"
                    />
                    <div class="hex-input-wrapper">
                      <input
                        v-model="backgroundColor"
                        type="text"
                        class="sidebar-hex-input"
                        placeholder=""
                        @input="onHexInputChange"
                        @blur="onHexInputBlur"
                      />
                      <button
                        class="add-favorite-btn"
                        @click="addToFavorites"
                        :title="t('Add to favorites')"
                        :disabled="!backgroundColor || favoriteColors.length >= MAX_FAVORITE_COLORS"
                      >
                        <i class="fa fa-star"></i>
                      </button>
                    </div>
                  </div>
                </div>
                
                <div v-if="favoriteColors.length > 0" class="favorite-colors-section">
                  <h4 class="section-title">{{ t('Favorite Colors') }}</h4>
                  <div class="favorite-colors-grid">
                    <div
                      v-for="(color, index) in favoriteColors"
                      :key="index"
                      class="favorite-color-item"
                      :class="{ 'active': color === backgroundColor }"
                      :style="{ backgroundColor: color }"
                      @click="selectCommonColor(color)"
                      :title="color"
                    >
                      <button
                        type="button"
                        class="remove-favorite-btn"
                        @click.stop="removeFavoriteColor(color)"
                        :title="t('Remove from favorites')"
                        :aria-label="t('Remove from favorites')"
                      ></button>
                    </div>
                  </div>
                </div>
                
                <div class="common-colors-section">
                  <h4 class="section-title">{{ t('Common Colors') }}</h4>
                  <div class="common-colors-grid">
                    <div
                      v-for="(color, index) in commonColors"
                      :key="index"
                      class="common-color-item"
                      :class="{ 'active': color === backgroundColor }"
                      :style="{ backgroundColor: color }"
                      @click="selectCommonColor(color)"
                      :title="color"
                    >
                    </div>
                  </div>
                </div>
              </n-tab-pane>
              
              <n-tab-pane name="image" :tab="t('Background')">
                <div class="background-images-section">
                  <h4 class="section-title">{{ t('Upload Custom Background') }}</h4>
                  <n-upload
                    :custom-request="handleCustomBackgroundUpload"
                    accept="image/*"
                    :show-file-list="false"
                  >
                    <n-upload-dragger>
                      <div class="upload-dragger-content">
                        <i class="fa fa-cloud-upload" style="font-size: 32px; color: #1fc76f;"></i>
                        <div class="upload-text">{{ t('Click or drag to upload') }}</div>
                      </div>
                    </n-upload-dragger>
                  </n-upload>
                </div>
                
                <div v-if="customBackgrounds.length > 0" class="custom-backgrounds-section">
                  <h4 class="section-title">{{ t('My Backgrounds') }}</h4>
                  <div class="background-images-grid">
                    <div
                      v-for="(bgImage, index) in customBackgrounds"
                      :key="index"
                      class="background-image-item"
                      @click="applyBackgroundImage(bgImage)"
                    >
                      <img :src="bgImage" alt="Background" />
                      <button
                        type="button"
                        class="remove-bg-btn"
                        @click.stop="removeCustomBackground(index)"
                        :title="t('Remove')"
                      ></button>
                    </div>
                  </div>
                </div>
                
                <div class="pexels-images-section">
                  <h4 class="section-title">{{ t('Pexels Backgrounds') }}</h4>
                  <div class="pexels-search-box">
                    <input
                      v-model="pexelsSearchInput"
                      type="text"
                      class="pexels-search-input"
                      :placeholder="t('Search backgrounds...')"
                      @keyup.enter="searchPexelsImages"
                    />
                    <button
                      type="button"
                      class="pexels-search-btn"
                      @click="searchPexelsImages"
                      :disabled="pexelsLoading"
                    >
                      <i class="fa fa-search"></i>
                    </button>
                  </div>
                  <n-spin :show="pexelsLoading">
                    <div v-if="pexelsImages.length > 0" class="background-images-grid">
                      <div
                        v-for="image in displayedPexelsImages"
                        :key="image.id"
                        class="background-image-item"
                        @click="applyBackgroundImage(image.src.medium)"
                      >
                        <img :src="image.src.tiny" :alt="image.alt" />
                      </div>
                    </div>
                    <div v-if="showLoadMoreButton" class="load-more-container">
                      <button 
                        type="button"
                        class="load-more-btn"
                        @click="loadMorePexelsImages"
                      >
                        {{ t('Show More') }}
                      </button>
                    </div>
                    <n-empty v-else-if="pexelsImages.length === 0" :description="t('No images found')" size="small" />
                  </n-spin>
                </div>
              </n-tab-pane>
            </n-tabs>
          </div>
        </div>
      </div>
    </transition>

    <!-- Right Sidebar for Common Colors and Color Picker (Desktop Only) -->
    <div v-if="uploadedImage" class="right-sidebar desktop-only">
      <n-tabs v-model:value="activeTab" type="line" animated>
        <n-tab-pane name="color" :tab="t('Color')">
          <div class="sidebar-content">
            <div class="color-picker-section">
              <h4 class="section-title">{{ t('Background Color') }}</h4>
              <div class="color-picker-container">
                <!-- Show checkerboard when background is cleared -->
                <div 
                  v-if="!backgroundColor" 
                  class="sidebar-color-picker-empty"
                  @click="triggerColorPicker"
                >
                </div>
                <input
                  v-if="backgroundColor"
                  v-model="backgroundColor"
                  type="color"
                  class="sidebar-color-picker"
                  @input="onColorChange"
                />
                <div class="hex-input-wrapper">
                  <input
                    v-model="backgroundColor"
                    type="text"
                    class="sidebar-hex-input"
                    placeholder=""
                    @input="onHexInputChange"
                    @blur="onHexInputBlur"
                  />
                  <button
                    class="add-favorite-btn"
                    @click="addToFavorites"
                    :title="t('Add to favorites')"
                    :disabled="!backgroundColor || favoriteColors.length >= MAX_FAVORITE_COLORS"
                  >
                    <i class="fa fa-star"></i>
                  </button>
                </div>
              </div>
            </div>
            
            <div v-if="favoriteColors.length > 0" class="favorite-colors-section">
              <h4 class="section-title">{{ t('Favorite Colors') }}</h4>
              <div class="favorite-colors-grid">
                <div
                  v-for="(color, index) in favoriteColors"
                  :key="index"
                  class="favorite-color-item"
                  :class="{ 'active': color === backgroundColor }"
                  :style="{ backgroundColor: color }"
                  @click="selectCommonColor(color)"
                  :title="color"
                >
                  <button
                    type="button"
                    class="remove-favorite-btn"
                    @click.stop="removeFavoriteColor(color)"
                    :title="t('Remove from favorites')"
                    :aria-label="t('Remove from favorites')"
                  ></button>
                </div>
              </div>
            </div>
            
            <div class="common-colors-section">
              <h4 class="section-title">{{ t('Common Colors') }}</h4>
              <div class="common-colors-grid">
                <div
                  v-for="(color, index) in commonColors"
                  :key="index"
                  class="common-color-item"
                  :class="{ 'active': color === backgroundColor }"
                  :style="{ backgroundColor: color }"
                  @click="selectCommonColor(color)"
                  :title="color"
                >
                </div>
              </div>
            </div>
          </div>
        </n-tab-pane>
        
        <n-tab-pane name="image" :tab="t('Background Image')">
          <div class="sidebar-content">
            <div class="background-images-section">
              <h4 class="section-title">{{ t('Upload Custom Background') }}</h4>
              <n-upload
                :custom-request="handleCustomBackgroundUpload"
                accept="image/*"
                :show-file-list="false"
              >
                <n-upload-dragger>
                  <div class="upload-dragger-content">
                    <i class="fa fa-cloud-upload" style="font-size: 32px; color: #1fc76f;"></i>
                    <div class="upload-text">{{ t('Click or drag to upload') }}</div>
                  </div>
                </n-upload-dragger>
              </n-upload>
            </div>
            
            <div v-if="customBackgrounds.length > 0" class="custom-backgrounds-section">
              <h4 class="section-title">{{ t('My Backgrounds') }}</h4>
              <div class="background-images-grid">
                <div
                  v-for="(bgImage, index) in customBackgrounds"
                  :key="index"
                  class="background-image-item"
                  @click="applyBackgroundImage(bgImage)"
                >
                  <img :src="bgImage" alt="Background" />
                  <button
                    type="button"
                    class="remove-bg-btn"
                    @click.stop="removeCustomBackground(index)"
                    :title="t('Remove')"
                  ></button>
                </div>
              </div>
            </div>
            
            <div class="pexels-images-section">
              <h4 class="section-title">{{ t('Pexels Backgrounds') }}</h4>
              <div class="pexels-search-box">
                <input
                  v-model="pexelsSearchInput"
                  type="text"
                  class="pexels-search-input"
                  :placeholder="t('Search backgrounds...')"
                  @keyup.enter="searchPexelsImages"
                />
                <button
                  type="button"
                  class="pexels-search-btn"
                  @click="searchPexelsImages"
                  :disabled="pexelsLoading"
                >
                  <i class="fa fa-search"></i>
                </button>
              </div>
              <n-spin :show="pexelsLoading">
                <div v-if="pexelsImages.length > 0" class="background-images-grid">
                  <div
                    v-for="image in displayedPexelsImages"
                    :key="image.id"
                    class="background-image-item"
                    @click="applyBackgroundImage(image.src.medium)"
                  >
                    <img :src="image.src.tiny" :alt="image.alt" />
                  </div>
                </div>
                <div v-if="showLoadMoreButton" class="load-more-container">
                  <button 
                    type="button"
                    class="load-more-btn"
                    @click="loadMorePexelsImages"
                  >
                    {{ t('Show More') }}
                  </button>
                </div>
                <n-empty v-else-if="pexelsImages.length === 0" :description="t('No images found')" />
              </n-spin>
            </div>
          </div>
        </n-tab-pane>
      </n-tabs>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { useMessage, NTabs, NTabPane, NSpin, NUpload, NUploadDragger, NButton, NEmpty } from 'naive-ui'
import { useRoute } from 'vue-router'
import axios from 'axios'
import { t } from '@/shared/i18n'
import { get_session_api_headers } from '@/shared/api/auth'
import { useAuthStore } from '@/shared/stores/auth'
import { useSEO } from '@/shared/composables/useSEO'
import { compressImageFile } from '@/shared/utils/imageResize'
import { Canvas, FabricImage } from 'fabric'
import ColorThief from 'colorthief'

const message = useMessage()
const authStore = useAuthStore()
const route = useRoute()

// SEO configuration
useSEO({
  title: t('Add Background'),
  description: t('Add custom background color to images. Free online tool to add background to transparent images. Supports JPG, PNG, WebP formats.'),
  keywords: t('add background, background color, image background, transparent background, online background tool, image processing, background editor, free tool')
})

interface HistoryItem {
  id: string
  originalImageUrl: string
  originalImageFile: File | null
  resultImage: string
  backgroundColor: string
  timestamp: number
}

const fileInputRef = ref<HTMLInputElement | null>(null)
const urlInputRef = ref<HTMLInputElement | null>(null)
const canvasRef = ref<HTMLCanvasElement | null>(null)
const uploadedImage = ref<File | null>(null)
const uploadedImageUrl = ref<string>('')
const resultImage = ref<string>('')
const resultImageBlobUrl = ref<string>('')
const imageUrl = ref<string>('')
const backgroundColor = ref<string>('#FFFFFF')
const isColorValid = ref<boolean>(true)
let colorChangeDebounceTimer: ReturnType<typeof setTimeout> | null = null
const colorThief = new ColorThief()

// Generate 11 color shades based on current background color (current color in middle)
const colorShades = computed(() => {
  return generateColorShades(backgroundColor.value)
})

// Favorite colors - persisted in localStorage
const favoriteColors = ref<string[]>([])
const MAX_FAVORITE_COLORS = 12

// Load favorite colors from localStorage
const loadFavoriteColors = () => {
  try {
    const saved = localStorage.getItem('add_background_favorite_colors')
    if (saved) {
      const parsed = JSON.parse(saved)
      if (Array.isArray(parsed)) {
        favoriteColors.value = parsed.slice(0, MAX_FAVORITE_COLORS)
      }
    }
  } catch (error) {
    console.error('Failed to load favorite colors:', error)
  }
}

// Save favorite colors to localStorage
const saveFavoriteColors = () => {
  try {
    localStorage.setItem('add_background_favorite_colors', JSON.stringify(favoriteColors.value))
  } catch (error) {
    console.error('Failed to save favorite colors:', error)
  }
}

// Add current color to favorites
const addToFavorites = () => {
  const color = backgroundColor.value.toUpperCase()
  
  // Check if color already exists
  if (favoriteColors.value.includes(color)) {
    message.info(t('Color already in favorites'))
    return
  }
  
  // Check if favorites is full
  if (favoriteColors.value.length >= MAX_FAVORITE_COLORS) {
    message.warning(t('Maximum 12 favorite colors allowed. Please remove one first.'))
    return
  }
  
  // Add to beginning of array
  favoriteColors.value.unshift(color)
  saveFavoriteColors()
  message.success(t('Color added to favorites'))
}

// Remove color from favorites
const removeFavoriteColor = (color: string) => {
  const index = favoriteColors.value.indexOf(color)
  if (index > -1) {
    favoriteColors.value.splice(index, 1)
    saveFavoriteColors()
  }
}

// Trigger color picker when clicking on empty checkerboard
const triggerColorPicker = (event: MouseEvent) => {
  // Get the position of the checkerboard element
  const targetElement = event.currentTarget as HTMLElement
  const rect = targetElement.getBoundingClientRect()
  
  // Create a temporary color input at the checkerboard position
  const tempInput = document.createElement('input')
  tempInput.type = 'color'
  tempInput.value = '#FFFFFF'
  tempInput.style.position = 'fixed'
  tempInput.style.left = `${rect.left}px`
  tempInput.style.top = `${rect.top}px`
  tempInput.style.width = `${rect.width}px`
  tempInput.style.height = `${rect.height}px`
  tempInput.style.opacity = '0'
  tempInput.style.border = 'none'
  tempInput.style.cursor = 'pointer'
  
  document.body.appendChild(tempInput)
  
  tempInput.addEventListener('input', (e) => {
    const target = e.target as HTMLInputElement
    backgroundColor.value = target.value
    onColorChange()
  })
  
  tempInput.addEventListener('change', () => {
    setTimeout(() => {
      document.body.removeChild(tempInput)
    }, 100)
  })
  
  // Trigger click to open color picker
  tempInput.click()
}

// Fetch Pexels images
const fetchPexelsImages = async () => {
  // Skip fetching if no API key is configured
  if (!PEXELS_API_KEY) {
    console.warn('Pexels API key not configured. Skipping image fetch.')
    pexelsImages.value = []
    pexelsLoading.value = false
    pexelsHasMore.value = false
    return
  }
  
  pexelsLoading.value = true
  try {
    const response = await axios.get('https://api.pexels.com/v1/search', {
      params: {
        query: pexelsQuery.value,
        page: pexelsPage.value,
        per_page: PEXELS_PER_PAGE,
        orientation: 'landscape',
        locale: 'zh-CN' // Add locale for better Chinese search results
      },
      headers: {
        Authorization: PEXELS_API_KEY
      },
      // Ensure proper encoding of parameters
      paramsSerializer: {
        encode: (param) => encodeURIComponent(param)
      }
    })
    const newPhotos = response.data.photos || []
    
    // Append new photos to existing list
    if (pexelsPage.value === 1) {
      pexelsImages.value = newPhotos
    } else {
      pexelsImages.value = [...pexelsImages.value, ...newPhotos]
    }
    
    // If we got fewer images than requested, there are no more images
    pexelsHasMore.value = newPhotos.length >= PEXELS_PER_PAGE
  } catch (error: any) {
    console.error('Failed to fetch Pexels images:', error)
    pexelsHasMore.value = false
    // Don't show error message if API key is not configured
    if (PEXELS_API_KEY) {
      message.error(t('Failed to load background images'))
    }
  } finally {
    pexelsLoading.value = false
  }
}

// Apply background image
const applyBackgroundImage = async (imageUrl: string) => {
  if (!fabricCanvas || !uploadedImage.value || !uploadedImageUrl.value) return
  
  try {
    processing.value = true
    
    // Save the original foreground image reference before clearing
    const currentObjects = fabricCanvas.getObjects()
    const foregroundImg = currentObjects.length > 0 ? currentObjects[0] : null
    
    // Load background image
    const bgImg = await FabricImage.fromURL(imageUrl, {
      crossOrigin: 'anonymous'
    })
    
    if (!bgImg || !fabricCanvas) {
      processing.value = false
      return
    }
    
    // Get canvas dimensions
    const canvasWidth = fabricCanvas.width || 1
    const canvasHeight = fabricCanvas.height || 1
    
    // Scale background image to cover canvas
    const bgScale = Math.max(
      canvasWidth / (bgImg.width || 1),
      canvasHeight / (bgImg.height || 1)
    )
    
    bgImg.set({
      scaleX: bgScale,
      scaleY: bgScale,
      left: canvasWidth / 2,
      top: canvasHeight / 2,
      originX: 'center',
      originY: 'center',
      selectable: false,
      evented: false
    })
    
    // Clear canvas
    fabricCanvas.clear()
    
    // Add background image first (at the bottom)
    fabricCanvas.add(bgImg)
    
    // Re-load and add the foreground image (original uploaded image)
    if (foregroundImg) {
      // Clone the foreground image to avoid issues
      const newForegroundImg = await FabricImage.fromURL(uploadedImageUrl.value, {
        crossOrigin: 'anonymous'
      })
      
      if (newForegroundImg) {
        newForegroundImg.set({
          left: canvasWidth / 2,
          top: canvasHeight / 2,
          originX: 'center',
          originY: 'center',
          selectable: false,
          evented: false
        })
        
        fabricCanvas.add(newForegroundImg)
      }
    }
    
    // Ensure correct layer order: background at bottom, foreground on top
    const allObjects = fabricCanvas.getObjects()
    if (allObjects.length === 2) {
      // Move background (first object) to back
      fabricCanvas.sendObjectToBack(allObjects[0])
      // Move foreground (second object) to front
      fabricCanvas.bringObjectToFront(allObjects[1])
    }
    
    fabricCanvas.renderAll()
    
    // Export result
    const dataUrl = fabricCanvas.toDataURL({
      format: 'png',
      quality: 0.95,
      multiplier: 1
    })
    
    resultImage.value = dataUrl
    await cacheResultImage(dataUrl)
    
    // Update history
    if (currentHistoryIndex.value === -1 && uploadedImage.value && uploadedImageUrl.value) {
      const historyItem: HistoryItem = {
        id: `history-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        originalImageUrl: uploadedImageUrl.value,
        originalImageFile: uploadedImage.value,
        resultImage: dataUrl,
        backgroundColor: backgroundColor.value,
        timestamp: Date.now()
      }
      historyList.value.unshift(historyItem)
      currentHistoryIndex.value = 0
    } else if (currentHistoryIndex.value >= 0) {
      historyList.value[currentHistoryIndex.value].resultImage = dataUrl
    }
    
    processing.value = false
    
    // Auto-close mobile color picker on mobile
    if (window.innerWidth <= 768) {
      closeMobileColorPicker()
    }
  } catch (error: any) {
    console.error('Apply background image error:', error)
    message.error(t('Failed to apply background image'))
    processing.value = false
  }
}

// Handle custom background upload
const handleCustomBackgroundUpload = async (options: any) => {
  const { file } = options
  
  const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
  if (!validTypes.includes(file.file?.type || '')) {
    message.warning(t('Unsupported image format. Please use JPG, PNG, or WebP'))
    return
  }
  
  const maxSize = 10 * 1024 * 1024
  if ((file.file?.size || 0) > maxSize) {
    message.warning(t('Image size exceeds 10MB limit'))
    return
  }
  
  try {
    const reader = new FileReader()
    reader.onload = (e) => {
      const dataUrl = e.target?.result as string
      if (dataUrl) {
        // Add to custom backgrounds
        customBackgrounds.value.unshift(dataUrl)
        // Limit to 20 custom backgrounds
        if (customBackgrounds.value.length > 20) {
          customBackgrounds.value = customBackgrounds.value.slice(0, 20)
        }
        saveCustomBackgrounds()
        message.success(t('Background uploaded successfully'))
      }
    }
    reader.readAsDataURL(file.file)
  } catch (error) {
    message.error(t('Failed to upload background'))
  }
}

// Remove custom background
const removeCustomBackground = (index: number) => {
  customBackgrounds.value.splice(index, 1)
  saveCustomBackgrounds()
}

// 36 common background colors - light to dark gradient
const commonColors = ref([
  // Pure Whites (3)
  '#FFFFFF', // Pure White
  '#FEFEFE', // Almost White
  '#FAFAFA', // Off White
  
  // Light Grays (6)
  '#F5F5F5', // Very Light Gray 1
  '#F0F0F0', // Very Light Gray 2
  '#EBEBEB', // Very Light Gray 3
  '#E8E8E8', // Light Gray 1
  '#E0E0E0', // Light Gray 2
  '#DADADA', // Light Gray 3
  
  // Light Pinks & Peaches (6)
  '#FFE4E1', // Misty Rose
  '#FFDDE1', // Light Pink
  '#FFE4CC', // Light Peach 1
  '#FFD7BE', // Light Peach 2
  '#FFDAB9', // Peach Puff
  '#FFCBA4', // Deep Peach
  
  // Light Yellows & Creams (6)
  '#FFFACD', // Lemon Chiffon
  '#FFF4CC', // Light Yellow 1
  '#FFECB3', // Light Yellow 2
  '#FFE4B5', // Moccasin
  '#FFDEAD', // Navajo White
  '#FFD699', // Light Orange
  
  // Light Greens & Blues (6)
  '#E8F5E9', // Light Green 1
  '#D4EDDA', // Light Green 2
  '#D4E4BC', // Pale Green
  '#E0F7FA', // Light Cyan
  '#C8E6F5', // Light Sky Blue
  '#B3E5FC', // Light Blue
  
  // Light Purples & Lavenders (3)
  '#F3E5F5', // Lavender Blush
  '#EDE7F6', // Light Purple 1
  '#E6D7FF', // Light Purple 2
  
  // Medium to Dark Grays (6)
  '#D4D4D4', // Medium Gray 1
  '#CACACA', // Medium Gray 2
  '#BEBEBE', // Medium Gray 3
  '#B0B0B0', // Medium Gray 4
  '#A0A0A0', // Dark Gray 1
  '#909090'  // Dark Gray 2
])

// OKLCH color utilities - perceptually uniform color space
const hexToOklch = (hex: string): { l: number; c: number; h: number } => {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  if (!result) return { l: 1, c: 0, h: 0 }
  
  // Convert hex to linear RGB
  let r = parseInt(result[1], 16) / 255
  let g = parseInt(result[2], 16) / 255
  let b = parseInt(result[3], 16) / 255
  
  // Apply sRGB to linear RGB conversion
  r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92
  g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92
  b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92
  
  // Convert linear RGB to OKLab
  const l_ = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b
  const m_ = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b
  const s_ = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b
  
  const l = Math.cbrt(l_)
  const m = Math.cbrt(m_)
  const s = Math.cbrt(s_)
  
  const L = 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s
  const a = 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s
  const bVal = 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s
  
  // Convert OKLab to OKLCH
  const C = Math.sqrt(a * a + bVal * bVal)
  const H = Math.atan2(bVal, a) * 180 / Math.PI
  
  return { l: L, c: C, h: H >= 0 ? H : H + 360 }
}

const oklchToHex = (l: number, c: number, h: number): string => {
  // Convert OKLCH to OKLab
  const hRad = h * Math.PI / 180
  const a = c * Math.cos(hRad)
  const bVal = c * Math.sin(hRad)
  
  // Convert OKLab to linear RGB
  const l_ = l + 0.3963377774 * a + 0.2158037573 * bVal
  const m_ = l - 0.1055613458 * a - 0.0638541728 * bVal
  const s_ = l - 0.0894841775 * a - 1.2914855480 * bVal
  
  const lCube = l_ * l_ * l_
  const mCube = m_ * m_ * m_
  const sCube = s_ * s_ * s_
  
  let r = +4.0767416621 * lCube - 3.3077115913 * mCube + 0.2309699292 * sCube
  let g = -1.2684380046 * lCube + 2.6097574011 * mCube - 0.3413193965 * sCube
  let bRgb = -0.0041960863 * lCube - 0.7034186147 * mCube + 1.7076147010 * sCube
  
  // Apply linear RGB to sRGB conversion
  r = r > 0.0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - 0.055 : 12.92 * r
  g = g > 0.0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - 0.055 : 12.92 * g
  bRgb = bRgb > 0.0031308 ? 1.055 * Math.pow(bRgb, 1 / 2.4) - 0.055 : 12.92 * bRgb
  
  // Clamp values to [0, 1]
  r = Math.max(0, Math.min(1, r))
  g = Math.max(0, Math.min(1, g))
  bRgb = Math.max(0, Math.min(1, bRgb))
  
  // Convert to hex
  const rHex = Math.round(r * 255).toString(16).padStart(2, '0')
  const gHex = Math.round(g * 255).toString(16).padStart(2, '0')
  const bHex = Math.round(bRgb * 255).toString(16).padStart(2, '0')
  
  return `#${rHex}${gHex}${bHex}`.toUpperCase()
}

// Generate 11 color shades with current color in the middle (6th position) using OKLCH
const generateColorShades = (baseColor: string): string[] => {
  const { l, c, h } = hexToOklch(baseColor)
  const shades: string[] = []
  
  // Generate 5 lighter shades before current color
  // Use perceptually uniform lightness steps (±0.08 intervals)
  for (let i = 5; i > 0; i--) {
    const lighterL = Math.min(1, l + (i * 0.08))
    shades.push(oklchToHex(lighterL, c, h))
  }
  
  // Add current color in the middle (6th position)
  shades.push(baseColor)
  
  // Generate 5 darker shades after current color
  for (let i = 1; i <= 5; i++) {
    const darkerL = Math.max(0, l - (i * 0.08))
    shades.push(oklchToHex(darkerL, c, h))
  }
  
  return shades
}
const resultImageUrl = computed(() => {
  if (!resultImage.value) return ''
  return resultImage.value
})

const historyList = ref<HistoryItem[]>([])
const currentHistoryIndex = ref<number>(-1)
const isDragging = ref(false)
const dragCounter = ref(0)
const processing = ref(false)
const showMobileColorPicker = ref(false)
let fabricCanvas: Canvas | null = null

// Tabs state
const activeTab = ref<'color' | 'image'>('color')

// Pexels images state
const pexelsImages = ref<any[]>([])
const pexelsLoading = ref(false)
const pexelsPage = ref(1)
const pexelsQuery = ref('nature background')
const pexelsSearchInput = ref('nature background')
const PEXELS_API_KEY = __PEXELS_API_KEY__ || ''
const pexelsDisplayCount = ref(12)
const PEXELS_PER_PAGE = 80 // Fetch 80 images per API call
const PEXELS_DISPLAY_INCREMENT = 12 // Display 12 more images each time
const pexelsHasMore = ref(true) // Track if there are more images to fetch

// Custom background images
const customBackgrounds = ref<string[]>([])

// Computed: Display limited Pexels images
const displayedPexelsImages = computed(() => {
  return pexelsImages.value.slice(0, pexelsDisplayCount.value)
})

// Show "Load More" button condition
const showLoadMoreButton = computed(() => {
  // Show button if:
  // 1. There are more images in cache to display, OR
  // 2. There might be more images to fetch from API
  return pexelsImages.value.length > pexelsDisplayCount.value || 
         (pexelsImages.value.length > 0 && pexelsHasMore.value && pexelsImages.value.length >= pexelsDisplayCount.value)
})

// Load more Pexels images
const loadMorePexelsImages = async () => {
  // First, try to display more from cached images
  const remainingCached = pexelsImages.value.length - pexelsDisplayCount.value
  
  if (remainingCached >= PEXELS_DISPLAY_INCREMENT) {
    // Enough cached images, just increase display count
    pexelsDisplayCount.value += PEXELS_DISPLAY_INCREMENT
  } else if (remainingCached > 0) {
    // Some cached images left, display them first
    pexelsDisplayCount.value = pexelsImages.value.length
    
    // Then fetch more if available
    if (pexelsHasMore.value) {
      pexelsPage.value++
      await fetchPexelsImages()
      // Display up to 12 more from the newly fetched images
      const needed = PEXELS_DISPLAY_INCREMENT - remainingCached
      pexelsDisplayCount.value = Math.min(
        pexelsImages.value.length,
        pexelsDisplayCount.value + needed
      )
    }
  } else {
    // No cached images left, fetch more from API
    if (pexelsHasMore.value) {
      pexelsPage.value++
      await fetchPexelsImages()
      // Display 12 more images
      pexelsDisplayCount.value = Math.min(
        pexelsImages.value.length,
        pexelsDisplayCount.value + PEXELS_DISPLAY_INCREMENT
      )
    }
  }
}

// Search Pexels images with new query
const searchPexelsImages = async () => {
  const searchTerm = pexelsSearchInput.value.trim()
  if (!searchTerm) {
    message.warning(t('Please enter search keywords'))
    return
  }
  
  // Reset state for new search
  pexelsQuery.value = searchTerm
  pexelsPage.value = 1
  pexelsImages.value = []
  pexelsDisplayCount.value = 12
  pexelsHasMore.value = true
  
  // Fetch new results
  await fetchPexelsImages()
}

// Load custom backgrounds from localStorage
const loadCustomBackgrounds = () => {
  try {
    const saved = localStorage.getItem('add_background_custom_images')
    if (saved) {
      const parsed = JSON.parse(saved)
      if (Array.isArray(parsed)) {
        customBackgrounds.value = parsed
      }
    }
  } catch (error) {
    console.error('Failed to load custom backgrounds:', error)
  }
}

// Save custom backgrounds to localStorage
const saveCustomBackgrounds = () => {
  try {
    localStorage.setItem('add_background_custom_images', JSON.stringify(customBackgrounds.value))
  } catch (error) {
    console.error('Failed to save custom backgrounds:', error)
  }
}

const adjustCanvasSize = () => {
  const canvas = canvasRef.value
  if (!canvas || !fabricCanvas) return
  
  const container = canvas.parentElement
  if (!container) return
  
  const previewSection = container.closest('.preview-section') as HTMLElement
  if (!previewSection) return
  
  // Check if mobile
  const isMobile = window.innerWidth <= 768
  
  const previewRect = previewSection.getBoundingClientRect()
  const padding = isMobile ? 24 : 48
  const maxAvailableWidth = Math.max(0, previewRect.width - padding)
  const maxAvailableHeight = Math.max(0, previewRect.height - padding)
  
  if (maxAvailableWidth <= 0 || maxAvailableHeight <= 0) return
  
  // Get canvas natural dimensions
  const canvasWidth = fabricCanvas.width || 1
  const canvasHeight = fabricCanvas.height || 1
  
  // Calculate scale to fit within available space
  const scale = Math.min(
    maxAvailableWidth / canvasWidth,
    maxAvailableHeight / canvasHeight,
    1
  )
  
  // Apply display dimensions to container
  const displayWidth = canvasWidth * scale
  const displayHeight = canvasHeight * scale
  
  container.style.width = `${displayWidth}px`
  container.style.height = `${displayHeight}px`
  
  // Scale canvas visually using CSS transform
  canvas.style.transform = `scale(${scale})`
  canvas.style.transformOrigin = 'top left'
}

watch(uploadedImageUrl, adjustCanvasSize)

const handleResize = () => {
  adjustCanvasSize()
}

const handlePaste = async (event: ClipboardEvent) => {
  // Prevent paste handling when user is in right sidebar or mobile sheet
  const target = event.target as HTMLElement
  
  // Check if paste is triggered from right sidebar or mobile sheet
  if (target) {
    const isInRightSidebar = target.closest('.right-sidebar')
    const isInMobileSheet = target.closest('.mobile-color-sheet')
    const isInUploadDragger = target.closest('.n-upload-dragger')
    
    // Don't handle paste if it's from sidebar, mobile sheet, or upload dragger
    if (isInRightSidebar || isInMobileSheet || isInUploadDragger) {
      return
    }
  }
  
  // Only handle paste when no image is uploaded yet (in upload area)
  if (uploadedImage.value) {
    return
  }
  
  const items = event.clipboardData?.items
  if (!items) return

  for (let i = 0; i < items.length; i++) {
    const item = items[i]
    if (item.type.indexOf('image') !== -1) {
      event.preventDefault()
      const file = item.getAsFile()
      if (file) {
        processFile(file)
      }
      return
    }
  }

  const text = event.clipboardData?.getData('text')
  if (text && isValidImageUrl(text)) {
    event.preventDefault()
    imageUrl.value = text.trim()
    await handleUrlSubmit()
  }
}

const isValidImageUrl = (url: string): boolean => {
  if (!url || typeof url !== 'string') return false
  try {
    const urlObj = new URL(url)
    if (!['http:', 'https:'].includes(urlObj.protocol)) {
      return false
    }
    
    const pathname = urlObj.pathname.toLowerCase()
    const imageExtensions = ['.jpg', '.jpeg', '.png', '.webp', '.gif', '.bmp']
    
    const hasImageExtension = imageExtensions.some(ext => pathname.endsWith(ext)) || 
                              urlObj.pathname.match(/\.(jpg|jpeg|png|webp|gif|bmp)(\?|$)/i) !== null
    
    return hasImageExtension || pathname.length > 0
  } catch {
    return false
  }
}

const handleUrlPaste = async (event: ClipboardEvent) => {
  const text = event.clipboardData?.getData('text')
  if (text && isValidImageUrl(text)) {
    event.preventDefault()
    imageUrl.value = text.trim()
    await handleUrlSubmit()
  }
}

const handleUrlSubmit = async () => {
  const url = imageUrl.value.trim()
  if (!url) {
    message.warning(t('Please enter an image URL'))
    return
  }

  if (!isValidImageUrl(url)) {
    message.warning(t('Please enter a valid image URL'))
    return
  }

  processing.value = true
  resultImage.value = ''
  currentHistoryIndex.value = -1

  try {
    const response = await fetch(url, { mode: 'cors' })
    if (!response.ok) {
      throw new Error(`Failed to load image: ${response.status}`)
    }

    const blob = await response.blob()
    
    const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
    if (!validTypes.includes(blob.type)) {
      message.warning(t('Unsupported image format. Please use JPG, PNG, or WebP'))
      processing.value = false
      return
    }

    const maxSize = 10 * 1024 * 1024
    if (blob.size > maxSize) {
      message.warning(t('Image size exceeds 10MB limit'))
      processing.value = false
      return
    }

    const originalFile = new File([blob], 'image-from-url', { type: blob.type })
    
    try {
      const compressedFile = await compressImageFile(originalFile, {
        maxWidth: 1024,
        maxHeight: 1024,
        quality: 0.92,
        mode: 'contain'
      })
      
      uploadedImage.value = compressedFile
      const imageUrl = URL.createObjectURL(compressedFile)
      uploadedImageUrl.value = imageUrl
      
      await extractDominantColorAndInitialize(imageUrl)
    } catch (error) {
      message.error(t('Image processing failed, please try again'))
      processing.value = false
    }
  } catch (error: any) {
    let errorMessage = t('Failed to load image from URL')
    
    if (error.message?.includes('CORS')) {
      errorMessage = t('CORS error. The image server does not allow cross-origin access.')
    } else if (error.message?.includes('Failed to load')) {
      errorMessage = t('Failed to load image. Please check the URL and try again.')
    }
    
    message.error(errorMessage)
    processing.value = false
  }
}

onMounted(() => {
  window.addEventListener('resize', handleResize)
  window.addEventListener('paste', handlePaste)
  loadFavoriteColors()
  loadCustomBackgrounds()
  fetchPexelsImages()
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
  window.removeEventListener('paste', handlePaste)
  if (colorChangeDebounceTimer) {
    clearTimeout(colorChangeDebounceTimer)
  }
  if (uploadedImageUrl.value && uploadedImageUrl.value.startsWith('blob:')) {
    URL.revokeObjectURL(uploadedImageUrl.value)
  }
  if (resultImageBlobUrl.value) {
    URL.revokeObjectURL(resultImageBlobUrl.value)
  }
  if (fabricCanvas) {
    fabricCanvas.dispose()
    fabricCanvas = null
  }
  // Restore body scroll
  document.body.style.overflow = ''
})

const triggerFileInput = () => {
  if (fileInputRef.value) {
    fileInputRef.value.value = ''
    fileInputRef.value.click()
  }
}

const handleFileSelect = (event: Event) => {
  const target = event.target as HTMLInputElement
  const file = target.files?.[0]
  if (file) {
    processFile(file)
  }
}

const hasFiles = (event: DragEvent) => {
  const types = event.dataTransfer?.types
  if (!types) return false
  return Array.from(types).includes('Files')
}

const handleDragEnter = (event: DragEvent) => {
  if (!hasFiles(event)) return
  dragCounter.value += 1
  isDragging.value = true
}

const handleDragOver = (event: DragEvent) => {
  if (!hasFiles(event)) return
  event.dataTransfer!.dropEffect = 'copy'
  isDragging.value = true
}

const handleDragLeave = (event: DragEvent) => {
  if (!hasFiles(event)) return
  dragCounter.value = Math.max(0, dragCounter.value - 1)
  if (dragCounter.value === 0) {
    isDragging.value = false
  }
}

const handleDrop = (event: DragEvent) => {
  if (!hasFiles(event)) return
  event.preventDefault()
  dragCounter.value = 0
  isDragging.value = false
  const file = event.dataTransfer?.files[0]
  if (file && file.type.startsWith('image/')) {
    processFile(file)
  } else {
    message.warning(t('Please upload an image file'))
  }
}

const processFile = async (file: File) => {
  const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
  if (!validTypes.includes(file.type)) {
    message.warning(t('Unsupported image format. Please use JPG, PNG, or WebP'))
    return
  }

  const maxSize = 10 * 1024 * 1024
  if (file.size > maxSize) {
    message.warning(t('Image size exceeds 10MB limit'))
    return
  }

  try {
    const compressedFile = await compressImageFile(file, {
      maxWidth: 1024,
      maxHeight: 1024,
      quality: 0.92,
      mode: 'contain'
    })
    
    // Check if image has transparency
    const hasTransparency = await checkImageTransparency(compressedFile)
    
    if (!hasTransparency) {
      // No transparency detected, auto remove background silently
      processing.value = true
      
      try {
        const removedBgImage = await removeBackground(compressedFile)
        if (removedBgImage) {
          uploadedImage.value = removedBgImage
          resultImage.value = ''
          currentHistoryIndex.value = -1
          
          const reader = new FileReader()
          reader.onload = async (e) => {
            uploadedImageUrl.value = e.target?.result as string
            await extractDominantColorAndInitialize(uploadedImageUrl.value)
          }
          reader.readAsDataURL(removedBgImage)
          return
        }
      } catch (error) {
        console.error('Auto remove background failed:', error)
        // Fall through to use original image silently
      }
    }
    
    // Use image directly (already has transparency or removal failed)
    uploadedImage.value = compressedFile
    resultImage.value = ''
    currentHistoryIndex.value = -1
    
    const reader = new FileReader()
    reader.onload = async (e) => {
      uploadedImageUrl.value = e.target?.result as string
      await extractDominantColorAndInitialize(uploadedImageUrl.value)
    }
    reader.readAsDataURL(compressedFile)
  } catch (error) {
    message.error(t('Image processing failed, please try again'))
    processing.value = false
  }
}

// Check if image has transparent pixels
const checkImageTransparency = (file: File): Promise<boolean> => {
  return new Promise((resolve) => {
    const reader = new FileReader()
    reader.onload = (e) => {
      const img = new Image()
      img.onload = () => {
        const canvas = document.createElement('canvas')
        canvas.width = img.width
        canvas.height = img.height
        const ctx = canvas.getContext('2d')
        
        if (!ctx) {
          resolve(false)
          return
        }
        
        ctx.drawImage(img, 0, 0)
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
        const pixels = imageData.data
        
        // Sample pixels to check for transparency
        // Check every 10th pixel for performance
        let transparentPixels = 0
        const sampleRate = 10
        
        for (let i = 3; i < pixels.length; i += 4 * sampleRate) {
          if (pixels[i] < 255) {
            transparentPixels++
          }
        }
        
        // If more than 1% pixels are transparent, consider it has transparency
        const totalSampledPixels = Math.floor(pixels.length / (4 * sampleRate))
        const transparencyRatio = transparentPixels / totalSampledPixels
        
        resolve(transparencyRatio > 0.01)
      }
      img.onerror = () => resolve(false)
      img.src = e.target?.result as string
    }
    reader.onerror = () => resolve(false)
    reader.readAsDataURL(file)
  })
}

// Remove background using API
const removeBackground = async (file: File): Promise<File | null> => {
  if (!authStore.isLoggedIn) {
    message.warning(t('Please login first to use auto remove background feature'))
    return null
  }

  try {
    const formData = new FormData()
    formData.append('file', file)

    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), 180000)
    
    const response = await fetch('/api/v1/tools/rmbg/file/free', {
      method: 'POST',
      body: formData,
      signal: controller.signal
    })
    
    clearTimeout(timeoutId)

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    const reader = response.body?.getReader()
    const decoder = new TextDecoder()
    let buffer = ''

    if (!reader) {
      throw new Error('Response body is not readable')
    }

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      buffer += decoder.decode(value, { stream: true })
      const lines = buffer.split('\n')
      buffer = lines.pop() || ''

      for (const line of lines) {
        if (line.trim()) {
          try {
            const result = JSON.parse(line.trim())
            if (result.status === 'success' && result.image_url) {
              // Download the image and convert to File
              const imgResponse = await fetch(result.image_url)
              const blob = await imgResponse.blob()
              const removedFile = new File([blob], 'removed-bg.png', { type: 'image/png' })
              return removedFile
            }
          } catch (parseError) {
            // Continue
          }
        }
      }
    }

    // Process last line
    if (buffer.trim()) {
      try {
        const result = JSON.parse(buffer.trim())
        if (result.status === 'success' && result.image_url) {
          const imgResponse = await fetch(result.image_url)
          const blob = await imgResponse.blob()
          const removedFile = new File([blob], 'removed-bg.png', { type: 'image/png' })
          return removedFile
        }
      } catch (parseError) {
        // Continue
      }
    }

    return null
  } catch (error: any) {
    console.error('Remove background error:', error)
    throw error
  }
}

const initializeCanvas = async () => {
  if (!canvasRef.value || !uploadedImageUrl.value) {
    processing.value = false
    return
  }
  
  try {
    await nextTick()
    
    // Clean up old canvas
    if (fabricCanvas) {
      fabricCanvas.dispose()
      fabricCanvas = null
    }
    
    // Create new fabric canvas
    fabricCanvas = new Canvas(canvasRef.value, {
      selection: false
    })
    
    // Load image using fabric v7 API
    const img = await FabricImage.fromURL(uploadedImageUrl.value, {
      crossOrigin: 'anonymous'
    })
    
    if (!img || !fabricCanvas) {
      processing.value = false
      return
    }
    
    // Get image natural dimensions (already compressed to max 1024x1024)
    const imgWidth = img.width || 1
    const imgHeight = img.height || 1
    
    // Set canvas to actual image dimensions (no scaling)
    // This ensures download maintains the same size as upload
    fabricCanvas.setDimensions({ width: imgWidth, height: imgHeight })
    
    img.set({
      left: 0,
      top: 0,
      selectable: false,
      evented: false,
      scaleX: 1,
      scaleY: 1
    })
    
    fabricCanvas.add(img)
    fabricCanvas.centerObject(img)
    fabricCanvas.renderAll()
    
    // Adjust visual display size to fit container
    adjustCanvasSize()
    
    // Auto-apply default background color
    await nextTick()
    applyBackgroundSilent()
    
    processing.value = false
  } catch (error) {
    console.error('Canvas initialization error:', error)
    message.error(t('Failed to load image'))
    processing.value = false
  }
}

const validateHexColor = () => {
  const hex = backgroundColor.value.trim()
  const isValid = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(hex)
  isColorValid.value = isValid
  return isValid
}

// Auto-apply background when color changes
const onColorChange = () => {
  if (validateHexColor()) {
    debouncedApplyBackground()
  }
}

const onHexInputChange = () => {
  // Validate and auto-format hex input
  let hex = backgroundColor.value.trim()
  if (!hex.startsWith('#')) {
    hex = '#' + hex
    backgroundColor.value = hex
  }
  if (validateHexColor()) {
    debouncedApplyBackground()
  }
}

const onHexInputBlur = () => {
  // Format hex on blur
  let hex = backgroundColor.value.trim().toUpperCase()
  if (!hex.startsWith('#')) {
    hex = '#' + hex
  }
  // Expand 3-digit to 6-digit
  if (/^#[A-Fa-f0-9]{3}$/.test(hex)) {
    hex = '#' + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3]
  }
  backgroundColor.value = hex
  if (validateHexColor()) {
    applyBackgroundSilent()
  }
}

const selectShade = (color: string) => {
  backgroundColor.value = color
  applyBackgroundSilent()
  // Auto-close mobile color picker on mobile
  if (window.innerWidth <= 768) {
    closeMobileColorPicker()
  }
}

const selectCommonColor = (color: string) => {
  backgroundColor.value = color
  applyBackgroundSilent()
  // Auto-close mobile color picker on mobile
  if (window.innerWidth <= 768) {
    closeMobileColorPicker()
  }
}

const toggleMobileColorPicker = () => {
  showMobileColorPicker.value = !showMobileColorPicker.value
  // Prevent body scroll when sheet is open
  if (showMobileColorPicker.value) {
    document.body.style.overflow = 'hidden'
  } else {
    document.body.style.overflow = ''
  }
}

const closeMobileColorPicker = () => {
  showMobileColorPicker.value = false
  document.body.style.overflow = ''
}

const debouncedApplyBackground = () => {
  if (colorChangeDebounceTimer) {
    clearTimeout(colorChangeDebounceTimer)
  }
  colorChangeDebounceTimer = setTimeout(() => {
    applyBackgroundSilent()
  }, 150)
}

const applyBackgroundSilent = async () => {
  if (!fabricCanvas || !uploadedImage.value) return
  if (!validateHexColor()) return

  try {
    const objects = fabricCanvas.getObjects()
    if (objects.length === 0) return
    
    // Set background color using fabric v7 API
    fabricCanvas.backgroundColor = backgroundColor.value
    fabricCanvas.renderAll()
    
    // Export to data URL using fabric v7 API
    const dataUrl = fabricCanvas.toDataURL({
      format: 'png',
      quality: 0.95,
      multiplier: 1
    })
    
    resultImage.value = dataUrl
    await cacheResultImage(dataUrl)
    
    // Update history
    if (currentHistoryIndex.value === -1 && uploadedImage.value && uploadedImageUrl.value) {
      const historyItem: HistoryItem = {
        id: `history-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        originalImageUrl: uploadedImageUrl.value,
        originalImageFile: uploadedImage.value,
        resultImage: dataUrl,
        backgroundColor: backgroundColor.value,
        timestamp: Date.now()
      }
      historyList.value.unshift(historyItem)
      currentHistoryIndex.value = 0
    } else if (currentHistoryIndex.value >= 0) {
      historyList.value[currentHistoryIndex.value].resultImage = dataUrl
      historyList.value[currentHistoryIndex.value].backgroundColor = backgroundColor.value
    }
  } catch (error: any) {
    console.error('Apply background error:', error)
  }
}

const resetUpload = () => {
  if (uploadedImageUrl.value && uploadedImageUrl.value.startsWith('blob:')) {
    URL.revokeObjectURL(uploadedImageUrl.value)
  }
  if (resultImageBlobUrl.value) {
    URL.revokeObjectURL(resultImageBlobUrl.value)
    resultImageBlobUrl.value = ''
  }
  if (fabricCanvas) {
    fabricCanvas.dispose()
    fabricCanvas = null
  }
  uploadedImage.value = null
  uploadedImageUrl.value = ''
  resultImage.value = ''
  imageUrl.value = ''
  backgroundColor.value = '#FFFFFF'
  currentHistoryIndex.value = -1
  if (fileInputRef.value) {
    fileInputRef.value.value = ''
  }
}

// Clear background (restore to transparent)
const clearBackground = async () => {
  if (!fabricCanvas || !uploadedImage.value || !uploadedImageUrl.value) return
  
  try {
    processing.value = true
    
    // Clean up old canvas
    if (fabricCanvas) {
      fabricCanvas.dispose()
      fabricCanvas = null
    }
    
    // Create new fabric canvas WITHOUT background
    fabricCanvas = new Canvas(canvasRef.value, {
      selection: false,
      backgroundColor: null
    })
    
    // Load the original uploaded image (already has transparency)
    const img = await FabricImage.fromURL(uploadedImageUrl.value, {
      crossOrigin: 'anonymous'
    })
    
    if (!img || !fabricCanvas) {
      processing.value = false
      return
    }
    
    // Get image natural dimensions
    const imgWidth = img.width || 1
    const imgHeight = img.height || 1
    
    // Set canvas to actual image dimensions
    fabricCanvas.setDimensions({ width: imgWidth, height: imgHeight })
    
    img.set({
      left: 0,
      top: 0,
      selectable: false,
      evented: false,
      scaleX: 1,
      scaleY: 1
    })
    
    // Ensure no background color (null for transparent)
    fabricCanvas.backgroundColor = null
    
    fabricCanvas.add(img)
    fabricCanvas.centerObject(img)
    fabricCanvas.renderAll()
    
    // Adjust visual display size to fit container
    adjustCanvasSize()
    
    // Export result with transparent background
    const dataUrl = fabricCanvas.toDataURL({
      format: 'png',
      quality: 0.95,
      multiplier: 1
    })
    
    resultImage.value = dataUrl
    await cacheResultImage(dataUrl)
    
    // Clear hex input by setting empty string (placeholder will show)
    backgroundColor.value = ''
    
    // Update or create history item
    if (currentHistoryIndex.value === -1) {
      const historyItem: HistoryItem = {
        id: `history-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        originalImageUrl: uploadedImageUrl.value,
        originalImageFile: uploadedImage.value,
        resultImage: dataUrl,
        backgroundColor: '',
        timestamp: Date.now()
      }
      historyList.value.unshift(historyItem)
      currentHistoryIndex.value = 0
    } else if (currentHistoryIndex.value >= 0) {
      historyList.value[currentHistoryIndex.value].resultImage = dataUrl
      historyList.value[currentHistoryIndex.value].backgroundColor = ''
    }
    
    processing.value = false
  } catch (error: any) {
    console.error('Clear background error:', error)
    message.error(t('Failed to clear background'))
    processing.value = false
  }
}

// Convert RGB to light pastel color using OKLCH (adaptive approach)
const rgbToLightPastel = (r: number, g: number, b: number): string => {
  // Convert RGB to hex first
  const rHex = Math.round(r).toString(16).padStart(2, '0')
  const gHex = Math.round(g).toString(16).padStart(2, '0')
  const bHex = Math.round(b).toString(16).padStart(2, '0')
  const hexColor = `#${rHex}${gHex}${bHex}`
  
  // Convert to OKLCH
  const { l, c, h } = hexToOklch(hexColor)
  
  // Adaptive lightness: ensure it's in the light range (0.85-0.95)
  // If original is already light, keep it; if dark, lighten it
  let pastelL: number
  if (l >= 0.85) {
    // Already light enough, use original
    pastelL = l
  } else if (l >= 0.7) {
    // Medium brightness, lighten moderately
    pastelL = 0.88
  } else {
    // Dark color, lighten significantly
    pastelL = 0.92
  }
  
  // Adaptive chroma: maintain color character while keeping it soft
  // Reduce chroma but keep some saturation for color recognition
  const pastelC = Math.min(c * 0.5, 0.12) // Keep up to 50% chroma, max 0.12
  
  return oklchToHex(pastelL, pastelC, h)
}

// Extract dominant color from image using color-thief
const extractDominantColorAndInitialize = async (imageDataUrl: string) => {
  try {
    const img = new Image()
    img.crossOrigin = 'Anonymous'
    
    await new Promise((resolve, reject) => {
      img.onload = resolve
      img.onerror = reject
      img.src = imageDataUrl
    })
    
    // Extract dominant color using color-thief
    const dominantColor = colorThief.getColor(img)
    
    if (dominantColor && Array.isArray(dominantColor) && dominantColor.length === 3) {
      // Convert to light pastel color with same hue
      const [r, g, b] = dominantColor
      backgroundColor.value = rgbToLightPastel(r, g, b)
    }
    
    await initializeCanvas()
  } catch (error) {
    console.error('Failed to extract dominant color:', error)
    // Fallback to default white background
    backgroundColor.value = '#FFFFFF'
    await initializeCanvas()
  }
}

const selectHistoryItem = async (index: number) => {
  if (index < 0 || index >= historyList.value.length) return
  
  const item = historyList.value[index]
  currentHistoryIndex.value = index
  uploadedImageUrl.value = item.originalImageUrl
  resultImage.value = item.resultImage
  backgroundColor.value = item.backgroundColor
  uploadedImage.value = item.originalImageFile
  
  if (item.resultImage) {
    await cacheResultImage(item.resultImage)
  }
  
  // Re-render canvas with the saved result
  await initializeCanvasFromResult(item)
}

const initializeCanvasWithBackground = async (bgColor: string) => {
  if (!canvasRef.value || !uploadedImageUrl.value) {
    processing.value = false
    return
  }
  
  try {
    await nextTick()
    
    // Clean up old canvas
    if (fabricCanvas) {
      fabricCanvas.dispose()
      fabricCanvas = null
    }
    
    // Create new fabric canvas
    fabricCanvas = new Canvas(canvasRef.value, {
      selection: false
    })
    
    // Load image using fabric v7 API
    const img = await FabricImage.fromURL(uploadedImageUrl.value, {
      crossOrigin: 'anonymous'
    })
    
    if (!img || !fabricCanvas) {
      processing.value = false
      return
    }
    
    // Get image natural dimensions (already compressed to max 1024x1024)
    const imgWidth = img.width || 1
    const imgHeight = img.height || 1
    
    // Set canvas to actual image dimensions (no scaling)
    fabricCanvas.setDimensions({ width: imgWidth, height: imgHeight })
    
    img.set({
      left: 0,
      top: 0,
      selectable: false,
      evented: false,
      scaleX: 1,
      scaleY: 1
    })
    
    // Apply saved background color if provided
    if (bgColor) {
      fabricCanvas.backgroundColor = bgColor
    }
    
    fabricCanvas.add(img)
    fabricCanvas.centerObject(img)
    fabricCanvas.renderAll()
    
    // Adjust visual display size to fit container
    adjustCanvasSize()
    
    processing.value = false
  } catch (error) {
    console.error('Canvas initialization error:', error)
    message.error(t('Failed to load image'))
    processing.value = false
  }
}

// Initialize canvas from saved result (for history items)
const initializeCanvasFromResult = async (item: HistoryItem) => {
  if (!canvasRef.value || !item.resultImage) {
    processing.value = false
    return
  }
  
  try {
    await nextTick()
    
    // Clean up old canvas
    if (fabricCanvas) {
      fabricCanvas.dispose()
      fabricCanvas = null
    }
    
    // Create new fabric canvas
    fabricCanvas = new Canvas(canvasRef.value, {
      selection: false
    })
    
    // Load the result image directly
    const resultImg = await FabricImage.fromURL(item.resultImage, {
      crossOrigin: 'anonymous'
    })
    
    if (!resultImg || !fabricCanvas) {
      processing.value = false
      return
    }
    
    // Get image natural dimensions
    const imgWidth = resultImg.width || 1
    const imgHeight = resultImg.height || 1
    
    // Set canvas to actual image dimensions
    fabricCanvas.setDimensions({ width: imgWidth, height: imgHeight })
    
    resultImg.set({
      left: 0,
      top: 0,
      selectable: false,
      evented: false,
      scaleX: 1,
      scaleY: 1
    })
    
    fabricCanvas.add(resultImg)
    fabricCanvas.centerObject(resultImg)
    fabricCanvas.renderAll()
    
    // Adjust visual display size to fit container
    adjustCanvasSize()
    
    processing.value = false
  } catch (error) {
    console.error('Canvas initialization from result error:', error)
    message.error(t('Failed to load image'))
    processing.value = false
  }
}

const getHistoryThumbnailUrl = (item: HistoryItem): string => {
  if (item.resultImage) {
    return item.resultImage
  }
  return item.originalImageUrl
}

const cacheResultImage = async (imageUrl: string) => {
  try {
    if (resultImageBlobUrl.value) {
      URL.revokeObjectURL(resultImageBlobUrl.value)
      resultImageBlobUrl.value = ''
    }
    
    const response = await fetch(imageUrl)
    if (!response.ok) {
      throw new Error(`Failed to fetch image: ${response.status}`)
    }
    const blob = await response.blob()
    resultImageBlobUrl.value = URL.createObjectURL(blob)
  } catch (error) {
    // Cache failure doesn't affect display
  }
}

const handleDownload = async () => {
  if (!resultImage.value) return

  try {
    let blobUrl = resultImageBlobUrl.value
    
    if (!blobUrl) {
      const response = await fetch(resultImage.value)
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }
      const blob = await response.blob()
      blobUrl = URL.createObjectURL(blob)
      resultImageBlobUrl.value = blobUrl
    }
    
    const link = document.createElement('a')
    link.href = blobUrl
    link.download = `background-added-${Date.now()}.png`
    link.style.display = 'none'
    
    document.body.appendChild(link)
    link.click()
    
    requestAnimationFrame(() => {
      document.body.removeChild(link)
    })
  } catch (error: any) {
    message.error(t('Failed to download image'))
  }
}

const removeHistoryItem = (index: number) => {
  if (index < 0 || index >= historyList.value.length) return

  const removingCurrent = currentHistoryIndex.value === index
  historyList.value.splice(index, 1)

  if (historyList.value.length === 0) {
    currentHistoryIndex.value = -1
    resetUpload()
    return
  }

  if (removingCurrent) {
    const nextIndex = Math.min(index, historyList.value.length - 1)
    selectHistoryItem(nextIndex)
  } else if (currentHistoryIndex.value > index) {
    currentHistoryIndex.value -= 1
  }
}
</script>

<style scoped lang="scss">
.add-background-page {
  position: relative;
  width: calc(100% + 40px);
  height: calc(100vh - 64px - 40px);
  margin: -20px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.global-drag-overlay {
  position: fixed;
  inset: 0;
  background: radial-gradient(circle at center, rgba(248, 250, 252, 0.92), rgba(248, 250, 252, 0.82));
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  pointer-events: none;
  animation: fade-in 0.15s ease;

  &::before {
    content: '';
    position: absolute;
    width: 55vmax;
    height: 55vmax;
    border-radius: 35%;
    border: 1px solid rgba(148, 163, 184, 0.25);
    animation: rotate 12s linear infinite;
    pointer-events: none;
  }
}

.overlay-content {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 18px 30px;
  color: #0f172a;
  font-size: 18px;
  font-weight: 600;
  text-align: center;
  letter-spacing: 0.04em;

  &::before {
    content: '';
    position: absolute;
    inset: -10px;
    border-radius: 999px;
    border: 1px solid rgba(79, 70, 229, 0.35);
    opacity: 0.7;
    animation: pulse-line 1.8s ease-out infinite;
  }

  &::after {
    content: '';
    position: absolute;
    inset: -28px 16px;
    border-radius: 999px;
    background: linear-gradient(90deg, rgba(59, 130, 246, 0), rgba(59, 130, 246, 0.24), rgba(59, 130, 246, 0));
    filter: blur(10px);
    opacity: 0.65;
    animation: shimmer 2.4s linear infinite;
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
    transform: scale(0.98);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes rotate {
  to {
    transform: rotate(360deg);
  }
}

@keyframes pulse-line {
  0% {
    transform: scale(0.9);
    opacity: 0;
  }
  50% {
    transform: scale(1);
    opacity: 0.5;
  }
  100% {
    transform: scale(1.05);
    opacity: 0;
  }
}

@keyframes shimmer {
  0% {
    opacity: 0.2;
  }
  50% {
    opacity: 0.7;
  }
  100% {
    opacity: 0.2;
  }
}

.page-header {
  flex-shrink: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding: 12px 36px;
  gap: 16px;
  transition: padding-right 0.3s ease;
}

.page-header.has-sidebar {
  padding-right: 316px;
}

.page-header h2 {
  font-size: 20px;
  font-weight: 600;
  color: #1f2937;
  margin: 0;
  flex-shrink: 0;
}

/* Header Color Shades */
.header-color-shades {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  justify-content: center;
}

.shade-item {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  cursor: pointer;
  border: 2px solid transparent;
  transition: all 0.15s ease;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0; /* Prevent flex compression */
  
  &:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    border-color: #1fc76f;
  }
  
  &.active {
    border-color: #1fc76f;
    
    &::after {
      content: '';
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%) rotate(45deg);
      width: 8px;
      height: 14px;
      border: solid #1fc76f;
      border-width: 0 2.5px 2.5px 0;
    }
  }
}

.page-content {
  flex: 1;
  width: 100%;
  max-width: 100%;
  margin: 0 auto;
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  transition: padding-right 0.3s ease;
}

.page-content.has-sidebar {
  padding-right: 296px; /* Make space for right sidebar + padding */
}

.page-header h2 {
  font-size: 20px;
  font-weight: 600;
  color: #1f2937;
  margin: 0;
  flex-shrink: 0;
}

.toolbar-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}

.toolbar-btn {
  width: 36px;
  height: 36px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: white;
  color: #64748b;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;

  i {
    font-size: 14px;
  }

  &:hover {
    border-color: #1fc76f;
    color: #1fc76f;
    background: #f0fdf4;
  }

  &:active {
    transform: scale(0.95);
  }
}

.tool-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
}

.main-area {
  flex: 1;
  display: flex;
  gap: 0;
  overflow: hidden;
  min-height: 0;
  position: relative;
}

.upload-section {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  background: white;
  border-radius: 0;
  padding: 24px;
  box-shadow: none;
  border: none;
}

.upload-section:has(.preview-section) {
  padding: 0;
  border-radius: 0;
  border: none;
  box-shadow: none;
  background: transparent;
}

.upload-area {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed #cbd5e1;
  border-radius: 12px;
  padding: 48px 32px;
  text-align: center;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  background: #fafbfc;
  min-height: 0;
  position: relative;

  &:hover {
    border-color: #1fc76f;
    background: #f0fdf4;
  }

  &.dragging {
    border-color: #1fc76f;
    background: #ecfdf5;
    border-style: solid;
  }
}

.upload-processing-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.95);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  border-radius: 12px;
  backdrop-filter: blur(4px);
  z-index: 10;

  p {
    font-size: 14px;
    color: #64748b;
    margin: 0;
    font-weight: 500;
  }
}

.upload-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  width: 100%;
  max-width: 500px;
}

.upload-btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 20px 40px;
  background: #e6f8f0;
  color: #0d684b;
  border: 1px solid #1fc76f;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  white-space: nowrap;

  i {
    font-size: 16px;
  }

  &:hover:not(:disabled) {
    background: #dcfce7;
    border-color: #1fc76f;
    color: #166534;
    transform: translateY(-1px);
    box-shadow: 0 2px 8px rgba(31, 199, 111, 0.15);
  }

  &:active:not(:disabled) {
    background: #1fc76f;
    border-color: #1fc76f;
    color: white;
    transform: translateY(0);
    box-shadow: 0 1px 4px rgba(31, 199, 111, 0.2);
  }

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    background: #f1f5f9;
    border-color: #e2e8f0;
    color: #94a3b8;
  }

  &:disabled:hover {
    transform: none;
    box-shadow: none;
  }
}

.divider {
  display: flex;
  align-items: center;
  color: #94a3b8;
  font-size: 13px;
  width: 100%;
  max-width: 300px;

  &::before,
  &::after {
    content: '';
    flex: 1;
    height: 1px;
    background: #e5e7eb;
  }

  span {
    padding: 0 12px;
  }
}

.upload-hint {
  font-size: 13px;
  color: #64748b;
  margin: 0;
  line-height: 1.5;
}

.upload-format-hint {
  font-size: 12px;
  color: #94a3b8;
  margin: 0;
}

.url-input-wrapper {
  width: 100%;
  max-width: 500px;
}

.url-input {
  width: 100%;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 12px 16px;
  font-size: 14px;
  color: #1f2937;
  background: white;
  outline: none;
  transition: all 0.2s ease;

  &::placeholder {
    color: #94a3b8;
  }

  &:focus {
    border-color: #1fc76f;
    box-shadow: 0 0 0 3px rgba(31, 199, 111, 0.1);
  }

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    background: #f8fafc;
  }
}

.preview-section {
  flex: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  min-height: 0;
  padding: 24px;
  background: #fafbfc;
}

.canvas-container {
  position: relative;
  display: inline-block;
  margin: auto;
  /* Checkerboard pattern for transparent background */
  background-image:
    linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
    linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #f1f5f9 75%),
    linear-gradient(-45deg, transparent 75%, #f1f5f9 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
  background-color: #ffffff;
  padding: 0;
  border-radius: 8px;
}

.preview-canvas {
  display: block;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  image-rendering: -webkit-optimize-contrast;
  image-rendering: crisp-edges;
  /* Ensure canvas itself has no background */
  background: transparent;
}

/* Right Sidebar */
.right-sidebar {
  position: fixed;
  right: 0;
  top: 64px;
  bottom: 0;
  width: 280px;
  background: white;
  border-left: 1px solid #e5e7eb;
  box-shadow: -2px 0 8px rgba(0, 0, 0, 0.05);
  z-index: 100;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  
  :deep(.n-tabs) {
    height: 100%;
    display: flex;
    flex-direction: column;
    
    .n-tabs-nav {
      padding: 12px 20px 0;
    }
    
    .n-tabs-tab {
      font-weight: 500;
    }
    
    .n-tabs-pane-wrapper {
      flex: 1;
      overflow-y: auto;
      overflow-x: hidden;
    }
  }
}

/* Hide desktop sidebar on mobile */
.desktop-only {
  display: flex;
}

/* Mobile Color Picker Button - Hidden on desktop */
.mobile-color-picker-btn {
  display: none;
}

/* Mobile Bottom Sheet */
.mobile-color-sheet-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 1000;
  display: flex;
  align-items: flex-end;
  backdrop-filter: blur(2px);
}

.mobile-color-sheet {
  width: 100%;
  max-height: 70vh;
  background: white;
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.15);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.sheet-handle {
  width: 40px;
  height: 4px;
  background: #cbd5e1;
  border-radius: 2px;
  margin: 12px auto 8px;
  flex-shrink: 0;
}

.sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-bottom: 1px solid #e5e7eb;
  flex-shrink: 0;

  h4 {
    font-size: 16px;
    font-weight: 600;
    color: #1f2937;
    margin: 0;
  }
}

.sheet-close-btn {
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 8px;
  background: #f3f4f6;
  color: #6b7280;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
  padding: 0;

  i {
    font-size: 16px;
  }

  &:hover {
    background: #e5e7eb;
    color: #374151;
  }

  &:active {
    transform: scale(0.95);
  }
}

.sheet-content {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  -webkit-overflow-scrolling: touch;
  
  :deep(.n-tabs) {
    height: 100%;
    display: flex;
    flex-direction: column;
    
    .n-tabs-nav {
      margin-bottom: 16px;
    }
    
    .n-tabs-tab {
      font-weight: 500;
      font-size: 14px;
    }
    
    .n-tabs-pane-wrapper {
      flex: 1;
      overflow-y: auto;
    }
  }
}

/* Slide up animation */
.slide-up-enter-active,
.slide-up-leave-active {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.slide-up-enter-active .mobile-color-sheet,
.slide-up-leave-active .mobile-color-sheet {
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.slide-up-enter-from,
.slide-up-leave-to {
  opacity: 0;
}

.slide-up-enter-from .mobile-color-sheet,
.slide-up-leave-to .mobile-color-sheet {
  transform: translateY(100%);
}

.slide-up-enter-to .mobile-color-sheet,
.slide-up-leave-from .mobile-color-sheet {
  transform: translateY(0);
}

.sidebar-content {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.color-picker-section,
.favorite-colors-section,
.common-colors-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.section-title {
  font-size: 14px;
  font-weight: 600;
  color: #1f2937;
  margin: 0;
  padding-bottom: 12px;
  border-bottom: 1px solid #e5e7eb;
}

.hex-input-wrapper {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
}

.add-favorite-btn {
  width: 44px;
  height: 44px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: white;
  color: #fbbf24;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
  padding: 0;
  flex-shrink: 0;

  i {
    font-size: 16px;
  }

  &:hover:not(:disabled) {
    border-color: #fbbf24;
    background: #fffbeb;
    transform: scale(1.05);
  }

  &:active:not(:disabled) {
    transform: scale(0.95);
    background: #fef3c7;
  }

  &:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    color: #cbd5e1;
  }
}

.color-picker-container {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.sidebar-color-picker {
  width: 100%;
  height: 60px;
  border: 2px solid #e5e7eb;
  border-radius: 8px;
  cursor: pointer;
  background: none;
  
  &::-webkit-color-swatch-wrapper {
    padding: 0;
  }
  
  &::-webkit-color-swatch {
    border: none;
    border-radius: 6px;
  }
}

.sidebar-color-picker-empty {
  width: 100%;
  height: 60px;
  border: 2px solid #e5e7eb;
  border-radius: 8px;
  cursor: pointer;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Checkerboard pattern for transparent background */
  background-image:
    linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
    linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #f1f5f9 75%),
    linear-gradient(-45deg, transparent 75%, #f1f5f9 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
  background-color: #ffffff;
  transition: all 0.2s ease;
  
  &:hover {
    border-color: #1fc76f;
  }
}

.sidebar-hex-input {
  flex: 1;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 12px;
  font-size: 14px;
  font-family: monospace;
  color: #1f2937;
  background: white;
  outline: none;
  transition: all 0.2s ease;
  text-align: center;
  min-width: 0;

  &:focus {
    border-color: #1fc76f;
    box-shadow: 0 0 0 3px rgba(31, 199, 111, 0.1);
  }

  &.invalid {
    border-color: #ef4444;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
  }
}

.favorite-colors-grid,
.common-colors-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
}

.favorite-colors-grid {
  position: relative;
}

.favorite-color-item,
.common-color-item {
  aspect-ratio: 1;
  border-radius: 8px;
  cursor: pointer;
  border: 2px solid transparent;
  transition: all 0.15s ease;
  position: relative;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: visible;
  
  &:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    border-color: #1fc76f;
  }
  
  &.active {
    border-color: #1fc76f;
    
    &::after {
      content: '';
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%) rotate(45deg);
      width: 8px;
      height: 14px;
      border: solid #1fc76f;
      border-width: 0 2.5px 2.5px 0;
    }
  }
}

.favorite-color-item {
  .remove-favorite-btn {
    position: absolute;
    top: -8px;
    right: -8px;
    width: 20px;
    height: 20px;
    border-radius: 999px;
    border: 2px solid white;
    background: #ef4444;
    color: white;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transform: scale(0.8);
    transition: all 0.2s ease;
    cursor: pointer;
    font-size: 0;
    line-height: 1;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    z-index: 10;

    &::before,
    &::after {
      content: '';
      position: absolute;
      width: 10px;
      height: 2px;
      background: white;
      border-radius: 999px;
    }

    &::before {
      transform: rotate(45deg);
    }

    &::after {
      transform: rotate(-45deg);
    }

    &:hover {
      background: rgba(239, 68, 68, 0.9);
      transform: scale(1);
    }
  }

  &:hover .remove-favorite-btn {
    opacity: 1;
    transform: scale(1);
  }
}

/* Background Images Sections */
.background-images-section,
.custom-backgrounds-section,
.pexels-images-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Pexels Search Box */
.pexels-search-box {
  display: flex;
  gap: 8px;
  margin-bottom: 4px;
}

.pexels-search-input {
  flex: 1;
  padding: 10px 14px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  font-size: 14px;
  color: #1f2937;
  background: white;
  transition: all 0.2s ease;
  outline: none;
  
  &::placeholder {
    color: #9ca3af;
  }
  
  &:focus {
    border-color: #1fc76f;
    box-shadow: 0 0 0 3px rgba(31, 199, 111, 0.1);
  }
}

.pexels-search-btn {
  padding: 10px 16px;
  background: #1fc76f;
  border: none;
  border-radius: 8px;
  color: white;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  
  i {
    font-size: 16px;
  }
  
  &:hover:not(:disabled) {
    background: #1ab866;
    box-shadow: 0 2px 8px rgba(31, 199, 111, 0.3);
  }
  
  &:active:not(:disabled) {
    transform: scale(0.98);
  }
  
  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}

.upload-dragger-content {
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  
  .upload-text {
    font-size: 13px;
    color: #64748b;
    font-weight: 500;
  }
}

.background-images-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

.background-image-item {
  aspect-ratio: 1 / 1;
  border-radius: 8px;
  cursor: pointer;
  border: 2px solid #e5e7eb;
  transition: all 0.15s ease;
  position: relative;
  overflow: visible;
  background: #f3f4f6;
  
  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  
  &:hover {
    border-color: #1fc76f;
    box-shadow: 0 2px 8px rgba(31, 199, 111, 0.3);
    
    .remove-bg-btn {
      opacity: 1;
      transform: scale(1);
    }
  }
  
  .remove-bg-btn {
    position: absolute;
    top: -8px;
    right: -8px;
    width: 20px;
    height: 20px;
    border-radius: 999px;
    border: 2px solid white;
    background: #ef4444;
    color: white;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transform: scale(0.9);
    transition: all 0.2s ease;
    cursor: pointer;
    font-size: 0;
    line-height: 1;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    z-index: 10;

    &::before,
    &::after {
      content: '';
      position: absolute;
      width: 10px;
      height: 2px;
      background: white;
      border-radius: 999px;
    }

    &::before {
      transform: rotate(45deg);
    }

    &::after {
      transform: rotate(-45deg);
    }

    &:hover {
      background: rgba(239, 68, 68, 0.9);
      transform: scale(1);
    }
  }
}

.load-more-container {
  display: flex;
  justify-content: center;
  margin-top: 16px;
}

.load-more-btn {
  padding: 10px 24px;
  background: white;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  color: #64748b;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  
  &:hover {
    border-color: #1fc76f;
    color: #1fc76f;
    background: #f0fdf4;
  }
  
  &:active {
    transform: scale(0.98);
  }
}

.canvas-processing-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.9);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  backdrop-filter: blur(4px);

  p {
    font-size: 14px;
    color: #64748b;
    margin: 0;
  }
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #e5e7eb;
  border-top-color: #1fc76f;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.history-bar {
  flex-shrink: 0;
  width: 100%;
  background: transparent;
  display: flex;
  justify-content: center;
}

.history-scroll-container {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  overflow-y: visible;
  padding: 10px;
  scrollbar-width: thin;
  scrollbar-color: #cbd5e1 transparent;

  &::-webkit-scrollbar {
    height: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
  }

  &::-webkit-scrollbar-thumb {
    background: #cbd5e1;
    border-radius: 3px;

    &:hover {
      background: #94a3b8;
    }
  }
}

.history-item {
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s ease;
  position: relative;

  &.add-button {
    background: white;
    border: 2px dashed #cbd5e1;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #64748b;
    transition: all 0.2s ease;
    padding: 0;
    margin: 0;
    outline: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;

    i {
      font-size: 24px;
      pointer-events: none;
    }

    &:hover {
      border-color: #1fc76f;
      color: #1fc76f;
      background: #f0fdf4;
    }

    &:active {
      transform: scale(0.95);
    }

    &:focus {
      outline: none;
      border-color: #1fc76f;
    }
  }

  &.active {
    .history-thumbnail {
      border-color: #1fc76f;
      box-shadow: 0 0 10px rgba(31, 199, 111, 0.35);
    }
  }
}

.history-thumbnail {
  width: 100%;
  height: 100%;
  border-radius: 8px;
  border: 2px solid #e5e7eb;
  overflow: visible;
  position: relative;
  background: 
    linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
    linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #f1f5f9 75%),
    linear-gradient(-45deg, transparent 75%, #f1f5f9 75%);
  background-size: 10px 10px;
  background-position: 0 0, 0 5px, 5px -5px, -5px 0px;
  transition: all 0.2s ease;

  img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    border-radius: 6px;
  }

  &:hover {
    border-color: #1fc76f;
  }

  .history-delete-btn {
    position: absolute;
    top: -8px;
    right: -8px;
    width: 20px;
    height: 20px;
    border-radius: 999px;
    border: 2px solid white;
    background: #ef4444;
    color: white;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transform: scale(0.9);
    transition: all 0.2s ease;
    cursor: pointer;
    font-size: 0;
    line-height: 1;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    z-index: 10;

    &::before,
    &::after {
      content: '';
      position: absolute;
      width: 10px;
      height: 2px;
      background: white;
      border-radius: 999px;
    }

    &::before {
      transform: rotate(45deg);
    }

    &::after {
      transform: rotate(-45deg);
    }

    &:hover {
      background: rgba(239, 68, 68, 0.85);
    }
  }

  &:hover .history-delete-btn {
    opacity: 1;
    transform: scale(1);
  }
}

@media (max-width: 768px) {
  /* Hide desktop sidebar on mobile */
  .desktop-only {
    display: none !important;
  }
  
  /* Show mobile color picker button */
  .mobile-color-picker-btn {
    display: flex !important;
  }

  .page-header {
    padding: 10px 12px;
    flex-wrap: wrap;
    padding-right: 12px !important; /* Reset padding on mobile */
  }

  .page-header h2 {
    font-size: 18px;
    width: auto;
    flex-shrink: 0;
  }

  .header-color-shades {
    width: 100%;
    justify-content: center; /* Center align the color shades */
    order: 3;
    gap: 4px;
    padding: 8px 0; /* Add vertical padding for separation */
  }
  
  .shade-item {
    width: 28px;
    height: 28px;
    flex-shrink: 0; /* Prevent flex compression */
    
    &.active::after {
      width: 6px;
      height: 11px;
      border-width: 0 2px 2px 0;
    }
  }

  .toolbar-actions {
    order: 2;
    margin-left: auto;
  }

  .page-content {
    padding: 8px 12px;
    padding-right: 12px !important;
    padding-bottom: 12px !important; /* Remove bottom padding since no fixed sidebar */
  }

  .upload-section {
    padding: 16px;
  }

  .upload-area {
    padding: 32px 16px;
  }

  .upload-content {
    gap: 14px;
    max-width: 100%;
  }

  .upload-btn {
    width: 100%;
    justify-content: center;
    padding: 18px 32px;
    font-size: 15px;

    i {
      font-size: 15px;
    }
  }

  .divider {
    max-width: 100%;
  }

  .url-input-wrapper {
    width: 100%;
    max-width: 100%;
  }

  .url-input {
    font-size: 12px;
    padding: 6px 10px;
  }

  .preview-section {
    padding: 12px;
    min-height: 400px;
  }

  .canvas-container {
    padding: 0;
    margin: 0 auto; /* Center horizontally */
    display: block; /* Ensure proper centering */
  }

  .hex-input-wrapper {
    flex-direction: row;
  }

  .add-favorite-btn {
    width: 44px;
    height: 44px;
    
    i {
      font-size: 18px;
    }
  }

  .sidebar-hex-input {
    font-size: 13px;
    padding: 10px;
  }

  .favorite-colors-grid,
  .common-colors-grid {
    grid-template-columns: repeat(6, 1fr);
    gap: 8px;
  }

  .history-bar {
    padding: 20px 0;
  }

  .history-item {
    width: 48px;
    height: 48px;
  }

  .history-scroll-container {
    gap: 12px;
    padding: 12px 8px;
  }

  .toolbar-btn {
    width: 32px;
    height: 32px;

    i {
      font-size: 12px;
    }
  }

  .main-area {
    flex-direction: column;
  }
}

@media (max-width: 1024px) and (min-width: 769px) {
  .page-header.has-sidebar {
    padding-right: 276px; /* Adjust for smaller sidebar */
  }

  .page-content.has-sidebar {
    padding-right: 256px; /* Adjust for smaller sidebar */
  }

  .right-sidebar {
    width: 240px;
  }
  
  .favorite-colors-grid,
  .common-colors-grid {
    grid-template-columns: repeat(6, 1fr);
    gap: 8px;
  }
}
</style>