<template>
  <div ref="scrollContainer" class="flex items-center max-w-full h-full overflow-x-auto overflow-y-auto relative gap-4 flex-col" @scroll.passive="onScroll">

    <div class="flex w-full flex-1 flex-col sm:flex-row  gap-4">

      <div class="flex relative flex-col w-full px-0 sm:p-4 gap-2 md:gap-4">  

          <div v-if="overlayVisible && (!$root.user || $root.user.settings?.show_overlay_decorations !== false)" class="w-full z-0 mix-blend-overlay pointer-events-none bg-cover bg-center h-full top-0 left-0 absolute" style="background-image: url('https://miocai.com/media/uploads/69c841bdeb61f_New%20Project.png')" aria-hidden="true"></div>


        <!-- Auto Carousel for Event Banner and Poll Banner -->
        <div class="relative w-full min-h-0" :class="[
          (currentSlide === 'poll' || currentSlide === 'welcome') ? '' : 'aspect-video md:aspect-[21/9] lg:aspect-[24/5]'
        ]" style="min-height: 0;">
          <div :class="(currentSlide === 'poll' || currentSlide === 'welcome') ? 'w-full' : 'absolute inset-0 w-full h-full'">
            <!-- Carousel Container -->
            <div class="relative w-full h-full overflow-hidden sm:rounded-lg sm:border sm:border-theme-800 bg-theme-950"
              @mouseenter="onBannerHover" @mouseleave="onBannerLeave"
              @touchstart="onTouchStart" @touchend="onTouchEnd">
              <!-- Welcome Banner Slide (all users, default page 1) -->
              <div class="transition-opacity duration-500"
                :class="currentSlide === 'welcome' ? 'opacity-100 z-20 relative w-full' : 'opacity-0 z-10 pointer-events-none absolute inset-0 w-full h-full'">
                <div class="flex relative w-full overflow-hidden  sm:rounded-lg sm:border sm:border-theme-900/50 min-h-[200px] sm:min-h-[220px] md:min-h-[260px] lg:min-h-[300px]">
                  <!-- Sparkles -->
                  <div class="absolute inset-0 overflow-hidden pointer-events-none">
                    <div class="welcome-sparkle" style="top:12%;left:8%;animation-delay:0s"></div>
                    <div class="welcome-sparkle" style="top:22%;left:78%;animation-delay:0.6s"></div>
                    <div class="welcome-sparkle" style="top:55%;left:4%;animation-delay:1.2s"></div>
                    <div class="welcome-sparkle" style="top:65%;left:88%;animation-delay:1.8s"></div>
                    <div class="welcome-sparkle" style="top:8%;left:48%;animation-delay:0.3s"></div>
                    <div class="welcome-sparkle" style="top:78%;left:38%;animation-delay:2.1s"></div>
                    <div class="welcome-sparkle" style="top:40%;left:92%;animation-delay:0.9s"></div>
                    <div class="welcome-sparkle" style="top:32%;left:22%;animation-delay:1.5s"></div>
                  </div>
                  <div class="absolute -inset-1/2 bg-gradient-to-tr from-transparent via-white/[0.02] to-transparent transform -rotate-12 pointer-events-none"></div>
                  <!-- Content -->
                  <div class="relative z-10 flex flex-col md:flex-row items-center p-4 sm:p-6 md:p-8 gap-4 md:gap-8 w-full">
                    <!-- Left: text -->
                    <div class="flex-1 flex flex-col gap-2 sm:gap-3 text-center md:text-left justify-center">
                      <p class="text-purple-300/80 text-xs sm:text-sm font-medium tracking-wide">
                        {{ $root.user ? $root.translations.home_welcome_back : $root.translations.home_welcome_guest }}
                      </p>
                      <h2 class="text-xl sm:text-2xl lg:text-4xl font-bold text-white leading-tight">
                        {{ $root.translations.home_hero_title }}
                      </h2>
                      <p class="text-gray-400 text-xs sm:text-sm leading-relaxed max-w-lg mx-auto md:mx-0">
                        {{ $root.translations.home_hero_subtitle }}
                      </p>
                      <!-- Buttons -->
                      <div class="flex flex-col sm:flex-row gap-2 justify-center md:justify-start pt-1">
                        <BaseButton
                          :label="$root.translations.home_create_new_chat"
                          type="primary"
                          icon="ph-plus"
                          class="px-4 py-2 text-xs sm:text-sm font-medium"
                          @click.stop="scrollToCharacters"
                        />
                        <BaseButton
                          :label="$root.translations.privacy_policy"
                          type="secondary"
                          icon="ph-shield-check"
                          class="px-4 py-2 text-xs sm:text-sm font-medium"
                          @click.stop="$router.push('/privacy')"
                        />

                      </div>
                      <!-- Stats -->
                      <div class="flex flex-wrap gap-x-4 gap-y-1 pt-2 justify-center md:justify-start">
                        <div class="flex items-center gap-1">
                          <svg class="w-3.5 h-3.5 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
                          <span class="text-white text-xs font-semibold">3.1B+</span>
                          <span class="text-gray-500 text-[10px]">{{ $root.translations.messages }}</span>
                        </div>
                        <div class="flex items-center gap-1">
                          <svg class="w-3.5 h-3.5 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
                          <span class="text-white text-xs font-semibold">50K+</span>
                          <span class="text-gray-500 text-[10px]">{{ $root.translations.home_active_users }}</span>
                        </div>
                        <div class="flex items-center gap-1">
                          <svg class="w-3.5 h-3.5 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
                          <span class="text-white text-xs font-semibold">9K+</span>
                          <span class="text-gray-500 text-[10px]">{{ $root.translations.home_public_bots }}</span>
                        </div>

                      </div>
                    </div>
                    <img loading="lazy" class="hidden opacity-50 blur-xl md:block absolute right-0 top-0 h-full" src="https://miocai.com/media/uploads/69e92de7d83c4_banter.png" alt="">

                    <img loading="lazy" class="hidden md:block absolute right-0 top-0 h-full" src="https://miocai.com/media/uploads/69e92de7d83c4_banter.png" alt="">
                  </div>
                </div>
              </div>

              <!-- Event Banner Slide -->
              <div class="absolute inset-0 w-full h-full transition-opacity duration-500"
                :class="currentSlide === 'event' ? 'opacity-100 z-20' : 'opacity-0 z-10 pointer-events-none'">
                <EventBanner class="w-full h-full" ref="eventBanner" @has-content="onEventContent" />
              </div>

              <!-- Poll Banner Slide (signed-in users) -->
              <div v-if="$root.user" class="transition-opacity duration-500" :class="[
                currentSlide === 'poll' ? 'opacity-100 z-20 relative w-full' : 'opacity-0 z-10 pointer-events-none absolute inset-0 w-full h-full'
              ]">
                <PollBanner class="w-full h-full" ref="pollBanner" @has-content="onPollContent" />
              </div>

              <!-- CTA Banner Slide (guests) -->
              <div v-if="!$root.user" class="absolute inset-0 w-full h-full transition-opacity duration-500"
                :class="currentSlide === 'cta' ? 'opacity-100 z-20' : 'opacity-0 z-10 pointer-events-none'">
                <div @click="$router.push('/documentation/8')"
                  class="flex relative h-full cursor-pointer w-full overflow-hidden bg-theme-950 transition-colors duration-300 hover:bg-theme-900/20">
                  <div class="grid absolute left-0 transform rotate-[9deg] top-0 grid-cols-6 gap-24 h-full">
                    <div v-for="i in 6" :key="i" class="bg-theme-900/10 rounded-3xl w-12 h-full"></div>
                  </div>
                  <div
                    class="flex relative z-20 flex-col text-center md:text-left gap-2 sm:gap-4 p-4 sm:p-6 flex-1 justify-center">
                    <h3 class="lg:text-4xl md:text-3xl text-2xl font-semibold text-theme-200">
                      {{ $root.translations.home_banner_title_prefix }}
                      <span class="text-brand-400 font-extrabold">{{ $root.translations.home_banner_title_highlight
                      }}</span>
                      {{ $root.translations.home_banner_title_suffix }}
                    </h3>
                    <p class="text-theme-400 text-sm">{{ $root.translations.home_banner_subtitle }}</p>
                    <div class="flex gap-2 items-center">
                      <BaseButton :label="$root.translations.learn_more" type="primary" class="mt-2 w-full md:w-1/2"
                        icon="ph-arrow-right" @click.stop="scrollToCharacters">{{ $root.translations.get_started }}
                      </BaseButton>
                    </div>
                  </div>
                  <div class="relative flex-1 hidden md:block">
                    <div
                      class="absolute top-0 right-0 w-full h-full object-center z-20 [mask-image:linear-gradient(to_right,transparent_0%,black_50%,black_100%)] [mask-size:100%_100%] [mask-repeat:no-repeat] bg-cover bg-center"
                      style="background-image: url('https://miocai.com/media/uploads/690d14d44cc7e_paste.png')"
                      aria-hidden="true"></div>
                  </div>
                </div>
              </div>

              <!-- Carousel Indicators -->
              <div v-if="availableSlides.length > 1"
                class="absolute bottom-3 left-1/2 transform -translate-x-1/2 hidden sm:flex gap-2 z-30">
                <button v-for="(slide, idx) in availableSlides" :key="slide" @click="goToSlide(idx)"
                  class="w-2 h-2 rounded-full transition-all duration-300"
                  :class="carouselIndex === idx ? 'bg-brand-400 w-6' : 'bg-theme-600 hover:bg-theme-500'"></button>
              </div>

              <!-- Navigation Arrows -->
              <button v-if="availableSlides.length > 1" @click="prevSlide"
                class="absolute left-2 top-1/2 transform -translate-y-1/2 hidden sm:block p-2 text-theme-300  hover:text-theme-100 transition-colors z-30">
                <i class="ph ph-caret-left text-lg"></i>
              </button>
              <button v-if="availableSlides.length > 1" @click="nextSlide"
                class="absolute right-2 top-1/2 transform -translate-y-1/2 hidden sm:block p-2 text-theme-300  hover:text-theme-100 transition-colors z-30">
                <i class="ph ph-caret-right text-lg"></i>
              </button>
            </div>
          </div>
        </div>
        <!-- /CAROUSEL -->

        <!-- Features Carousel -->
        <div class="relative px-4 sm:px-0">
          <div class="relative overflow-hidden">
            <!-- Carousel Container -->
            <div class="grid grid-cols-1 md:grid-cols-3 gap-2 md:gap-4 transition-all duration-500 ease-in-out"
              @mouseenter="onFeaturesHover" @mouseleave="onFeaturesLeave">
              <info v-for="feature in visibleFeatures" :key="feature.title" :title="feature.title"
                :content="feature.content" :color="feature.color" :icon="feature.icon" />
            </div>



            <!-- Carousel Indicators -->
            <div v-if="features.length > 3" class="flex justify-center gap-2 mt-4">
              <button v-for="(slide, idx) in totalFeatureSlides" :key="idx" @click="goToFeaturesSlide(idx)"
                class="w-2 h-2 rounded-full transition-all duration-300"
                :class="featuresCarouselIndex === idx ? 'bg-brand-400 w-6' : 'bg-theme-600 hover:bg-theme-500'"></button>
            </div>
          </div>
        </div>

      </div>
    </div>
    <div class="p-4 pt-0 w-full" ref="charactersSection">
      <character_browser class=" w-full min-h-[80vh] h-fit" :bigview="true" :search="searchQuery"></character_browser>
    </div>
    <!-- Footer
      -->

    <div class="relative w-full">
                <img loading="lazy" src="https://miocai.com/media/uploads/6a46cb16862d3_paste.png" class="w-full" alt="">
      <!-- <h3 class="text-7xl absolute left-1/2 transform -translate-x-1/2 top-1/2 -translate-y-1/2 text-theme-950 font-extrabold">:P</h3> -->
      <div class="p-4 absolute bottom-0 left-0 w-full bg-theme-950/50 text-center text-theme-100 text-xs">
      <div class="max-w-4xl mx-auto">
        <p class="mb-2">
          &copy; {{ new Date().getFullYear() }} MiocAI. {{ $root.translations.home_footer_rights }}
        </p>
        <div class="flex flex-wrap justify-center gap-4 text-xs">
          <span>{{ $root.translations.home_footer_address_label }} Sachsen Anhalt, Germany</span>
          <span>•</span>
          <span>{{ $root.translations.home_footer_contact_label }} <a href="mailto:support@miocai.com"
              class="text-brand-500 hover:underline">support@miocai.com</a></span>
          <span>•</span>
          <span><a href="/legal" class="text-brand-500 hover:underline">{{ $root.translations.home_footer_terms
          }}</a></span>
        </div>
      </div>
    </div>
    </div>
  </div>
