Typography

Semantic Headings
Typography
Semantic H1

Progress and possibility

Semantic H2

Progress and possibility

Semantic H3

Progress and possibility

Semantic H4

Progress and possibility

Semantic H5
Progress and possibility
Semantic H6
Progress and possibility
Heading Styles
Typography
heading
heading="display"

Progress and possibility

heading
heading="4xl"

Progress and possibility

heading
heading="3xl"

Progress and possibility

heading
heading="2xl"

Progress and possibility

heading
heading="xl"

Progress and possibility

heading
heading="lg"

Progress and possibility

heading
heading="md"

Progress and possibility

heading
heading="sm"

Progress and possibility

heading
heading="xs"

Progress and possibility

Paragraph Styles
Typography
paragraph
paragraph="lg"

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

paragraph
paragraph="md"

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

paragraph

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

paragraph
paragraph="sm"

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Rich Text
Typography
rich-text

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

Block quote

Ordered list

  1. Item 1
  2. Item 2
  3. Item 3

Unordered list

  • Item A
  • Item B
  • Item C
Text link

Bold text

Emphasis

Superscript

Subscript

All Block Quotes
Block Quote
All Unordered Lists
All List Items
  • This is some text inside of a div block.
  • This is some text inside of a div block.
  • This is some text inside of a div block.
Text Links
Typography
All Links
Text Link

Color

Primary
Color
Var:
--primary-orange
Prop:
orange
Value:
#FF7500
Var:
--primary-yellow
Prop:
yellow
Value:
#FED758
Var:
--primary-red
Prop:
red
Value:
#FF1C1C
Neutral
Color
Var:
--neutral-900
Prop:
neutral-900
Value:
#000000
Var:
--neutral-800
Prop:
neutral-800
Value:
#444444
Var:
--neutral-300
Prop:
neutral-300
Value:
#EBEBEB
Var:
--neutral-200
Prop:
neutral-200
Value:
#F6F6F4
Var:
--neutral-100
Prop:
neutral-100
Value:
#FFFFFF
Var:
--shade-900-8
Prop:
shade-900-8
Value:
rgba(0, 0, 0, 0.08)
Secondary
Color
Var:
--secondary-teal-dark
Prop:
teal-dark
Value:
#024852
Var:
--secondary-teal-light
Prop:
teal-light
Value:
#5CA4B3
Var:
--secondary-teal-xlight
Prop:
teal-xlight
Value:
#5CA4B3
Var:
--secondary-taupe
Prop:
taupe
Value:
#B1A099
Var:
--secondary-olive
Prop:
olive
Value:
#708574
Var:
--secondary-terracotta
Prop:
terracotta
Value:
#973822
Utility
Color
Var:
--utility-error
Prop:
error
Value:
#FF1C1C
Var:
--utility-focus
Prop:
focus
Value:
#973822
Var:
--utility-default
Prop:
default
Value:
#708574

Elements

Element / Heading
Elements

Heading

Heading:
h1
h2
h3
h4
h5
h6
Heading Size:
xs
sm
md
lg
xl
2xl
3xl
4xl
display
Element / Icon
Elements
Icon Select:
#youtube
#twitter
#linkedin
#twitch
#instagram
#facebook
Icon Size:
sm
md
lg
Element / Label
Elements
Built to Go Further
Element / Text Button
Elements
Icon Size:
sm
md
lg
Element / Button
Elements
Element / Button
Elements
Color
dark
light
Element / Bg Video
Elements
Element / Image
Elements
Cards/ Icon Card
Elements
Capacity

CORE Architecture

A scalable battery system architecture that helps align module design, pack integration, and production execution.

Cards/ Image Background Card
Elements

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Cards/ Location Card
Elements
Mason, Ohio
Heading

Supporting production-focused work, including module and pack manufacturing capabilities for programs moving toward repeatable, scalable output.

4240 Irwin Simpson Road Mason OH 45040
Cards/ System Card
Elements

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Cards / Manufacturing Card
Elements
2GWh

Operating Capacity

2

Production Lines

15+

Years of Testing Data

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Element / Copy Block
Elements
Built to Go Further

Heading

Through a safety-first design philosophy and rigorous validation, Acculon bridges the gap between cutting-edge chemistries and industrial-grade durability. We build tougher batteries so you can go further.

Color
dark
light
Element / Field
Elements
Custom CSS
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Components

Global / Footer
Components
Section / Empty
Components
Masthead / Hero
Components
Built to Go Further

A Heading Goes Here

Through a safety-first design philosophy and rigorous validation, Acculon bridges the gap between cutting-edge chemistries and industrial-grade durability. We build tougher batteries so you can go further.

Built to Go Further

A Heading Goes Here

Through a safety-first design philosophy and rigorous validation, Acculon bridges the gap between cutting-edge chemistries and industrial-grade durability. We build tougher batteries so you can go further.

Masthead / Hero Form
Components
Built to Go Further

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Talk with a Specialist

Share your runtime, load, redundancy, and site requirements. Our team will help identify the CORE configuration best aligned to your application.

Custom CSS

Message Received!

Your message has been successfully received. Our team will review it and be in touch shortly.
Oops! Something went wrong while submitting the form.
Section / CTA
Components
Get Started Today

Tell us what challenge you are facing and we will provide a solution that meets your needs.

