TentangProgramSistem & BiayaKontakArtikel
BIMBINGAN BELAJAR FAQIH

Hadir dengan berbagai metode belajar yang dapat disesuaikan dengan gaya belajar setiap individu yang unik

"Kami percaya bahwa tujuan belajar sejati adalah mencapai pemahaman yang mendalam"

Kami percaya bahwa setiap pelajar dapat diterangi oleh cahaya ilmu. Oleh karena itu, kami hadir sebagai wadah bimbingan yang membantu siswa tidak hanya menghafal, tetapi benar-benar menguasai konsep dan formula, sehingga mereka mampu mengaitkan pengetahuan dengan peranannya dalam kehidupan
(function() {
  if (window.textRevealInitialized) return;
  window.textRevealInitialized = true;

  const CONFIG = {
    triggerPosition: 50,
    animationLength: 100,
    startColor: '#000000',
    endColor: '#e6e6e6',
    textOpacity: 0.2,
    revealType: 'letter',
    scrollSmoothness: 1,
    elementSelection: 'paragraphs',
    animationSpeed: 0.6
  };

  const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
  
  let rafId = null;
  let cachedElements = new Map();
  let observer = null;
  
  function throttle(func, limit) {
    let lastFunc;
    let lastRan;
    return function() {
      const context = this;
      const args = arguments;
      if (!lastRan) {
        func.apply(context, args);
        lastRan = Date.now();
      } else {
        clearTimeout(lastFunc);
        lastFunc = setTimeout(function() {
          if ((Date.now() - lastRan) >= limit) {
            func.apply(context, args);
            lastRan = Date.now();
          }
        }, limit - (Date.now() - lastRan));
      }
    };
  }

  document.addEventListener('DOMContentLoaded', initTextReveal);
  
  function initTextReveal() {
    const revealSections = document.querySelectorAll('[data-text-reveal-section]');
    
    if (!revealSections.length) return;
    
    setupIntersectionObserver();
    revealSections.forEach(processTextRevealSection);
    
    window.addEventListener('scroll', throttledHandleScroll, { passive: true });
    
    requestAnimationFrame(handleScroll);
  }
  
  function setupIntersectionObserver() {
    observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          if (!cachedElements.has(entry.target)) {
            cacheElementData(entry.target);
          }
        }
      });
    }, {
      root: null,
      rootMargin: '50px',
      threshold: 0
    });
  }
  
  function cacheElementData(element) {
    const rect = element.getBoundingClientRect();
    const windowHeight = window.innerHeight;
    
    cachedElements.set(element, {
      element: element,
      rect: rect,
      lastUpdate: Date.now(),
      windowHeight: windowHeight
    });
  }
  
  function processTextRevealSection(section) {
    const startColor = section.getAttribute('data-text-reveal-start-color') || CONFIG.startColor;
    const endColor = section.getAttribute('data-text-reveal-end-color') || CONFIG.endColor;
    const triggerPosition = parseFloat(section.getAttribute('data-text-reveal-trigger-position')) || CONFIG.triggerPosition;
    const animationLength = parseFloat(section.getAttribute('data-text-reveal-animation-length')) || CONFIG.animationLength;
    const revealType = section.getAttribute('data-text-reveal-type') || CONFIG.revealType;
    const targetSelector = section.getAttribute('data-text-reveal-target') || null;
    
    let textElements = [];
    
    if (targetSelector) {
      const targetElement = document.querySelector(targetSelector);
      if (targetElement) {
        textElements = [targetElement];
      }
    } else {
      if (CONFIG.elementSelection === 'headings') {
        textElements = Array.from(section.querySelectorAll('h1, h2, h3, h4, h5, h6, .brxe-heading'));
      } else if (CONFIG.elementSelection === 'paragraphs') {
        textElements = Array.from(section.querySelectorAll('p, .brxe-text, .brxe-text-basic'));
      } else {
        textElements = Array.from(section.querySelectorAll(
          '.brxe-heading, .brxe-text, .brxe-text-basic, ' + 
          '.bricks-heading, .bricks-text, ' +
          '.has-text, .text-wrapper, ' +
          'h1, h2, h3, h4, h5, h6, p, .text'
        ));
        
        if (!textElements.length) {
          const allElements = Array.from(section.querySelectorAll('*'));
          
          textElements = allElements.filter(element => {
            if (element.tagName.match(/^(DIV|SECTION|ARTICLE|ASIDE|FIGURE|HEADER|FOOTER|NAV|MAIN|UL|OL|FORM|TABLE)$/i)) {
              return false;
            }
            
            const hasDirectText = Array.from(element.childNodes).some(node => 
              node.nodeType === Node.TEXT_NODE && node.textContent.trim().length > 0
            );
            
            return hasDirectText;
          });
        }
      }
    }
    
    if (!textElements.length) {
      return;
    }
    
    section.dataset.textRevealProcessed = 'true';
    section.dataset.textRevealTriggerPosition = triggerPosition;
    section.dataset.textRevealAnimationLength = animationLength;
    section.dataset.textRevealType = revealType;
    
    textElements.forEach(element => {
      if (element.textContent && element.textContent.trim()) {
        processTextElement(element, section, startColor, endColor, revealType);
        if (observer) {
          observer.observe(element);
        }
      }
    });
  }
  
  function processTextElement(element, section, startColor, endColor, revealType) {
    if (element.dataset.textRevealProcessed === 'true') {
      return;
    }
    
    const originalText = element.textContent.trim();
    
    if (!originalText) {
      return;
    }
    
    const computedStyle = window.getComputedStyle(element);
    const originalFontSize = computedStyle.fontSize;
    const originalFontWeight = computedStyle.fontWeight;
    const originalLineHeight = computedStyle.lineHeight;
    const originalFontFamily = computedStyle.fontFamily;
    const originalTextAlign = computedStyle.textAlign;
    
    element.innerHTML = '';
    
    const textContainer = document.createElement('div');
    textContainer.className = 'text-reveal-container';
    textContainer.style.cssText = `
      display: block;
      width: 100%;
      font-size: ${originalFontSize};
      font-weight: ${originalFontWeight};
      line-height: ${originalLineHeight};
      font-family: ${originalFontFamily};
      text-align: ${originalTextAlign};
    `;
    
    const transitionDuration = Math.max(CONFIG.animationSpeed, 0.01);
    
    if (revealType === 'letter') {
      const words = originalText.split(/s+/);
      let letterIndex = 0;
      const lettersTotal = originalText.replace(/s+/g, '').length;
      
      const wordFlowContainer = document.createElement('span');
      wordFlowContainer.className = 'text-reveal-word-flow';
      wordFlowContainer.style.cssText = `
        display: inline;
        width: auto;
      `;
      textContainer.appendChild(wordFlowContainer);
      
      words.forEach((word, wordIndex) => {
        const wordContainer = document.createElement('span');
        wordContainer.className = 'text-reveal-word-container';
        wordContainer.style.cssText = `
          display: inline-block;
          margin-right: 0.25em;
          white-space: nowrap;
        `;
        
        for (let i = 0; i < word.length; i++) {
          const letter = word[i];
          
          const letterContainer = document.createElement('span');
          letterContainer.className = 'text-reveal-letter';
          letterContainer.style.cssText = `
            position: relative;
            display: inline-block;
          `;
          
          const initialLetterSpan = document.createElement('span');
          initialLetterSpan.className = 'text-reveal-initial';
          initialLetterSpan.textContent = letter;
          initialLetterSpan.style.cssText = `
            position: absolute;
            top: 0;
            left: 0;
            color: ${startColor};
            opacity: 1;
            transition: opacity ${transitionDuration}s ease;
          `;
          
          const revealedLetterSpan = document.createElement('span');
          revealedLetterSpan.className = 'text-reveal-revealed';
          revealedLetterSpan.textContent = letter;
          revealedLetterSpan.style.cssText = `
            position: relative;
            color: ${endColor};
            opacity: 0;
            transition: opacity ${transitionDuration}s ease;
          `;
          
          letterContainer.dataset.index = letterIndex;
          letterContainer.dataset.total = lettersTotal;
          letterContainer.dataset.revealType = revealType;
          
          letterIndex++;
          
          letterContainer.appendChild(initialLetterSpan);
          letterContainer.appendChild(revealedLetterSpan);
          wordContainer.appendChild(letterContainer);
        }
        
        wordFlowContainer.appendChild(wordContainer);
        
        if (wordIndex < words.length - 1) {
          const spaceElement = document.createElement('span');
          spaceElement.className = 'text-reveal-space';
          spaceElement.style.cssText = `
            display: inline-block;
            width: 0.25em;
          `;
          wordFlowContainer.appendChild(spaceElement);
        }
      });
    } else if (revealType === 'line') {
      const lineContainer = document.createElement('span');
      lineContainer.className = 'text-reveal-line';
      lineContainer.style.cssText = `
        position: relative;
        display: inline-block;
        width: 100%;
      `;
      
      const initialLineSpan = document.createElement('span');
      initialLineSpan.className = 'text-reveal-initial';
      initialLineSpan.textContent = originalText;
      initialLineSpan.style.cssText = `
        position: absolute;
        top: 0;
        left: 0;
        color: ${startColor};
        opacity: 1;
        transition: opacity ${transitionDuration}s ease;
        width: 100%;
      `;
      
      const revealedLineSpan = document.createElement('span');
      revealedLineSpan.className = 'text-reveal-revealed';
      revealedLineSpan.textContent = originalText;
      revealedLineSpan.style.cssText = `
        position: relative;
        color: ${endColor};
        opacity: 0;
        transition: opacity ${transitionDuration}s ease;
        width: 100%;
      `;
      
      lineContainer.dataset.index = 0;
      lineContainer.dataset.total = 1;
      lineContainer.dataset.revealType = revealType;
      
      lineContainer.appendChild(initialLineSpan);
      lineContainer.appendChild(revealedLineSpan);
      textContainer.appendChild(lineContainer);
    } else {
      const words = originalText.split(/s+/);
      
      const wordFlowContainer = document.createElement('span');
      wordFlowContainer.className = 'text-reveal-word-flow';
      wordFlowContainer.style.cssText = `
        display: inline;
        width: auto;
      `;
      textContainer.appendChild(wordFlowContainer);
      
      words.forEach((word, index) => {
        const wordContainer = document.createElement('span');
        wordContainer.className = 'text-reveal-word';
        wordContainer.style.cssText = `
          position: relative;
          margin: 0 0.25em 0 0;
          display: inline-block;
        `;
        
        const initialWordSpan = document.createElement('span');
        initialWordSpan.className = 'text-reveal-initial';
        initialWordSpan.textContent = word;
        initialWordSpan.style.cssText = `
          position: absolute;
          top: 0;
          left: 0;
          color: ${startColor};
          opacity: 1;
          transition: opacity ${transitionDuration}s ease;
        `;
        
        const revealedWordSpan = document.createElement('span');
        revealedWordSpan.className = 'text-reveal-revealed';
        revealedWordSpan.textContent = word;
        revealedWordSpan.style.cssText = `
          position: relative;
          color: ${endColor};
          opacity: 0;
          transition: opacity ${transitionDuration}s ease;
        `;
        
        wordContainer.dataset.index = index;
        wordContainer.dataset.total = words.length;
        wordContainer.dataset.revealType = revealType;
        
        wordContainer.appendChild(initialWordSpan);
        wordContainer.appendChild(revealedWordSpan);
        wordFlowContainer.appendChild(wordContainer);
        
        if (index < words.length - 1) {
          const spaceElement = document.createElement('span');
          spaceElement.className = 'text-reveal-space';
          spaceElement.style.cssText = `
            display: inline-block;
            width: 0.25em;
          `;
          wordFlowContainer.appendChild(spaceElement);
        }
      });
    }
    
    element.appendChild(textContainer);
    element.dataset.textRevealProcessed = 'true';
  }
  
  const throttledHandleScroll = throttle(handleScroll, 16);
  
  function handleScroll() {
    if (rafId) cancelAnimationFrame(rafId);
    
    rafId = requestAnimationFrame(() => {
      const revealSections = document.querySelectorAll('[data-text-reveal-section][data-text-reveal-processed="true"]');
      
      revealSections.forEach(section => {
        const windowHeight = window.innerHeight;
        
        const textElements = section.querySelectorAll('[data-text-reveal-processed="true"]');
        
        textElements.forEach(element => {
          let cachedData = cachedElements.get(element);
          
          if (!cachedData || Date.now() - cachedData.lastUpdate > 100) {
            cachedData = {
              rect: element.getBoundingClientRect(),
              lastUpdate: Date.now()
            };
            cachedElements.set(element, cachedData);
          }
          
          const elementRect = cachedData.rect;
          const elementCenter = elementRect.top + (elementRect.height / 2);
          
          if (elementRect.bottom < 0 || elementRect.top > windowHeight) {
            return;
          }
          
          const triggerPosition = parseFloat(section.dataset.textRevealTriggerPosition || CONFIG.triggerPosition);
          const triggerPoint = (windowHeight * triggerPosition) / 100;
          
          const animationLength = parseFloat(section.dataset.textRevealAnimationLength || CONFIG.animationLength);
          const animationDistance = (windowHeight * animationLength) / 100;
          
          let scrollProgress = (elementCenter - triggerPoint) / animationDistance;
          scrollProgress = scrollProgress * (1 / CONFIG.animationSpeed);
          
          let elementProgress = clamp(scrollProgress, 0, 1);
          const progress = elementProgress;
          
          const revealType = section.dataset.textRevealType || CONFIG.revealType;
          
          if (revealType === 'letter') {
            const letters = element.querySelectorAll('.text-reveal-letter');
            const totalLetters = letters.length;
            
            const letterCountFromData = parseInt(letters[0]?.dataset.total || totalLetters);
            
            letters.forEach((letter) => {
              let letterProgress;
              
              const originalLetterIndex = parseInt(letter.dataset.index || 0);
              const letterIndex = letterCountFromData - 1 - originalLetterIndex;
              
              const segmentSize = 1 / letterCountFromData;
              const letterStart = letterIndex * segmentSize;
              const letterEnd = (letterIndex + 1) * segmentSize;
              
              if (progress <= letterStart) {
                letterProgress = 0;
              } else if (progress >= letterEnd) {
                letterProgress = 1;
              } else {
                letterProgress = (progress - letterStart) / segmentSize;
              }
              
              const revealedLetter = letter.querySelector('.text-reveal-revealed');
              const initialLetter = letter.querySelector('.text-reveal-initial');
              
              if (revealedLetter) revealedLetter.style.opacity = letterProgress;
              if (initialLetter) initialLetter.style.opacity = 1 - letterProgress;
            });
          } else if (revealType === 'line') {
            const lines = element.querySelectorAll('.text-reveal-line');
            
            lines.forEach(line => {
              const revealedLine = line.querySelector('.text-reveal-revealed');
              const initialLine = line.querySelector('.text-reveal-initial');
              
              if (revealedLine) revealedLine.style.opacity = progress;
              if (initialLine) initialLine.style.opacity = 1 - progress;
            });
          } else {
            const words = element.querySelectorAll('.text-reveal-word');
            const totalWords = words.length;
            
            words.forEach((word, index) => {
              let wordProgress;
              
              const originalWordIndex = parseInt(word.dataset.index || 0);
              const wordIndex = totalWords - 1 - originalWordIndex;
              
              const segmentSize = 1 / totalWords;
              const wordStart = wordIndex * segmentSize;
              const wordEnd = (wordIndex + 1) * segmentSize;
              
              if (progress <= wordStart) {
                wordProgress = 0;
              } else if (progress >= wordEnd) {
                wordProgress = 1;
              } else {
                wordProgress = (progress - wordStart) / segmentSize;
              }
              
              const revealedWord = word.querySelector('.text-reveal-revealed');
              const initialWord = word.querySelector('.text-reveal-initial');
              
              if (revealedWord) revealedWord.style.opacity = wordProgress;
              if (initialWord) initialWord.style.opacity = 1 - wordProgress;
            });
          }
        });
      });
    });
  }
  
  window.addEventListener('resize', throttle(() => {
    cachedElements.clear();
    handleScroll();
  }, 100), { passive: true });
  
  window.destroyTextReveal = function() {
    if (rafId) cancelAnimationFrame(rafId);
    window.removeEventListener('scroll', throttledHandleScroll);
    window.removeEventListener('resize', handleScroll);
    
    if (observer) {
      observer.disconnect();
      observer = null;
    }
    
    cachedElements.clear();
    
    const sections = document.querySelectorAll('[data-text-reveal-section][data-text-reveal-processed="true"]');
    sections.forEach(section => {
      section.removeAttribute('data-text-reveal-processed');
      section.removeAttribute('data-text-reveal-trigger-position');
      section.removeAttribute('data-text-reveal-animation-length');
      section.removeAttribute('data-text-reveal-type');
      
      const textElements = section.querySelectorAll('[data-text-reveal-processed="true"]');
      textElements.forEach(element => {
        const textContainers = element.querySelectorAll('.text-reveal-word, .text-reveal-word-container, .text-reveal-letter, .text-reveal-line, .text-reveal-space');
        let originalText = '';
        
        if (textContainers.length > 0) {
          const firstContainer = textContainers[0];
          
          if (firstContainer.classList.contains('text-reveal-line')) {
            const textElement = firstContainer.querySelector('.text-reveal-revealed');
            if (textElement) originalText = textElement.textContent;
          } else if (firstContainer.classList.contains('text-reveal-word')) {
            element.querySelectorAll('.text-reveal-word').forEach((container, index) => {
              if (index > 0) originalText += ' ';
              const textEl = container.querySelector('.text-reveal-revealed');
              if (textEl) originalText += textEl.textContent;
            });
          } else if (firstContainer.classList.contains('text-reveal-word-container')) {
            element.querySelectorAll('.text-reveal-word-container').forEach((container, index) => {
              if (index > 0) originalText += ' ';
              let wordText = '';
              container.querySelectorAll('.text-reveal-letter').forEach(letter => {
                const textEl = letter.querySelector('.text-reveal-revealed');
                if (textEl) wordText += textEl.textContent;
              });
              originalText += wordText;
            });
          } else {
            textContainers.forEach(container => {
              if (container.classList.contains('text-reveal-space')) {
                originalText += ' ';
              } else if (container.classList.contains('text-reveal-letter')) {
                const textEl = container.querySelector('.text-reveal-revealed');
                if (textEl) originalText += textEl.textContent;
              }
            });
          }
        }
        
        element.innerHTML = originalText.trim();
        element.removeAttribute('data-text-reveal-processed');
      });
    });
    
    window.textRevealInitialized = false;
  };

  if (document.readyState !== 'loading') {
    initTextReveal();
  }
})();
(function() {
  window.AvatarCircles = window.AvatarCircles || {};
  
  function isMobileDevice() {
    const userAgent = navigator.userAgent || navigator.vendor || window.opera;
    if (/android|iphone|ipad|ipod|blackberry|windows phone/i.test(userAgent)) {
      return true;
    }
    
    if (window.innerWidth < 768) {
      return true;
    }
    
    return false;
  }
  
  function initAvatarCircles() {
    // ✅ CORREGIDO: Ya no bloqueamos móviles aquí
    // if (isMobileDevice()) {
    //   console.log('Mobile device detected, Avatar Circles will not initialize');
    //   return;
    // }
    
    const containers = document.querySelectorAll('[data-avatar-circles]');
    
    containers.forEach((container) => {
      try {
        // Skip if already initialized
        if (container.hasAttribute('data-circles-initialized')) {
          return;
        }
        
        // Mark as initialized immediately to prevent duplicate processing
        container.setAttribute('data-circles-initialized', 'true');
        
        
        container.setAttribute('data-avatar-image-1', 'https://randomuser.me/api/portraits/women/44.jpg');
        container.setAttribute('data-avatar-profile-1', '#');
        container.setAttribute('data-avatar-bg-1', 'transparent');
        
        container.setAttribute('data-avatar-extra', '0');
        container.setAttribute('data-avatar-extra-url', '#');
        container.setAttribute('data-counter-style', 'circle');
        container.setAttribute('data-abbreviate-numbers', 'true');
        container.setAttribute('data-extra-text-color', '#ffffff');
        container.setAttribute('data-animation-type', 'none');
        container.setAttribute('data-animation-speed', '500');
        container.setAttribute('data-animate-on-scroll', 'true');
        
        let avatarCount = 1;
        const avatars = [];
        
        while (true) {
          const imageUrl = container.getAttribute(`data-avatar-image-${avatarCount}`);
          if (!imageUrl) break;
          
          avatars.push({
            imageUrl,
            profileUrl: container.getAttribute(`data-avatar-profile-${avatarCount}`) || '#',
            backgroundColor: container.getAttribute(`data-avatar-bg-${avatarCount}`) || 'transparent'
          });
          avatarCount++;
        }
        
        if (avatars.length === 0) return;
        
        const numExtraPeople = parseInt(container.getAttribute('data-avatar-extra') || '0', 10);
        const counterStyle = container.getAttribute('data-counter-style') || 'circle';
        const abbreviateNumbers = container.getAttribute('data-abbreviate-numbers') === 'true';
        const extraTextColor = container.getAttribute('data-extra-text-color') || '#ffffff';
        const animationType = container.getAttribute('data-animation-type') || 'none';
        const animationSpeed = parseInt(container.getAttribute('data-animation-speed') || '500');
        const animateOnScroll = container.getAttribute('data-animate-on-scroll') === 'true';
        
        container.innerHTML = '';
        Object.assign(container.style, {
          display: 'flex',
          position: 'relative',
          zIndex: '10'
        });
        
        const avatarSize = 40;
        const borderWidth = 4;
        const borderColor = '#d9d9d9';
        const extraBgColor = '#4a6cf7';
        const extraHoverColor = '#3451b2';
        const overlap = 16;
        
        if (animationType !== 'none' && !document.querySelector('#avatar-circles-animations')) {
          const styleSheet = document.createElement('style');
          styleSheet.id = 'avatar-circles-animations';
          styleSheet.textContent = '            @keyframes fadeInUp {              from { opacity: 0; transform: translateY(20px); }              to { opacity: 1; transform: translateY(0); }            }                        @keyframes breathing {              0%, 100% { transform: scale(1); }              50% { transform: scale(1.05); }            }                        @keyframes glow {              0%, 100% { box-shadow: 0 0 5px rgba(255, 255, 255, 0.1); }              50% { box-shadow: 0 0 15px rgba(239, 96, 19, 0.6); }            }                        .avatar-cascade { opacity: 0; }            .avatar-cascade.animate { animation-name: fadeInUp; animation-fill-mode: forwards; }            .avatar-breathing img { animation: breathing 3s infinite ease-in-out; }            .avatar-glow img { animation: glow 3s infinite ease-in-out; }          ';
          document.head.appendChild(styleSheet);
        }
        
        avatars.forEach((avatar, index) => {
          const link = document.createElement('a');
          link.href = avatar.profileUrl;
          link.target = '_blank';
          link.rel = 'noopener noreferrer';
          
          if (animationType !== 'none') {
            link.classList.add('avatar-' + animationType);
            
            if (animationType === 'cascade') {
              const delay = index * (animationSpeed / 1000 / avatars.length);
              link.style.animationDelay = delay + 's';
              link.style.animationDuration = (animationSpeed / 1000) + 's';
            }
          }
          
          Object.assign(link.style, {
            marginLeft: index > 0 ? `-${overlap}px` : '0',
            transition: 'all 0.3s ease',
            position: 'relative',
            zIndex: avatars.length - index
          });
          
          const img = document.createElement('img');
          img.src = avatar.imageUrl;
          img.width = avatarSize;
          img.height = avatarSize;
          img.alt = `Avatar ${index + 1}`;
          
          Object.assign(img.style, {
            height: `${avatarSize}px`,
            width: `${avatarSize}px`,
            borderRadius: '50%',
            border: `${borderWidth}px solid ${borderColor}`,
            backgroundColor: avatar.backgroundColor,
            transition: 'all 0.3s ease',
            objectFit: 'cover'
          });
          
          link.appendChild(img);
          container.appendChild(link);
        });
        
        if (numExtraPeople > 0) {
          const morePeopleLink = document.createElement('a');
          morePeopleLink.href = container.getAttribute('data-avatar-extra-url') || '#';
          
          let displayText;
          if (abbreviateNumbers) {
            if (numExtraPeople >= 1000000) {
              displayText = (Math.floor(numExtraPeople / 100000) / 10).toFixed(1).replace(/.0$/, '') + 'M+';
            } else if (numExtraPeople >= 1000) {
              displayText = (Math.floor(numExtraPeople / 100) / 10).toFixed(1).replace(/.0$/, '') + 'K+';
            } else {
              displayText = numExtraPeople + '+';
            }
          } else {
            displayText = numExtraPeople + '+';
          }
          
          morePeopleLink.textContent = displayText;
          
          if (animationType !== 'none') {
            morePeopleLink.classList.add('avatar-' + animationType);
            
            if (animationType === 'cascade') {
              const delay = avatars.length * (animationSpeed / 1000 / (avatars.length + 1));
              morePeopleLink.style.animationDelay = delay + 's';
              morePeopleLink.style.animationDuration = (animationSpeed / 1000) + 's';
            }
          }
          
          let counterCSS = {
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            height: `${avatarSize}px`,
            width: `${avatarSize}px`,
            borderRadius: '50%',
            border: `${borderWidth}px solid ${borderColor}`,
            backgroundColor: extraBgColor,
            color: extraTextColor,
            fontSize: `${Math.max(12, avatarSize / 3)}px`,
            fontWeight: '600',
            textDecoration: 'none',
            marginLeft: `-${overlap}px`,
            transition: 'all 0.3s ease'
          };
          
          if (counterStyle === 'auto-expand') {
            const digitCount = displayText.length;
            const extraWidth = avatarSize * (1 + (digitCount > 2 ? (digitCount - 2) * 0.35 : 0));
            counterCSS.width = `${extraWidth}px`;
          } else if (counterStyle === 'pill') {
            counterCSS.borderRadius = `${avatarSize / 2}px`;
            counterCSS.minWidth = `${avatarSize}px`;
            counterCSS.padding = '0 10px';
          } else if (counterStyle === 'badge') {
            counterCSS = {
              ...counterCSS,
              height: `${Math.max(24, avatarSize * 0.7)}px`,
              minWidth: `${Math.max(24, avatarSize * 0.7)}px`,
              borderRadius: '20px',
              marginLeft: '5px',
              boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
              whiteSpace: 'nowrap',
              paddingLeft: `${Math.max(12, displayText.length * 3)}px`,
              paddingRight: `${Math.max(12, displayText.length * 3)}px`
            };
          }
          
          Object.assign(morePeopleLink.style, counterCSS);
          container.appendChild(morePeopleLink);
        }
        
        if (animationType === 'cascade' && animateOnScroll) {
          const observer = new IntersectionObserver(entries => {
            entries.forEach(entry => {
              if (entry.isIntersecting) {
                setTimeout(() => {
                  entry.target.querySelectorAll('.avatar-cascade').forEach(el => {
                    el.classList.add('animate');
                  });
                }, 100);
                observer.unobserve(entry.target);
              }
            });
          }, { threshold: 0.2 });
          
          observer.observe(container);
        } else if (animationType === 'cascade') {
          setTimeout(() => {
            container.querySelectorAll('.avatar-cascade').forEach(el => {
              el.classList.add('animate');
            });
          }, 100);
        }
      } catch (error) {
        console.warn('Error initializing avatar circles:', error);
      }
    });
  }
  
  // Single initialization strategy to prevent the flicker
  let initialized = false;
  
  function init() {
    // ✅ CORREGIDO: Ya no bloqueamos móviles aquí tampoco
    if (initialized) {
      return;
    }
    
    initialized = true;
    initAvatarCircles();
  }
  
  // Use only one event to prevent multiple initializations
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
  
  // Only use one additional initialization attempt to catch any late-loaded content
  window.addEventListener('load', init);
  
  window.AvatarCircles = {
    init: init,
    version: '1.0',
    isMobile: isMobileDevice
  };
})();
LAYANAN