</template>

<script>
import ReviewCarousel from '../../modules/ReviewCarousel.vue'
import DocumentNavbar from '../../core/document-navbar.vue'
import Character from '../../modules/character.vue'
import character_browser from '../../modules/browser.vue'
import BaseButton from '../../modules/BaseButton.vue'
import info from '../../modules/info.vue'
import PollBanner from '../../modules/PollBanner.vue'
import EventBanner from '../../modules/EventBanner.vue'

export default {
  name: "Home",
  components: {
    'document-navbar': DocumentNavbar,
    'character': Character,
    character_browser,
    ReviewCarousel,
    BaseButton,
    info,
    PollBanner,
    EventBanner
  },
  mounted() {
    this.$root.setWorker("characterWorker");
    if (this.$route.params.string) {
      this.searchQuery = this.$route.params.string
    }
    // Start carousel auto-rotation
    this.startCarousel();
    // Start features carousel auto-rotation
    this.startFeaturesCarousel();
    // Check for content availability after a short delay
    this.$nextTick(() => {
      this.checkContentAvailability();
    });
    // Pause auto-rotation when the tab is hidden (not wasteful on mobile/background)
    document.addEventListener("visibilitychange", this._onVisibilityChange);
    // Delay decorative overlay so it doesn't become the LCP element
    setTimeout(() => { this.overlayVisible = true; }, 3000);
  },
  beforeUnmount() {
    this.stopCarousel();
    this.stopFeaturesCarousel();
    document.removeEventListener("visibilitychange", this._onVisibilityChange);
  },
  watch: {
    '$route.params.string'(newVal) {
      if (newVal) {
        this.searchQuery = newVal;
      }
    },
    // Restart carousel when slide count changes
    carouselSlideCount() {
      this.stopCarousel();
      this.startCarousel();
    },
    // Restart features carousel when features count changes
    featuresCount() {
      this.stopFeaturesCarousel();
      this.startFeaturesCarousel();
    }
  },
  computed: {
    // Build array of available slide names
    availableSlides() {
      const slides = ['welcome']; // Always first
      if (this.hasEventBanner) slides.push('event');
      if (this.$root.user && this.hasPollBanner) slides.push('poll');
      else if (!this.$root.user) slides.push('cta');
      return slides;
    },
    // Calculate how many slides are available
    carouselSlideCount() {
      return this.availableSlides.length;
    },
    // Get current slide name based on index
    currentSlide() {
      return this.availableSlides[this.carouselIndex] || this.availableSlides[0];
    },
    // Calculate visible features based on current carousel index
    visibleFeatures() {
      const startIndex = this.featuresCarouselIndex * 3;
      const endIndex = startIndex + 3;
      // If we're at the end and don't have enough items, wrap around
      if (endIndex <= this.features.length) {
        return this.features.slice(startIndex, endIndex);
      } else {
        const firstPart = this.features.slice(startIndex);
        const secondPart = this.features.slice(0, 3 - firstPart.length);
        return [...firstPart, ...secondPart];
      }
    },
    // Calculate total number of slides needed for features carousel
    totalFeatureSlides() {
      return Math.ceil(this.features.length / 3);
    }
  },
  data() {
    return {
      error: null,
      overlayVisible: false,
      searchQuery: "",
      top: [], // No longer needed
      homePageSearchQuery: '', // Add search query data property
      // Carousel state
      carouselIndex: 0,
      carouselInterval: null,
      carouselDelay: 6000, // 6 seconds between slides
      hasEventBanner: true, // Assume true initially, will be updated
      hasPollBanner: true, // Assume true initially, will be updated
      isHoveringBanner: false, // Track if user is hovering over banner
      isAtTop: true, // Track if scroll is at the very top
      touchStartX: null, // Track horizontal swipe start
      touchStartY: null, // Track vertical swipe start
      // Features carousel state
      featuresCarouselIndex: 0,
      featuresCarouselInterval: null,
      featuresCarouselDelay: 5000, // 5 seconds between features slides
      isHoveringFeatures: false, // Track if user is hovering over features
      features: [
        // {
        //   title: "Top AI Models Ready",
        //   content: "Pick from 10+ leading text and image models, including giants with up to 1 trillion parameters.",
        //   color: "brand",
        //   icon: "ghost"
        // },
        // {
        //   title: "Really private.",
        //   content: "Your chats stay encrypted and off the record. Even we can't see them.",
        //   color: "brand",
        //   icon: "shield-check"
        // },
        // {
        //   title: "Unlimited Memory",
        //   content: "Your AI remembers every detail forever. Pin key messages and keep long talks on track.",
        //   color: "brand",
        //   icon: "house"
        // }

        {
          title: this.$root.translations.home_feature_models_title,
          content: this.$root.translations.home_feature_models_content,
          color: "indigo",
          icon: "brain"
        },
        {
          title: this.$root.translations.home_feature_private_title,
          content: this.$root.translations.home_feature_private_content,
          color: "brand",
          icon: "shield-check"
        },
        {
          title: this.$root.translations.home_feature_memory_title,
          content: this.$root.translations.home_feature_memory_content,
          color: "purple",
          icon: "memory"
        },
        {
          title: this.$root.translations.home_feature_interface_title,
          content: this.$root.translations.home_feature_interface_content,
          color: "green",
          icon: "lightning"
        },
        {
          title: this.$root.translations.home_feature_language_title,
          content: this.$root.translations.home_feature_language_content,
          color: "blue",
          icon: "globe"
        },
        {
          title: this.$root.translations.home_feature_personas_title,
          content: this.$root.translations.home_feature_personas_content,
          color: "orange",
          icon: "user-gear"
        },
        {
          title: this.$root.translations.home_feature_images_title,
          content: this.$root.translations.home_feature_images_content,
          color: "pink",
          icon: "image"
        },
        {
          title: this.$root.translations.home_feature_voice_title,
          content: this.$root.translations.home_feature_voice_content,
          color: "teal",
          icon: "microphone"
        },
        {
          title: this.$root.translations.home_feature_self_hosted_title,
          content: this.$root.translations.home_feature_self_hosted_content,
          color: "violet",
          icon: "sparkle"
        },
        {
          title: this.$root.translations.home_feature_creation_title,
          content: this.$root.translations.home_feature_creation_content,
          color: "emerald",
          icon: "user-plus"
        },
        {
          title: this.$root.translations.home_feature_video_title,
          content: this.$root.translations.home_feature_video_content,
          color: "purple",
          icon: "video"
        }


        // {
        //   title: this.$root.translations.home_feature_models_title,
        //   content: this.$root.translations.home_feature_models_content,
        //   color: "red",
        //   icon: "brain"
        // },
        // {
        //   title: this.$root.translations.home_feature_private_title,
        //   content: this.$root.translations.home_feature_private_content,
        //   color: "red",
        //   icon: "shield-check"
        // },
        // {
        //   title: this.$root.translations.home_feature_memory_title,
        //   content: this.$root.translations.home_feature_memory_content,
        //   color: "red",
        //   icon: "memory"
        // }
      ]
    }
  },
  methods: {
    toArray(input) {
      if (Array.isArray(input)) {
        return input;
      } else if (typeof input === 'object' && input !== null) {
        return Object.values(input);
      } else {
        return [];
      }
    },

    homePageSearch(query) { // Add search method for home page
      this.homePageSearchQuery = query;
    },

    scrollToCharacters() {
      const section = this.$refs.charactersSection;
      if (section && section.scrollIntoView) {
        section.scrollIntoView({ behavior: 'smooth', block: 'start' });
      }
    },

    // Carousel methods
    startCarousel() {
      // Disable auto-cycling on mobile
      if (this.isMobile()) return;
      if (this.carouselSlideCount <= 1) return;
      this.stopCarousel();
      this.carouselInterval = setInterval(() => {
        // Only cycle if not hovering and at the top
        if (!this.isHoveringBanner && this.isAtTop) {
          this.nextSlide();
        }
      }, this.carouselDelay);
    },

    stopCarousel() {
      if (this.carouselInterval) {
        clearInterval(this.carouselInterval);
        this.carouselInterval = null;
      }
    },

    nextSlide() {
      if (this.carouselSlideCount <= 1) return;
      this.carouselIndex = (this.carouselIndex + 1) % this.carouselSlideCount;
    },

    prevSlide() {
      if (this.carouselSlideCount <= 1) return;
      this.carouselIndex = (this.carouselIndex - 1 + this.carouselSlideCount) % this.carouselSlideCount;
      // Reset timer when manually navigating
      this.startCarousel();
    },

    goToSlide(index) {
      if (index >= 0 && index < this.carouselSlideCount) {
        this.carouselIndex = index;
        // Reset timer when manually navigating
        this.startCarousel();
      }
    },

    // Content availability handlers
    onEventContent(hasContent) {
      this.hasEventBanner = hasContent;
    },

    onPollContent(hasContent) {
      this.hasPollBanner = hasContent;
    },

    checkContentAvailability() {
      // Check EventBanner content (now emits has-content via onEventContent)
      if (this.$refs.eventBanner) {
        this.hasEventBanner = this.$refs.eventBanner.eventCount > 0;
      }
      // Check PollBanner content
      if (this.$refs.pollBanner) {
        this.hasPollBanner = !!this.$refs.pollBanner.poll;
      }
    },

    // Banner hover handlers
    onBannerHover() {
      this.isHoveringBanner = true;
    },

    onBannerLeave() {
      this.isHoveringBanner = false;
    },

    // Features carousel methods
    startFeaturesCarousel() {
      // Disable auto-cycling on mobile
      if (this.isMobile()) return;
      if (this.features.length <= 3) return;
      this.stopFeaturesCarousel();
      this.featuresCarouselInterval = setInterval(() => {
        // Only cycle if not hovering and at the top
        if (!this.isHoveringFeatures && this.isAtTop) {
          this.nextFeaturesSlide();
        }
      }, this.featuresCarouselDelay);
    },

    stopFeaturesCarousel() {
      if (this.featuresCarouselInterval) {
        clearInterval(this.featuresCarouselInterval);
        this.featuresCarouselInterval = null;
      }
    },

    nextFeaturesSlide() {
      if (this.features.length <= 3) return;
      this.featuresCarouselIndex = (this.featuresCarouselIndex + 1) % this.totalFeatureSlides;
    },

    prevFeaturesSlide() {
      if (this.features.length <= 3) return;
      this.featuresCarouselIndex = (this.featuresCarouselIndex - 1 + this.totalFeatureSlides) % this.totalFeatureSlides;
      // Reset timer when manually navigating
      this.startFeaturesCarousel();
    },

    goToFeaturesSlide(index) {
      if (index >= 0 && index < this.totalFeatureSlides) {
        this.featuresCarouselIndex = index;
        // Reset timer when manually navigating
        this.startFeaturesCarousel();
      }
    },

    // Features hover handlers
    onFeaturesHover() {
      this.isHoveringFeatures = true;
    },

    onFeaturesLeave() {
      this.isHoveringFeatures = false;
    },

    // Scroll handler - pause cycling when not at top
    onScroll() {
      const el = this.$refs.scrollContainer;
      const atTop = el ? el.scrollTop === 0 : true;
      // Only touch reactivity when the value actually changes (avoids a
      // reactive write on every scroll frame).
      if (atTop !== this.isAtTop) this.isAtTop = atTop;
    },

    // Pause/resume carousels when the tab is hidden/visible to avoid wasted ticks
    _onVisibilityChange() {
      if (document.hidden) {
        this.stopCarousel();
        this.stopFeaturesCarousel();
      } else {
        this.startCarousel();
        this.startFeaturesCarousel();
      }
    },

    // Check if device is mobile
    isMobile() {
      return window.innerWidth < 640; // sm breakpoint
    },

    // Mobile swipe handlers
    onTouchStart(e) {
      if (!this.isMobile()) return;
      this.touchStartX = e.touches[0].clientX;
      this.touchStartY = e.touches[0].clientY;
    },

    onTouchEnd(e) {
      if (!this.isMobile() || this.touchStartX === null) return;
      const dx = e.changedTouches[0].clientX - this.touchStartX;
      const dy = e.changedTouches[0].clientY - this.touchStartY;
      this.touchStartX = null;
      this.touchStartY = null;
      // Only trigger on a clear horizontal swipe (ignore vertical scroll)
      if (Math.abs(dx) < 50 || Math.abs(dx) <= Math.abs(dy)) return;
      if (dx < 0) {
        this.nextSlide();
      } else {
        this.prevSlide();
      }
    }
  },
};
</script>

<style scoped>
.welcome-sparkle {
  position: absolute;
  width: 3px;
  height: 3px;
  background: white;
  border-radius: 50%;
  animation: twinkle 3s infinite ease-in-out;
}
@keyframes twinkle {
  0%, 100% { opacity: 0.15; transform: scale(0.8); }
  50% { opacity: 0.9; transform: scale(1.3); }
}
</style>