Section / Split
Components
Built to Go Further

Heading

Through a safety-first design philosophy and rigorous validation, Acculon bridges the gap between cutting-edge chemistries and industrial-grade durability. We build tougher batteries so you can go further.

Section / Card Grid
Components
Capacity

CORE Architecture

A scalable battery system architecture that helps align module design, pack integration, and production execution.

Capacity

CORE Architecture

A scalable battery system architecture that helps align module design, pack integration, and production execution.

Capacity

CORE Architecture

A scalable battery system architecture that helps align module design, pack integration, and production execution.

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

2GWh

Operating Capacity

2

Production Lines

15+

Years of Testing Data

Heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Section / Image Copy
Components
Built to Go Further

Heading

Through a safety-first design philosophy and rigorous validation, Acculon bridges the gap between cutting-edge chemistries and industrial-grade durability. We build tougher batteries so you can go further.

Section / Cascading Slider
Components
Built to go further

Engineered for Mission-Critical Electrification

Through a safety-first design philosophy and rigorous validation, Acculon bridges the gap between cutting-edge chemistries and industrial-grade durability. We build tougher batteries so you can go further.

// required script in before closing </body> tag

<script>
  function initCascadingSlider() {

  const duration = 0.65;
  const ease = 'power3.inOut';

  const breakpoints = [
    { maxWidth: 479, activeWidth: 0.78, siblingWidth: 0.08 },
    { maxWidth: 767, activeWidth: 0.70, siblingWidth: 0.10 },
    { maxWidth: 991, activeWidth: 0.60, siblingWidth: 0.10 },
    { maxWidth: Infinity, activeWidth: 0.60, siblingWidth: 0.13 },
  ];

  const wrappers = document.querySelectorAll('[data-cascading-slider-wrap]');
  wrappers.forEach(setupInstance);

  function setupInstance(wrapper) {
    const viewport = wrapper.querySelector('[data-cascading-viewport]');
    const prevButton = wrapper.querySelector('[data-cascading-slider-prev]');
    const nextButton = wrapper.querySelector('[data-cascading-slider-next]');
    const slides = Array.from(viewport.querySelectorAll('[data-cascading-slide]'));
    let totalSlides = slides.length;

    if (totalSlides === 0) return;

    if (totalSlides < 9) {
      const originalSlides = slides.slice();
      while (slides.length < 9) {
        originalSlides.forEach(function(original) {
          const clone = original.cloneNode(true);
          clone.setAttribute('data-clone', '');
          viewport.appendChild(clone);
          slides.push(clone);
        });
      }
      totalSlides = slides.length;
    }

    let activeIndex = 0;
    let isAnimating = false;
    let slideWidth = 0;
    let slotCenters = {};
    let slotWidths = {};

    function readGap() {
      const raw = getComputedStyle(viewport).getPropertyValue('--gap').trim();
      if (!raw) return 0;
      const temp = document.createElement('div');
      temp.style.width = raw;
      temp.style.position = 'absolute';
      temp.style.visibility = 'hidden';
      viewport.appendChild(temp);
      const px = temp.offsetWidth;
      viewport.removeChild(temp);
      return px;
    }

    function getSettings() {
      const windowWidth = window.innerWidth;
      for (let i = 0; i < breakpoints.length; i++) {
        if (windowWidth <= breakpoints[i].maxWidth) return breakpoints[i];
      }
      return breakpoints[breakpoints.length - 1];
    }

    function getOffset(slideIndex, fromIndex) {
      if (fromIndex === undefined) fromIndex = activeIndex;
      let distance = slideIndex - fromIndex;
      const half = totalSlides / 2;
      if (distance > half) distance -= totalSlides;
      if (distance < -half) distance += totalSlides;
      return distance;
    }

    function measure() {
      const settings = getSettings();
      const viewportWidth = viewport.offsetWidth;
      const gap = readGap();

      const activeSlideWidth = viewportWidth * settings.activeWidth;
      const siblingSlideWidth = viewportWidth * settings.siblingWidth;
      const farSlideWidth = Math.max(0, (viewportWidth - activeSlideWidth - 2 * siblingSlideWidth - 4 * gap) / 2);

      slideWidth = activeSlideWidth;

      const visibleSlots = [
        { slot: -2, width: farSlideWidth },
        { slot: -1, width: siblingSlideWidth },
        { slot: 0, width: activeSlideWidth },
        { slot: 1, width: siblingSlideWidth },
        { slot: 2, width: farSlideWidth },
      ];

      let x = 0;
      visibleSlots.forEach(function(def, i) {
        slotCenters[String(def.slot)] = x + def.width / 2;
        slotWidths[String(def.slot)] = def.width;
        if (i < visibleSlots.length - 1) x += def.width + gap;
      });

      slotCenters['-3'] = slotCenters['-2'] - farSlideWidth / 2 - gap - farSlideWidth / 2;
      slotWidths['-3'] = farSlideWidth;
      slotCenters['3'] = slotCenters['2'] + farSlideWidth / 2 + gap + farSlideWidth / 2;
      slotWidths['3'] = farSlideWidth;

      slides.forEach(function(slide) {
        slide.style.width = slideWidth + 'px';
      });
    }

    function getSlideProps(offset) {
      const clamped = Math.max(-3, Math.min(3, offset));
      const slotWidth = slotWidths[String(clamped)];
      const clipAmount = Math.max(0, (slideWidth - slotWidth) / 2);
      const translateX = slotCenters[String(clamped)] - slideWidth / 2;

      return {
        x: translateX,
        '--clip': clipAmount,
        zIndex: 10 - Math.abs(clamped),
      };
    }

    function layout(animate, previousIndex) {
      slides.forEach(function(slide, index) {
        const offset = getOffset(index);

        if (offset < -3 || offset > 3) {
          if (animate && previousIndex !== undefined) {
            const previousOffset = getOffset(index, previousIndex);
            if (previousOffset >= -2 && previousOffset <= 2) {
              const exitSlot = previousOffset < 0 ? -3 : 3;
              gsap.to(slide, Object.assign({}, getSlideProps(exitSlot), {
                duration: duration,
                ease: ease,
                overwrite: true,
              }));
              return;
            }
          }

          const parkSlot = offset < 0 ? -3 : 3;
          gsap.set(slide, getSlideProps(parkSlot));
          return;
        }

        const props = getSlideProps(offset);
        slide.setAttribute('data-status', offset === 0 ? 'active' : 'inactive');

        if (animate) {
          gsap.to(slide, Object.assign({}, props, {
            duration: duration,
            ease: ease,
            overwrite: true,
          }));
        } else {
          gsap.set(slide, props);
        }
      });
    }

    function goTo(targetIndex) {
      const normalizedTarget = ((targetIndex % totalSlides) + totalSlides) % totalSlides;
      if (isAnimating || normalizedTarget === activeIndex) return;
      isAnimating = true;

      const previousIndex = activeIndex;
      const travelDirection = getOffset(normalizedTarget, previousIndex) > 0 ? 1 : -1;

      slides.forEach(function(slide, index) {
        const currentOffset = getOffset(index, previousIndex);
        const nextOffset = getOffset(index, normalizedTarget);
        const wasInRange = currentOffset >= -3 && currentOffset <= 3;
        const willBeVisible = nextOffset >= -2 && nextOffset <= 2;

        if (!wasInRange && willBeVisible) {
          const entrySlot = travelDirection > 0 ? 3 : -3;
          gsap.set(slide, getSlideProps(entrySlot));
        }

        const wasInvisible = Math.abs(currentOffset) >= 3;
        const willBeStaging = Math.abs(nextOffset) === 3;
        const crossesSides = currentOffset * nextOffset < 0;
        if (wasInvisible && willBeStaging && crossesSides) {
          gsap.set(slide, getSlideProps(nextOffset > 0 ? 3 : -3));
        }
      });

      activeIndex = normalizedTarget;
      layout(true, previousIndex);
      gsap.delayedCall(duration + 0.05, function() { isAnimating = false; });
    }

    if (prevButton) prevButton.addEventListener('click', function() { goTo(activeIndex - 1); });
    if (nextButton) nextButton.addEventListener('click', function() { goTo(activeIndex + 1); });

    slides.forEach(function(slide, index) {
      slide.addEventListener('click', function() {
        if (index !== activeIndex) goTo(index);
      });
    });

    document.addEventListener('keydown', function(event) {
      if (event.key === 'ArrowLeft') goTo(activeIndex - 1);
      if (event.key === 'ArrowRight') goTo(activeIndex + 1);
    });

    let resizeTimer;
    window.addEventListener('resize', function() {
      clearTimeout(resizeTimer);
      resizeTimer = setTimeout(function() {
        measure();
        layout(false);
      }, 100);
    });

    measure();
    layout(false);
  }
}

  initCascadingSlider();
  </script>