Bimbingan Komprehensif Kami

Kami menyediakan tiga pilar bimbingan utama untuk mendukung perjalanan akademis dan spiritual siswa:

Bimbingan Mata Pelajaran

Menguasai berbagai mata pelajaran sekolah dengan pendekatan yang fokus pada konsep

Bimbingan Qur'an

Memperdalam pemahaman dan kemampuan dalam membaca (Tahsin) dan menghafal (Tahfidz) Al-Qur'an

Bimbingan Bahasa

Mengasah kemampuan berkomunikasi global melalui kelas Bahasa Inggris dan Bahasa Arab

(function(){
            const style = document.createElement('style');
            style.textContent = `
              .brand-carousel-preview {
                display: flex;
                align-items: center;
                overflow: hidden;
                position: relative;
                width: 100%;
                height: 100%;
              }
              
              .brand-carousel-track {
                display: flex;
                align-items: center;
                will-change: transform;
                padding: 0;
                position: relative;
                gap: 30px;
                backface-visibility: hidden;
                perspective: 1000px;
                transform: translateZ(0);
              }
              
              .brand-logo {
                height: 80px;
                width: auto;
                opacity: 0.9;
                transition: all 0.3s ease;
                display: block;
                max-width: none;
                filter: brightness(0.95) contrast(1.1);
                
                border-radius: 8px;
                flex-shrink: 0;
              }
              
              .brand-logo:hover {
                opacity: 1;
                transform: translateY(-2px);
                filter: brightness(1) contrast(1.2);
                
              }
              
              .carousel-blur-left,
              .carousel-blur-right {
                position: absolute;
                top: 0;
                bottom: 0;
                width: min(80px, 15%);
                pointer-events: none;
                z-index: 1;
              }
              
              .carousel-blur-left {
                left: 0;
                background: linear-gradient(to right, rgba(245,245,245,0.9), rgba(245,245,245,0));
              }
              
              .carousel-blur-right {
                right: 0;
                background: linear-gradient(to left, rgba(245,245,245,0.9), rgba(245,245,245,0));
              }
            `;
            document.head.appendChild(style);
            
            class InfiniteCarousel {
              constructor(container, options = {}) {
                this.container = container;
                this.track = container.querySelector('.brand-carousel-track');
                this.options = {
                  speed: options.speed || 1,
                  gap: options.gap || 30,
                  ...options
                };
                
                this.animationId = null;
                this.currentX = 0;
                this.logos = [];
                this.clones = [];
                this.containerWidth = 0;
                this.contentWidth = 0;
                this.isRunning = false;
                this.isScrolling = false;
                this.lastMeasureTime = 0;
                this.isVisible = true;
                
                this.init();
              }
              
              init() {
                this.logos = Array.from(this.track.children);
                if (this.logos.length === 0) return;
                
                this.preloadImages().then(() => {
                  this.measureDimensions();
                  this.createClones();
                  this.setupIntersectionObserver();
                  this.setupScrollDetection();
                  this.setupResizeHandler();
                  this.start();
                });
              }
              
              preloadImages() {
                const images = this.logos.filter(logo => logo.tagName === 'IMG');
                const promises = images.map(img => {
                  return new Promise((resolve) => {
                    if (img.complete && img.naturalWidth > 0) {
                      resolve();
                    } else {
                      const handleLoad = () => {
                        img.removeEventListener('load', handleLoad);
                        img.removeEventListener('error', handleError);
                        resolve();
                      };
                      
                      const handleError = () => {
                        img.removeEventListener('load', handleLoad);
                        img.removeEventListener('error', handleError);
                        if (img.src.includes('.svg')) {
                          console.warn('SVG failed to load on mobile:', img.src);
                        }
                        resolve();
                      };
                      
                      img.addEventListener('load', handleLoad);
                      img.addEventListener('error', handleError);
                      
                      setTimeout(() => {
                        if (!img.complete) {
                          handleError();
                        }
                      }, 3000);
                    }
                  });
                });
                
                return Promise.all(promises);
              }
              
              setupIntersectionObserver() {
                const observer = new IntersectionObserver((entries) => {
                  entries.forEach(entry => {
                    this.isVisible = entry.isIntersecting;
                    if (!this.isVisible) {
                      this.pause();
                    } else {
                      this.resume();
                    }
                  });
                }, {
                  threshold: 0.1,
                  rootMargin: '50px'
                });
                
                observer.observe(this.container);
                this.intersectionObserver = observer;
              }
              
              setupScrollDetection() {
                let scrollTimer = null;
                
                const handleScroll = () => {
                  this.isScrolling = true;
                  clearTimeout(scrollTimer);
                  
                  scrollTimer = setTimeout(() => {
                    this.isScrolling = false;
                  }, 150);
                };
                
                window.addEventListener('scroll', handleScroll, { passive: true });
                window.addEventListener('touchmove', handleScroll, { passive: true });
                
                this.scrollHandler = handleScroll;
              }
              
              setupResizeHandler() {
                let resizeTimer = null;
                let lastWidth = this.container.offsetWidth;
                
                const handleResize = () => {
                  if (this.isScrolling) return;
                  
                  const currentWidth = this.container.offsetWidth;
                  if (Math.abs(currentWidth - lastWidth) < 10) return;
                  
                  lastWidth = currentWidth;
                  clearTimeout(resizeTimer);
                  
                  resizeTimer = setTimeout(() => {
                    if (!this.isScrolling && this.isVisible) {
                      this.measureDimensions();
                      this.createClones();
                    }
                  }, 300);
                };
                
                window.addEventListener('resize', handleResize, { passive: true });
                this.resizeHandler = handleResize;
              }
              
              measureDimensions() {
                const now = Date.now();
                if (now - this.lastMeasureTime < 100) return;
                this.lastMeasureTime = now;
                
                this.containerWidth = this.container.offsetWidth;
                
                this.contentWidth = 0;
                this.logos.forEach(logo => {
                  if (logo.offsetWidth > 0) {
                    this.contentWidth += logo.offsetWidth + this.options.gap;
                  }
                });
                this.contentWidth = Math.max(this.contentWidth - this.options.gap, 100);
              }
              
              createClones() {
                if (this.isScrolling) return;
                
                this.clones.forEach(clone => clone.remove());
                this.clones = [];
                
                if (this.contentWidth === 0) return;
                
                const totalNeeded = Math.ceil((this.containerWidth * 2.5) / this.contentWidth) + 1;
                
                for (let i = 0; i < totalNeeded; i++) {
                  this.logos.forEach(logo => {
                    const clone = logo.cloneNode(true);
                    clone.classList.add('carousel-clone');
                    this.track.appendChild(clone);
                    this.clones.push(clone);
                  });
                }
              }
              
              start() {
                if (this.isRunning || !this.isVisible) return;
                this.isRunning = true;
                this.animate();
              }
              
              pause() {
                this.isRunning = false;
                if (this.animationId) {
                  cancelAnimationFrame(this.animationId);
                  this.animationId = null;
                }
              }
              
              resume() {
                if (!this.isRunning && this.isVisible) {
                  this.start();
                }
              }
              
              stop() {
                this.pause();
              }
              
              animate() {
                if (!this.isRunning || !this.isVisible) return;
                
                this.currentX -= this.options.speed * 0.5;
                
                if (Math.abs(this.currentX) >= this.contentWidth + this.options.gap) {
                  this.currentX = 0;
                }
                
                this.track.style.transform = `translateX(${this.currentX}px)`;
                
                this.animationId = requestAnimationFrame(() => this.animate());
              }
              
              destroy() {
                this.stop();
                this.clones.forEach(clone => clone.remove());
                this.clones = [];
                
                if (this.intersectionObserver) {
                  this.intersectionObserver.disconnect();
                }
                
                if (this.resizeHandler) {
                  window.removeEventListener('resize', this.resizeHandler);
                }
                
                if (this.scrollHandler) {
                  window.removeEventListener('scroll', this.scrollHandler);
                  window.removeEventListener('touchmove', this.scrollHandler);
                }
              }
            }
            
            function init() {
              const c = document.querySelector('[data-brand-carousel]');
              if(!c) {
                console.warn('Brand Carousel: Container with data-brand-carousel attribute not found');
                return;
              }
              
              if(c._carouselInstance) {
                c._carouselInstance.destroy();
              }
              c.innerHTML = '';
              
              const preview = document.createElement('div');
              preview.className = 'brand-carousel-preview';
              
              const computedStyle = window.getComputedStyle(c);
              preview.style.width = '100%';
              preview.style.height = '100%';
              preview.style.position = 'relative';
              preview.style.display = 'flex';
              preview.style.alignItems = 'center';
              preview.style.overflow = 'hidden';
              
              const track = document.createElement('div');
              track.className = 'brand-carousel-track';
              
              const leftBlur = document.createElement('div');
              leftBlur.className = 'carousel-blur-left';
              const rightBlur = document.createElement('div');
              rightBlur.className = 'carousel-blur-right';
              
              c.setAttribute('data-brand-1', 'https://library.bricksfusion.com/wp-content/uploads/2025/06/pitch.svg');
              c.setAttribute('data-brand-2', 'https://library.bricksfusion.com/wp-content/uploads/2025/06/pinia.svg');
              c.setAttribute('data-brand-3', 'https://library.bricksfusion.com/wp-content/uploads/2025/06/obsidian.svg');
              c.setAttribute('data-brand-4', 'https://library.bricksfusion.com/wp-content/uploads/2025/06/atlassian-1.svg');
              
              let logos = [];
              for(let i = 1; i <= 8; i++) {
                const src = c.getAttribute(`data-brand-${i}`);
                if(src) {
                  logos.push({
                    src: src,
                    alt: `Brand ${i}`
                  });
                }
              }
              
              if (logos.length === 0) {
                logos = [
                  { src: "https://www.svgrepo.com/show/303205/html-5-logo.svg", alt: "HTML5" },
                  { src: "https://www.svgrepo.com/show/303481/css-3-logo.svg", alt: "CSS3" },
                  { src: "https://www.svgrepo.com/show/303206/javascript-logo.svg", alt: "JavaScript" },
                  { src: "https://www.svgrepo.com/show/303266/nodejs-icon-logo.svg", alt: "Node.js" }
                ];
              }
              
              logos.forEach(logo => {
                const img = document.createElement('img');
                img.src = logo.src;
                img.alt = logo.alt;
                img.className = 'brand-logo';
                track.appendChild(img);
              });
              
              preview.appendChild(leftBlur);
              preview.appendChild(track);
              preview.appendChild(rightBlur);
              c.appendChild(preview);
              
              setTimeout(() => {
                c._carouselInstance = new InfiniteCarousel(preview, {
                  speed: 1,
                  gap: 30
                });
              }, 100);
            }
            
            function reinitCarousel() {
              const carousel = document.querySelector('[data-brand-carousel]');
              if (carousel?._carouselInstance) carousel._carouselInstance.destroy();
              setTimeout(init, 100);
            }
            
            if (document.readyState === 'loading') {
              document.addEventListener('DOMContentLoaded', init);
            } else {
              init();
            }
            
            document.addEventListener('bricks/content_loaded', reinitCarousel);
            
            let resizeTimer;
            window.addEventListener('resize', () => {
              clearTimeout(resizeTimer);
              resizeTimer = setTimeout(reinitCarousel, 200);
            });
            
            setTimeout(init, 100);
          })();