Section / Table
Components
UPS Runtime
Service Life
Maintenance
Monitoring
Footprint
Thermal Conditions
Recovery
End-of-Life
Acculon Lithium
5-15 Minutes
10-15 Years
Monitored reduced routine service
Integrated BMS; alarms + reporting
Higher energy density; smaller footprint
BMS + thermal protections; site-dependent
Fastest recharge potential
Recycling pathway required; regulated handling
Lead-Acid
5-15 Minutes
3-5 Years
Regular inspections/testing; more frequent service
External monitoring; configuration-dependent
Lower energy density; larger footprint
Temperature impacts life; HVAC-sensitive
Slower recharge typical
Slower recharge typical
Section / Slider
Components

Utilities

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.

Utilities

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.

Utilities

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.

// required script in <head> tag
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">

// required script in before closing </body> tag
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
// required script in before closing </body> tag

<script>
 function initSliderComponents() {
  if (typeof Swiper === "undefined" || typeof gsap === "undefined") {
    setTimeout(initSliderComponents, 50);
    return;
  }

  document.querySelectorAll(".slider_component").forEach((component) => {
    const swiperEl = component.querySelector(".swiper");
    const wrapper = component.querySelector(".swiper-wrapper");

    if (!swiperEl || !wrapper) return;

    if (swiperEl.swiper) {
      swiperEl.swiper.destroy(true, true);
    }

    wrapper.querySelectorAll("[data-slider-clone='true']").forEach((slide) => {
      slide.remove();
    });

    const originalSlides = Array.from(wrapper.children).filter((slide) => {
      return slide.classList.contains("swiper-slide");
    });

    if (originalSlides.length < 2) return;

    const cloneSets = 3;
    const slideCount = originalSlides.length;
    const middleStart = slideCount * cloneSets;

    let activeDotIndex = null;

    originalSlides.forEach((slide, index) => {
      slide.dataset.sliderIndex = index;
    });

    for (let i = 0; i < cloneSets; i++) {
      originalSlides.forEach((slide, index) => {
        const clone = slide.cloneNode(true);
        clone.dataset.sliderClone = "true";
        clone.dataset.sliderIndex = index;
        wrapper.appendChild(clone);
      });
    }

    for (let i = 0; i < cloneSets; i++) {
      [...originalSlides].reverse().forEach((slide, reverseIndex) => {
        const index = slideCount - 1 - reverseIndex;
        const clone = slide.cloneNode(true);
        clone.dataset.sliderClone = "true";
        clone.dataset.sliderIndex = index;
        wrapper.insertBefore(clone, wrapper.firstChild);
      });
    }

    component.querySelectorAll(".slide-dot-matrix").forEach((svg) => {
      const dots = Array.from(svg.querySelectorAll("circle"));

      const sortedDots = [...dots].sort((a, b) => {
        return getDotSortValue(a) - getDotSortValue(b);
      });

      svg.dotMatrixDots = dots;
      svg.dotMatrixSortedDots = sortedDots;
      svg.dotMatrixTimeline = null;

      gsap.set(svg, {
        autoAlpha: 0
      });

      gsap.set(dots, {
        autoAlpha: 0
      });
    });

    const swiper = new Swiper(swiperEl, {
      slidesPerView: 3.5,
      slidesPerGroup: 1,
      spaceBetween: 2,
      speed: 900,

      initialSlide: middleStart,
      centeredSlides: true,
      slideActiveClass: "is-active",

      loop: false,
      rewind: false,

      allowTouchMove: true,
      simulateTouch: true,
      followFinger: true,
      grabCursor: true,
      slideToClickedSlide: true,

      keyboard: {
        enabled: true,
        onlyInViewport: true
      },

      observer: true,
      observeParents: true,
      resizeObserver: true,

      breakpoints: {
        0: {
          slidesPerView: 1.5
        },
        768: {
          slidesPerView: 2.25
        },
        992: {
          slidesPerView: 3.5
        }
      },

      on: {
        init(swiper) {
          swiper.slideTo(middleStart, 0, false);
          syncDotMatrices(swiper, true);
        },
        slideChangeTransitionStart(swiper) {
          syncDotMatrices(swiper, true);
        },
        slideChangeTransitionEnd(swiper) {
          resetSliderToMiddle(swiper);
          syncDotMatrices(swiper, false);
        },
        resize(swiper) {
          swiper.update();
          resetSliderToMiddle(swiper);
          syncDotMatrices(swiper, false);
        }
      }
    });

    requestAnimationFrame(() => {
      swiper.update();
      swiper.slideTo(middleStart, 0, false);
      syncDotMatrices(swiper, false);
    });

    function syncDotMatrices(swiper, shouldAnimate) {
      const activeSlide = swiper.slides[swiper.activeIndex];
      if (!activeSlide) return;

      const nextDotIndex = activeSlide.dataset.sliderIndex;

      if (shouldAnimate && nextDotIndex === activeDotIndex) return;

      activeDotIndex = nextDotIndex;

      swiper.slides.forEach((slide) => {
        const matrix = slide.querySelector(".slide-dot-matrix");
        if (!matrix) return;

        if (slide === activeSlide) {
          showDotMatrix(matrix, shouldAnimate);
        } else {
          hideDotMatrix(matrix);
        }
      });
    }

    function showDotMatrix(matrix, shouldAnimate) {
      const dots = matrix.dotMatrixDots || [];
      const sortedDots = matrix.dotMatrixSortedDots || [];

      if (matrix.dotMatrixTimeline) {
        matrix.dotMatrixTimeline.kill();
      }

      gsap.killTweensOf(matrix);
      gsap.killTweensOf(dots);

      if (!shouldAnimate) {
        gsap.set(matrix, {
          autoAlpha: 1
        });

        gsap.set(dots, {
          autoAlpha: 1
        });

        return;
      }

      matrix.dotMatrixTimeline = gsap.timeline();

      matrix.dotMatrixTimeline.set(matrix, {
        autoAlpha: 1
      });

      matrix.dotMatrixTimeline.fromTo(
        sortedDots,
        {
          autoAlpha: 0
        },
        {
          autoAlpha: 1,
          duration: 0.28,
          ease: "power1.out",
          stagger: {
            each: 0.004,
            from: "start"
          }
        }
      );
    }

    function hideDotMatrix(matrix) {
      const dots = matrix.dotMatrixDots || [];

      if (matrix.dotMatrixTimeline) {
        matrix.dotMatrixTimeline.kill();
      }

      gsap.killTweensOf(matrix);
      gsap.killTweensOf(dots);

      gsap.set(matrix, {
        autoAlpha: 0
      });

      gsap.set(dots, {
        autoAlpha: 0
      });
    }

    function resetSliderToMiddle(swiper) {
      const activeSlide = swiper.slides[swiper.activeIndex];
      if (!activeSlide) return;

      const realIndex = Number(activeSlide.dataset.sliderIndex);
      const targetIndex = middleStart + realIndex;

      if (swiper.activeIndex < middleStart || swiper.activeIndex >= middleStart + slideCount) {
        swiper.slideTo(targetIndex, 0, false);
      }
    }

    function getDotSortValue(dot) {
      const x = Number(dot.getAttribute("cx"));
      const y = Number(dot.getAttribute("cy"));

      return (x + y) * 100 + y;
    }
  });
}
initSliderComponents();
  </script>