PROGRAM

Sistem Belajar yang Fleksibel

Setiap individu memiliki gaya belajar yang unik. Oleh karena itu, kami menawarkan berbagai metode belajar yang dapat disesuaikan

Privat Individu

Dapatkan perhatian penuh dari tutor melalui sesi belajar tatap muka (offline) maupun daring (online).

Kelompok Offline

Belajar bersama menjadi lebih seru! Kelas dapat diadakan di lokasi BBF atau kami yang datang ke tempat Anda (undangan untuk komunitas)

Kelompok Online

Khusus untuk Bimbingan Bahasa Arab, Anda bisa belajar dari mana saja dengan nyaman.

Program Instansi

Kami menawarkan program khusus Bimbingan Bahasa Arab dan Qur'an untuk sekolah, kantor, maupun institusi lainnya.

Hubungi KamiMengapa BBF?
Mengapa BBF

Mengapa Memilih Kami?

Berikut beberapa alasan kenapa Anda memilih BBF

Manajemen Profesional

Kami memiliki manajemen yang berdedikasi untuk merekrut pengajar berkualitas dan melakukan pembinaan karakter. Kami menangani semua urusan administrasi, mulai dari presensi hingga penagihan, sehingga pengajar dapat fokus mendidik

01
Fokus pada Nilai Makna

Kami tidak hanya mengajarkan pengetahuan, tetapi juga berkomitmen untuk menanamkan nilai-nilai luhur dalam setiap proses belajar.

02
Proses Terstruktur

Dari pendaftaran awal hingga laporan perkembangan, semuanya diatur secara sistematis dan profesional

03
PAKET INVESTASI

Investasi Bulanan

Cara Pembayaran: Untuk kemudahan, pembayaran dilakukan pada pekan kedua program berjalan sesuai dengan SOP yang disepakati.

Individu
800k/bulan
Bahasa Arab, Bimbingan Mata Pelajaran, Tahsin & QuranHubungi Kami
Bahasa Arab
Tahsin
Bimbel
Anak remaja - semua kategori
IndividuPopular
200k/pertemuan
Bahasa Arab, Bimbingan Mata Pelajaran, Tahsin & QuranHubungi Kami
Bahasa Arab
Tahsin
Bimbel
Anak remaja - semua kategori
Kelompok
1,6jt/bulan
Bahasa Arab, Bimbingan Mata Pelajaran, Tahsin & QuranChat Kami
Komunitas Anak-Anak
Komunitas Arisan
Komunitas Wali Murid
Instansi
KelompokPopular
400k/pertemuan
Bahasa Arab, Bimbingan Mata Pelajaran, Tahsin & QuranChat Kami
Komunitas Anak-Anak
Komunitas Arisan
Komunitas Wali Murid
Instansi
Komunitas Remaja
Artikel

Info & Berita

Semoga artikel berikut bermanfaat untuk parenting buah hati Anda tercinta