Section / Technology
Components
01

Cell Strategy

Cell selection sets the technical foundation for the full system. Acculon uses testing, modeling, and application requirements to narrow the chemistry and performance choices that matter.

Chemistry Selection
Sodium-Ion, Lithium-Ion
Cell Modeling
Capacity, Degradation, Cycle Life
Lab Validation
Thermal, Abuse, Performance Testing
02

Module Architecture

At the module level, CORE architecture translates cell decisions into a repeatable building block with thermal isolation, cell-to-cell fusing, and adaptable configuration logic.

CORE Architecture
24V and 48V Module Platforms
Protection Strategy
Thermal Isolation, Cell-to-Cell Fusing
Module Configuration
Scalable Building Blocks
03

Pack Integration

Pack design brings power, energy, voltage, packaging, and operating environment into one manufacturable form factor that can move beyond prototype thinking.

Electrical Design
High and Low-Voltage Systems
Thermal Management
Passive Cooling, Active Heating
Packaging Fit
Packaging, Enclosure, Mounting
04

System Intelligence

BMS controls, telemetry, SOC/SOH insight, and certification pathways complete the system story, turning the battery into a connected product platform.

BMS Controls
UL-Certifiable Control Architecture
Connected Insight
SOC, SOH, Telemetry Data
Validation Pathway
Testing and Certification Support
// required script in before closing </body> tag
function initVideoScroll() {
  var ROOT_SELECTOR = "[data-video-scroll-init]";
  var ELEMENT_SELECTOR = "[data-video-scroll-element]";
  var MOBILE_MAX = 767;

  var roots = Array.prototype.slice
    .call(document.querySelectorAll(ROOT_SELECTOR))
    .filter(function (root) {
      return !!root.querySelector(ELEMENT_SELECTOR);
    });

  roots.forEach(initInstance);

  function initInstance(player, playerIndex) {
    destroyPreviousInstance(player);

    var slot =
      player.querySelector(".technology-slot") ||
      player.querySelector("[data-scroll-element-slot]") ||
      player.querySelector("[data-video-scroll-slot]") ||
      player;

    var container =
      (player.classList && player.classList.contains("technology-container") && player) ||
      player.querySelector(".technology-container") ||
      (slot && slot.closest(".technology-container")) ||
      player;

    var scrollElements = Array.prototype.slice.call(
      slot.querySelectorAll(ELEMENT_SELECTOR)
    );

    if (!scrollElements.length) return;

    var mediaShell =
      player.querySelector(".technology-video [data-player-src]") ||
      player.querySelector("[data-video-scroll-media]") ||
      player.querySelector("[data-player-src]") ||
      player;

    var video =
      mediaShell.querySelector("video") ||
      player.querySelector(".technology-video video") ||
      player.querySelector("video");

    if (!video) {
      console.warn("[VideoScroll] No video found inside:", player);
      return;
    }

    var src =
      mediaShell.getAttribute("data-player-src") ||
      video.getAttribute("data-player-src") ||
      video.getAttribute("data-src") ||
      video.getAttribute("src") ||
      getChildSource(mediaShell) ||
      getChildSource(player);

    if (!src) {
      console.warn("[VideoScroll] No video source found:", player);
      return;
    }

    var SCRUB = parseFloat(player.getAttribute("data-video-scroll-scrub"));
    if (isNaN(SCRUB)) SCRUB = 0.35;

    var SEEK_FPS = parseFloat(player.getAttribute("data-video-scroll-seek-fps"));
    if (isNaN(SEEK_FPS)) SEEK_FPS = 16;

    var SEEK_THROTTLE_MS = 1000 / SEEK_FPS;

    var SEEK_THRESHOLD = parseFloat(player.getAttribute("data-video-scroll-threshold"));
    if (isNaN(SEEK_THRESHOLD)) SEEK_THRESHOLD = 1 / 45;

    var FAST_SEEK_DISTANCE = parseFloat(
      player.getAttribute("data-video-scroll-fast-seek-distance")
    );
    if (isNaN(FAST_SEEK_DISTANCE)) FAST_SEEK_DISTANCE = 1.25;

    var END_PADDING = parseFloat(player.getAttribute("data-video-scroll-end-padding"));
    if (isNaN(END_PADDING)) END_PADDING = 0.05;

    var PRELOAD_MARGIN =
      player.getAttribute("data-video-scroll-preload-margin") || "150% 0px";

    var DESKTOP_START =
      player.getAttribute("data-video-scroll-desktop-start") ||
      player.getAttribute("data-video-scroll-global-start") ||
      "top top";

    var DESKTOP_END =
      player.getAttribute("data-video-scroll-desktop-end") ||
      player.getAttribute("data-video-scroll-global-end") ||
      "bottom bottom";

    var MOBILE_START =
      player.getAttribute("data-video-scroll-mobile-start") ||
      "top top";

    var MOBILE_END =
      player.getAttribute("data-video-scroll-mobile-end") ||
      "bottom bottom";

    var isHls = /\.m3u8(\?|$)/i.test(src);
    var isSafariNative = !!video.canPlayType("application/vnd.apple.mpegurl");
    var canUseHlsJs = !!(window.Hls && window.Hls.isSupported()) && !isSafariNative;

    var hls = null;
    var activeProgressTrigger = null;
    var setupComplete = false;

    var duration = 0;
    var hasMetadata = false;
    var isAttached = false;
    var isSeeking = false;
    var rafId = 0;
    var lastSeekAt = 0;
    var lastAppliedTime = NaN;
    var pendingTime = null;
    var currentProgress = 0;
    var seekTimeout = null;
    var preloadObserver = null;

    player._videoScrollCleanup = [];

    setStatus("idle");
    setActivated(false);
    prepareVideoElement();

    waitForDeps(function () {
      setupResponsiveTriggers();
      setupMediaPreload();
      syncToCurrentScroll(true);

      window.addEventListener("load", handleLateSync, { once: true });
      window.addEventListener("pageshow", handleLateSync);
      window.addEventListener("orientationchange", handleLateSync);

      var debouncedLateSync = debounce(handleLateSync, 120);
      window.addEventListener("resize", debouncedLateSync);

      player._videoScrollCleanup.push(function () {
        window.removeEventListener("pageshow", handleLateSync);
        window.removeEventListener("orientationchange", handleLateSync);
        window.removeEventListener("resize", debouncedLateSync);
      });
    });

    function prepareVideoElement() {
      video.muted = true;
      video.defaultMuted = true;
      video.setAttribute("muted", "");
      video.setAttribute("playsinline", "");
      video.setAttribute("webkit-playsinline", "");
      video.playsInline = true;
      video.preload = "auto";
      video.autoplay = false;
      video.loop = false;
      video.controls = false;
      if ("disableRemotePlayback" in video) video.disableRemotePlayback = true;

      video.addEventListener("loadedmetadata", handleMetadata);
      video.addEventListener("durationchange", handleMetadata);

      video.addEventListener("seeking", function () {
        isSeeking = true;
      });

      video.addEventListener("seeked", function () {
        isSeeking = false;
        clearSeekTimeout();
        if (pendingTime != null) scheduleSeek(true);
      });

      video.addEventListener("canplay", function () {
        if (hasMetadata) {
          setActivated(true);
          setStatus(isInScrubRange(currentProgress) ? "scrubbing" : "ready");
        }
      });

      video.addEventListener("error", function () {
        setStatus("error");
      });
    }

    function setupMediaPreload() {
      if (!("IntersectionObserver" in window)) {
        attachMediaOnce();
        return;
      }

      preloadObserver = new IntersectionObserver(
        function (entries) {
          entries.forEach(function (entry) {
            if (entry.isIntersecting) {
              attachMediaOnce();
              if (preloadObserver) preloadObserver.disconnect();
              preloadObserver = null;
            }
          });
        }, { root: null, rootMargin: PRELOAD_MARGIN, threshold: 0 }
      );

      preloadObserver.observe(player);

      player._videoScrollCleanup.push(function () {
        if (preloadObserver) preloadObserver.disconnect();
        preloadObserver = null;
      });

      if (isElementNearViewport(player, 1.5)) attachMediaOnce();
    }

    function attachMediaOnce() {
      if (isAttached) return;
      isAttached = true;
      setStatus("loading");

      try {
        video.pause();
        video.removeAttribute("src");
        video.load();
      } catch (_) {}

      if (isHls && canUseHlsJs) {
        hls = new Hls({
          autoStartLoad: true,
          startFragPrefetch: true,
          lowLatencyMode: false,
          enableWorker: true,
          capLevelToPlayerSize: true,
          maxBufferLength: 24,
          maxMaxBufferLength: 60,
          backBufferLength: 12,
          maxBufferSize: 80 * 1000 * 1000,
          abrEwmaDefaultEstimate: 3500000
        });

        hls.attachMedia(video);

        hls.on(Hls.Events.MEDIA_ATTACHED, function () {
          hls.loadSource(src);
        });

        hls.on(Hls.Events.MANIFEST_PARSED, function (_event, data) {
          var cap = chooseLevelCap(data && data.levels ? data.levels : []);
          if (cap > -1) hls.autoLevelCapping = cap;
          syncToCurrentScroll(true);
        });

        hls.on(Hls.Events.ERROR, function (_event, data) {
          if (data && data.fatal) {
            console.warn("[VideoScroll] HLS fatal error:", data);
            setStatus("error");
          }
        });

        player._hls = hls;
      } else {
        video.src = src;
        video.load();
      }
    }

    function handleMetadata() {
      if (!video.duration || isNaN(video.duration) || video.duration === Infinity) return;

      duration = video.duration;
      hasMetadata = true;

      syncToCurrentScroll(true);
      warmDecoder();

      setActivated(true);
      setStatus(isInScrubRange(currentProgress) ? "scrubbing" : "ready");
    }

    function setupResponsiveTriggers() {
      if (setupComplete) return;
      setupComplete = true;

      gsap.registerPlugin(ScrollTrigger);

      var mm = gsap.matchMedia();
      player._mm = mm;

      mm.add("(min-width: " + (MOBILE_MAX + 1) + "px)", function () {
        var firstElement = scrollElements[0];
        var lastElement = scrollElements[scrollElements.length - 1];

        var trigger = ScrollTrigger.create({
          trigger: slot,
          startTrigger: firstElement,
          start: DESKTOP_START,
          endTrigger: lastElement,
          end: DESKTOP_END,
          invalidateOnRefresh: true,
          onUpdate: function (self) {
            updateProgress(self.progress, false);
          },
          onRefresh: function (self) {
            updateProgress(self.progress, true);
          },
          onEnter: function () {
            setStatus("scrubbing");
          },
          onEnterBack: function () {
            setStatus("scrubbing");
          },
          onLeave: function () {
            updateProgress(1, true);
            setStatus("ready");
          },
          onLeaveBack: function () {
            updateProgress(0, true);
            setStatus("ready");
          }
        });

        activeProgressTrigger = trigger;
        player._videoScrollTriggers = [trigger];

        requestSyncs();

        return function cleanup() {
          try {
            trigger.kill();
          } catch (_) {}
          if (activeProgressTrigger === trigger) activeProgressTrigger = null;
        };
      });

      mm.add("(max-width: " + MOBILE_MAX + "px)", function () {
        gsap.set(slot, { willChange: "transform" });

        var slotTween = gsap.to(slot, {
          xPercent: -100,
          x: function () {
            return window.innerWidth - 4;
          },
          ease: "none",
          scrollTrigger: {
            trigger: container,
            start: MOBILE_START,
            end: MOBILE_END,
            scrub: true,
            invalidateOnRefresh: true
          }
        });

        var trigger = ScrollTrigger.create({
          trigger: container,
          start: MOBILE_START,
          end: MOBILE_END,
          invalidateOnRefresh: true,
          onUpdate: function (self) {
            updateProgress(self.progress, false);
          },
          onRefresh: function (self) {
            updateProgress(self.progress, true);
          },
          onEnter: function () {
            setStatus("scrubbing");
          },
          onEnterBack: function () {
            setStatus("scrubbing");
          },
          onLeave: function () {
            updateProgress(1, true);
            setStatus("ready");
          },
          onLeaveBack: function () {
            updateProgress(0, true);
            setStatus("ready");
          }
        });

        activeProgressTrigger = trigger;
        player._videoScrollTriggers = [trigger];

        requestSyncs();

        return function cleanup() {
          try {
            if (slotTween.scrollTrigger) slotTween.scrollTrigger.kill();
            slotTween.kill();
          } catch (_) {}

          try {
            trigger.kill();
          } catch (_) {}

          if (activeProgressTrigger === trigger) activeProgressTrigger = null;
          gsap.set(slot, { clearProps: "transform,willChange" });
        };
      });

      ScrollTrigger.addEventListener("refresh", syncAfterRefresh);
      player._videoScrollCleanup.push(function () {
        ScrollTrigger.removeEventListener("refresh", syncAfterRefresh);
      });

      ScrollTrigger.refresh();
    }

    function updateProgress(progress, force) {
      currentProgress = clamp(progress, 0, 1);

      if (!hasMetadata) {
        pendingTime = null;
        return;
      }

      var next = progressToTime(currentProgress);
      requestSeek(next, force);

      if (isInScrubRange(currentProgress)) {
        setStatus("scrubbing");
      }
    }

    function progressToTime(progress) {
      if (!duration) return 0;

      var count = Math.max(scrollElements.length, 1);
      var p = clamp(progress, 0, 1);

      if (p <= 0.001) return 0.001;
      if (p >= 0.999) return getEndTime();

      var rawChunk = p * count;
      var index = Math.min(count - 1, Math.floor(rawChunk));
      var local = rawChunk - index;

      var chunkStart = (duration * index) / count;
      var chunkEnd = (duration * (index + 1)) / count;

      return clamp(chunkStart + (chunkEnd - chunkStart) * local, 0.001, getEndTime());
    }

    function requestSeek(time, force) {
      pendingTime = clamp(time, 0.001, getEndTime());
      scheduleSeek(!!force);
    }

    function scheduleSeek(force) {
      if (rafId) return;

      rafId = requestAnimationFrame(function () {
        rafId = 0;
        flushSeek(!!force);
      });
    }

    function flushSeek(force) {
      if (pendingTime == null || !hasMetadata) return;

      var now = performance.now();

      if (!force && now - lastSeekAt < SEEK_THROTTLE_MS) {
        scheduleSeek(false);
        return;
      }

      if (!force && isSeeking) {
        scheduleSeek(false);
        return;
      }

      var next = pendingTime;
      var current = isFinite(video.currentTime) ? video.currentTime : 0;

      if (!force && isFinite(lastAppliedTime)) {
        if (Math.abs(next - lastAppliedTime) < SEEK_THRESHOLD) {
          pendingTime = null;
          return;
        }
      }

      pendingTime = null;
      lastSeekAt = now;
      lastAppliedTime = next;

      try {
        var distance = Math.abs(next - current);

        if (
          distance >= FAST_SEEK_DISTANCE &&
          typeof video.fastSeek === "function"
        ) {
          video.fastSeek(next);
        } else {
          video.currentTime = next;
        }

        isSeeking = true;
        clearSeekTimeout();
        seekTimeout = setTimeout(function () {
          isSeeking = false;
          seekTimeout = null;
          if (pendingTime != null) scheduleSeek(true);
        }, 350);
      } catch (_) {
        isSeeking = false;
      }
    }

    function syncToCurrentScroll(force) {
      if (activeProgressTrigger) {
        updateProgress(activeProgressTrigger.progress || 0, !!force);
        return;
      }

      var rect = container.getBoundingClientRect();
      var viewport = window.innerHeight || document.documentElement.clientHeight;
      var total = rect.height - viewport;

      if (total <= 0) {
        updateProgress(rect.top <= 0 ? 1 : 0, !!force);
        return;
      }

      var progress = clamp((0 - rect.top) / total, 0, 1);
      updateProgress(progress, !!force);
    }

    function syncAfterRefresh() {
      syncToCurrentScroll(true);
    }

    function handleLateSync() {
      requestSyncs();
    }

    function requestSyncs() {
      syncToCurrentScroll(true);

      requestAnimationFrame(function () {
        syncToCurrentScroll(true);
      });

      setTimeout(function () {
        syncToCurrentScroll(true);
      }, 80);

      setTimeout(function () {
        syncToCurrentScroll(true);
      }, 250);
    }

    function warmDecoder() {
      if (!hasMetadata) return;

      var playPromise;

      try {
        playPromise = video.play();
      } catch (_) {
        return;
      }

      var pauseAndSync = function () {
        try {
          video.pause();
        } catch (_) {}
        syncToCurrentScroll(true);
      };

      if (playPromise && typeof playPromise.then === "function") {
        playPromise.then(pauseAndSync).catch(pauseAndSync);
      } else {
        pauseAndSync();
      }
    }

    function chooseLevelCap(levels) {
      if (!levels || !levels.length) return -1;

      var rect = video.getBoundingClientRect();
      var targetWidth =
        (rect.width || video.clientWidth || video.width || 1280) *
        Math.min(window.devicePixelRatio || 1, 2);
      var targetHeight =
        (rect.height || video.clientHeight || video.height || 720) *
        Math.min(window.devicePixelRatio || 1, 2);

      var best = levels.length - 1;

      for (var i = 0; i < levels.length; i++) {
        var level = levels[i];
        if (!level) continue;

        if (
          (!level.width || level.width >= targetWidth) &&
          (!level.height || level.height >= targetHeight)
        ) {
          best = i;
          break;
        }
      }

      return best;
    }

    function getEndTime() {
      if (!duration) return 0.001;
      return Math.max(0.001, duration - END_PADDING);
    }

    function setStatus(status) {
      player.setAttribute("data-player-status", status);
      if (mediaShell && mediaShell.setAttribute) {
        mediaShell.setAttribute("data-player-status", status);
      }
    }

    function setActivated(value) {
      player.setAttribute("data-player-activated", value ? "true" : "false");
      if (mediaShell && mediaShell.setAttribute) {
        mediaShell.setAttribute("data-player-activated", value ? "true" : "false");
      }
    }

    function isInScrubRange(progress) {
      return progress > 0.001 && progress < 0.999;
    }

    function clearSeekTimeout() {
      if (seekTimeout) {
        clearTimeout(seekTimeout);
        seekTimeout = null;
      }
    }
  }

  function destroyPreviousInstance(player) {
    if (player._videoScrollCleanup) {
      player._videoScrollCleanup.forEach(function (cleanup) {
        try {
          cleanup();
        } catch (_) {}
      });
    }

    if (player._mm) {
      try {
        player._mm.revert();
      } catch (_) {}
      player._mm = null;
    }

    if (player._videoScrollTriggers) {
      player._videoScrollTriggers.forEach(function (trigger) {
        try {
          trigger.kill();
        } catch (_) {}
      });
      player._videoScrollTriggers = null;
    }

    if (player._hls) {
      try {
        player._hls.destroy();
      } catch (_) {}
      player._hls = null;
    }

    player._videoScrollCleanup = [];
  }

  function getChildSource(root) {
    var el =
      root.querySelector("[data-player-src]") ||
      root.querySelector("source[src]");

    if (!el) return "";
    return el.getAttribute("data-player-src") || el.getAttribute("src") || "";
  }

  function waitForDeps(callback) {
    var attempts = 0;

    (function check() {
      attempts++;

      if (window.gsap && window.ScrollTrigger) {
        callback();
        return;
      }

      if (attempts >= 80) {
        console.warn("[VideoScroll] GSAP or ScrollTrigger not found.");
        return;
      }

      setTimeout(check, 50);
    })();
  }

  function isElementNearViewport(el, multiplier) {
    var rect = el.getBoundingClientRect();
    var vh = window.innerHeight || document.documentElement.clientHeight;
    var margin = vh * multiplier;

    return rect.bottom >= -margin && rect.top <= vh + margin;
  }

  function debounce(fn, wait) {
    var timeout = null;

    return function () {
      clearTimeout(timeout);
      timeout = setTimeout(fn, wait);
    };
  }

  function clamp(value, min, max) {
    return Math.min(Math.max(value, min), max);
  }
}

initVideoScroll();
FAQ / FAQ List
Components
FAQ’s
How does Acculon recommend between LFP and sodium-based battery chemistry?
What information does Acculon need to recommend a battery configuration?
When should I use a 4U module versus a 45U battery rack?
How do I determine the right runtime for a data center UPS battery system?