NEW RELEASE
v2.2 - 40+ new elementsWhat's new
<!DOCTYPE html>
<html lang="en">
<head>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">

  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Horizontal Parallax Configurator - BricksFusion</title>
  <style>
    :root {
  --background: #000;
  --card-bg: #1e1e1e;
  --card-bg-hover: #252525;
  --text-primary: #f2f2f7;
  --text-secondary: #8e8e93;
  --accent: #ef6013;
  --accent-hover: #c64c0c;
  --border: #2c2c2e;
  --shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  --track: #2c2c2e;
  --thumb: #ef6013;
  --card-radius: 16px;
  --input-radius: 8px;
  --button-radius: 12px;
  --transition: all 0.25s ease;
  --font: 'Inter', BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --action-bar-height: 70px;
  --success: #28a745;
  --warning: #ffc107;
  --danger: #dc3545;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: var(--font);
  background-color: var(--background);
  color: var(--text-primary);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  padding-bottom: var(--action-bar-height);
}

.action-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--action-bar-height);
  background: linear-gradient(145deg, #1a1a1a, #0f0f0f);
  border-top: 1px solid var(--border);
  z-index: 1000;
  display: flex;
  align-items: center;
  padding: 0 1.5rem;
  gap: 1rem;
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(10px);
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1;
}

.breadcrumb-item {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  text-decoration: none;
  transition: var(--transition);
  padding: 0.5rem 0.75rem;
  border-radius: 6px;
}

.breadcrumb-item:hover {
  color: var(--text-primary);
  background-color: rgba(255, 255, 255, 0.05);
}

.breadcrumb-item.active {
  color: var(--accent);
  background-color: rgba(239, 96, 19, 0.1);
}

.breadcrumb-separator {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  opacity: 0.5;
}

.action-buttons {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.action-btn {
  padding: 0.6rem 1rem;
  background-color: var(--card-bg);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: var(--text-xs);
  font-weight: 500;
  border: 1px solid var(--border);
  border-radius: var(--button-radius);
  cursor: pointer;
  transition: var(--transition);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  white-space: nowrap;
}

.action-btn:hover {
  background-color: var(--card-bg-hover);
  border-color: var(--accent);
  transform: translateY(-1px);
}

.action-btn.primary {
  background: linear-gradient(90deg, var(--accent), #ff8c51);
  border-color: var(--accent);
  color: white;
}

.action-btn.primary:hover {
  background: linear-gradient(90deg, var(--accent-hover), #e67a3f);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(239, 96, 19, 0.3);
}

.data-attribute-display {
  background-color: rgba(50, 50, 50, 0.8);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.5rem 0.75rem;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: var(--text-xs);
  color: #ff8c51;
  cursor: pointer;
  transition: var(--transition);
  user-select: all;
}

.data-attribute-display:hover {
  background-color: rgba(239, 96, 19, 0.2);
  border-color: var(--accent);
}

.container {
  max-width: 100%;
  margin: 0 auto;
  padding: 2rem 1.5rem;
}

.page-header {
  text-align: center;
  margin-bottom: 2rem;
}

.page-title {
  font-size: 2.5rem;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 0.5rem;
  background: linear-gradient(90deg, var(--accent), #ff8c51);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.page-subtitle {
  font-size: var(--text-s);
  color: var(--text-secondary);
  font-weight: 500;
}

.instructions-toggle {
  margin-bottom: 2rem;
}

.instructions-card {
  background-color: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--card-radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: var(--transition);
}

.instructions-header {
  padding: 1rem 1.5rem;
  cursor: pointer;
  transition: var(--transition);
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid transparent;
}

.instructions-header:hover {
  background-color: var(--card-bg-hover);
}

.instructions-card.expanded .instructions-header {
  border-bottom-color: var(--border);
}

.instructions-title {
  font-size: var(--text-s);
  font-weight: 600;
}

.toggle-icon {
  font-size: 1.2em;
  transition: transform 0.3s ease;
}

.toggle-icon.expanded {
  transform: rotate(180deg);
}

.instructions-content {
  padding: 0 1.5rem;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease, padding 0.3s ease;
}

.instructions-content.show {
  max-height: 500px;
  padding: 1.5rem;
}

.instructions-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}

.how-to-use ol {
  padding-left: 1.5rem;
}

.how-to-use li {
  margin-bottom: 0.75rem;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.6;
}

.how-to-use strong {
  color: var(--text-primary);
  font-weight: 600;
}

.how-to-use code {
  background-color: rgba(50, 50, 50, 0.5);
  padding: 0.2rem 0.5rem;
  border-radius: 4px;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: var(--text-xs);
  color: #ff8c51;
}

.content {
  display: grid;
  grid-template-columns: 1fr 500px;
  gap: 2rem;
  align-items: start;
}

.preview-section {
  position: sticky;
  top: 2rem;
}

.controls-section {
  max-width: 500px;
}

.card {
  background-color: var(--card-bg);
  border-radius: var(--card-radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  margin-bottom: 1.5rem;
  border: 1px solid var(--border);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}

.preview-container {
  height: 400px;
  width: 100%;
  position: relative;
  overflow-y: auto;
  overflow-x: hidden;
  border-radius: var(--card-radius);
  background-color: #252525;
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  scroll-behavior: smooth;
}

.preview-container::-webkit-scrollbar {
  width: 8px;
}

.preview-container::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 4px;
}

.preview-container::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-radius: 4px;
}

.preview-container::-webkit-scrollbar-thumb:hover {
  background: var(--accent-hover);
}

.preview-container::before {
  content: '';
  display: block;
  height: 800px;
  width: 100%;
}

.scroll-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 1rem 1.5rem;
  border-radius: 12px;
  backdrop-filter: blur(15px);
  border: 1px solid rgba(255, 255, 255, 0.2);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: var(--text-xs);
  font-weight: 500;
  z-index: 10;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  animation: pulse 2s infinite;
}

.scroll-icon {
  font-size: 1.2em;
  color: var(--accent);
  animation: bounce 1.5s infinite;
}

.scroll-icon:first-child {
  animation-delay: 0s;
}

.scroll-icon:last-child {
  animation-delay: 0.75s;
}

@keyframes pulse {
  0%, 100% {
    transform: translate(-50%, -50%) scale(1);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  }
  50% {
    transform: translate(-50%, -50%) scale(1.05);
    box-shadow: 0 12px 40px rgba(239, 96, 19, 0.2);
  }
}

@keyframes bounce {
  0%, 20%, 50%, 80%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-4px);
  }
  60% {
    transform: translateY(-2px);
  }
}

.card-heading {
  padding: 1rem 1.5rem;
  font-size: var(--text-s);
  font-weight: 600;
  border-bottom: 1px solid var(--border);
  letter-spacing: 0.3px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.card-actions {
  display: flex;
  gap: 0.5rem;
}

.card-action-btn {
  padding: 0.4rem 0.8rem;
  background-color: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  font-size: var(--text-xs);
  transition: var(--transition);
}

.card-action-btn:hover {
  color: var(--text-primary);
  border-color: var(--accent);
  background-color: rgba(239, 96, 19, 0.1);
}

.card-content {
  padding: 1.5rem;
}

.control-group {
  margin-bottom: 1.5rem;
  position: relative;
}

.control-group:last-child {
  margin-bottom: 0;
}

.control-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.75rem;
}

.label-text {
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.2px;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.help-tooltip {
  cursor: help;
  opacity: 0.7;
  transition: var(--transition);
}

.help-tooltip:hover {
  opacity: 1;
  color: var(--accent);
}

.value-display {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.value-text {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  background-color: rgba(50, 50, 50, 0.5);
  padding: 2px 8px;
  border-radius: 4px;
  min-width: 45px;
  text-align: center;
}

.reset-btn {
  padding: 0.2rem 0.4rem;
  background-color: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  font-size: 10px;
  transition: var(--transition);
}

.reset-btn:hover {
  color: var(--danger);
  border-color: var(--danger);
  background-color: rgba(220, 53, 69, 0.1);
}

input[type="range"] {
  -webkit-appearance: none;
  width: 100%;
  height: 6px;
  background: var(--track);
  border-radius: 3px;
  outline: none;
  margin: 0.8rem 0;
  position: relative;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  background: var(--thumb);
  border-radius: 50%;
  cursor: pointer;
  transition: var(--transition);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

input[type="range"]::-webkit-slider-thumb:hover {
  transform: scale(1.2);
  box-shadow: 0 0 10px rgba(239, 96, 19, 0.5);
}

input[type="text"],
input[type="number"],
input[type="url"],
select {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--input-radius);
  font-family: var(--font);
  font-size: var(--text-xs);
  color: var(--text-primary);
  background-color: var(--card-bg);
  margin-bottom: 0.75rem;
  outline: none;
  transition: var(--transition);
}

input[type="text"]:focus,
input[type="number"]:focus,
input[type="url"]:focus,
select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}

.image-input-container {
  position: relative;
  margin-bottom: 1rem;
}

.image-preview {
  width: 100%;
  height: 120px;
  border-radius: var(--input-radius);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border: 2px dashed var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  margin-bottom: 0.75rem;
  transition: var(--transition);
  background-color: rgba(30, 30, 30, 0.5);
}

.image-preview:hover {
  border-color: var(--accent);
}

.image-preview.has-image {
  border-style: solid;
  border-color: var(--accent);
}

.notification {
  position: fixed;
  bottom: calc(var(--action-bar-height) + 1rem);
  left: 50%;
  background-color: var(--success);
  color: white;
  padding: 0.75rem 1rem;
  border-radius: var(--input-radius);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  z-index: 1001;
  transform: translate(-50%, 200px);
  opacity: 0;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease;
  font-size: var(--text-xs);
  font-weight: 500;
  max-width: 320px;
  word-wrap: break-word;
  line-height: 1.4;
  text-align: center;
}

.notification.show {
  transform: translate(-50%, 0);
  opacity: 1;
}

@media (max-width: 1200px) {
  .content {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
  
  .preview-section {
    position: static;
  }
  
  .controls-section {
    max-width: 100%;
  }
}

@media (max-width: 768px) {
  .action-bar {
    flex-direction: column;
    height: auto;
    min-height: var(--action-bar-height);
    padding: 0.75rem;
  }
  
  .breadcrumb {
    order: 1;
    width: 100%;
  }
  
  .action-buttons {
    order: 2;
    width: 100%;
    justify-content: center;
    flex-wrap: wrap;
  }
  
  body {
    padding-bottom: calc(var(--action-bar-height) + 20px);
  }
  
  .notification {
    bottom: calc(var(--action-bar-height) + 2rem);
    max-width: 280px;
    transform: translate(-50%, 250px);
  }
  
  .notification.show {
    transform: translate(-50%, 0);
    opacity: 1;
  }
  
  .preview-container {
    height: 300px;
  }
  
  .data-attribute-display {
    font-size: 10px;
    padding: 0.4rem 0.6rem;
  }
  
  .action-btn {
    font-size: 11px;
    padding: 0.5rem 0.8rem;
  }
  
  .page-title {
    font-size: 2rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

button:focus-visible,
input:focus-visible,
.action-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-track {
  background: var(--background);
}

::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--accent);
}

.loading {
  opacity: 0.6;
  pointer-events: none;
  position: relative;
}

.loading::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}
  </style>
</head>
<body>
  <div class="action-bar">
    <nav class="breadcrumb">
      <a href="https://bricksfusion.com" class="breadcrumb-item">Home</a>
      <span class="breadcrumb-separator">›</span>
      <a href="https://bricksfusion.com/interaction/" class="breadcrumb-item">Interactions</a>
      <span class="breadcrumb-separator">›</span>
      <span class="breadcrumb-item active">Horizontal Parallax</span>
    </nav>
    
    <div class="action-buttons">
      <div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
        data-horizontal-parallax
      </div>
      <button class="action-btn primary" id="download-config" title="Copy JavaScript code (Ctrl+D)" data-protection-animation="true">
        <span>📋</span>
        Copy JS
      </button>
      <button class="action-btn" id="copy-full-section" title="Copy complete section JSON for Bricks Builder (Ctrl+S)" data-protection-animation="true">
        <span>📦</span>
        Copy Full Section
      </button>
    </div>
  </div>

  <div class="container">
    <div class="page-header">
      <h1 class="page-title">Horizontal Parallax</h1>
      <p class="page-subtitle">Interactive background effects for Bricks Builder</p>
    </div>

    <div class="instructions-toggle">
      <div class="instructions-card" id="instructions-card">
        <div class="instructions-header" id="instructions-toggle">
          <div class="instructions-title">
            How to Use & Code Information
          </div>
          <span class="toggle-icon">▼</span>
        </div>
        <div class="instructions-content" id="instructions-content">
          <div class="instructions-grid">
            <div class="how-to-use">
              <ol>
                <li>Customize your horizontal parallax effect using the controls below</li>
                <li>Click <strong>Copy JS</strong> to copy the JavaScript code to clipboard</li>
                <li>In Bricks Builder, add a <strong>Code</strong> element</li>
                <li>Paste or upload the JavaScript code</li>
                <li>To add the effect to any section: go to <strong>Section → Style → Attributes</strong>, add <code>data-horizontal-parallax</code> as attribute name (leave value empty)</li>
              </ol>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="content">
      <section class="preview-section">
        <div class="preview-container" id="parallax-preview" data-horizontal-parallax>
          <div class="scroll-badge">
            <div class="scroll-icon">⬇</div>
            <span>Scroll to see effect</span>
            <div class="scroll-icon">⬆</div>
          </div>
        </div>
      </section>

      <section class="controls-section">
        <div class="card">
          <div class="card-heading">
            Background Image
            <div class="card-actions">
              <button class="card-action-btn" id="reset-image" title="Reset Image">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Image URL</span>
              </div>
              <div class="image-input-container">
                <div class="image-preview" id="image-preview">
                  <span>Enter image URL below</span>
                </div>
                <input type="url" id="image-url" placeholder="https://example.com/image.jpg" value="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&auto=format&fit=crop&w=2000&q=80">
              </div>
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Parallax Settings
            <div class="card-actions">
              <button class="card-action-btn" id="reset-parallax" title="Reset Parallax Settings">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Parallax Intensity
                  <span class="help-tooltip" title="Controls how much the background moves">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="intensity-value">0.2</span></span>
                  <button class="reset-btn" onclick="resetParameter('parallax-intensity', 0.2)">↺</button>
                </div>
              </div>
              <input type="range" id="parallax-intensity" min="0.05" max="0.5" step="0.01" value="0.2">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Zoom Factor
                  <span class="help-tooltip" title="Initial zoom level of the background">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="zoom-value">1.4</span></span>
                  <button class="reset-btn" onclick="resetParameter('zoom-factor', 1.4)">↺</button>
                </div>
              </div>
              <input type="range" id="zoom-factor" min="1.1" max="2.5" step="0.1" value="1.4">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Transition Duration
                  <span class="help-tooltip" title="Smoothness of transitions">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="transition-value">0.3</span>s</span>
                  <button class="reset-btn" onclick="resetParameter('transition-duration', 0.3)">↺</button>
                </div>
              </div>
              <input type="range" id="transition-duration" min="0.1" max="1" step="0.1" value="0.3">
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Visual Filters
            <div class="card-actions">
              <button class="card-action-btn" id="reset-filters" title="Reset Visual Filters">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Brightness
                  <span class="help-tooltip" title="Adjusts image brightness">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="brightness-value">100</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('brightness', 100)">↺</button>
                </div>
              </div>
              <input type="range" id="brightness" min="20" max="200" step="5" value="100">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Contrast
                  <span class="help-tooltip" title="Adjusts image contrast">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="contrast-value">100</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('contrast', 100)">↺</button>
                </div>
              </div>
              <input type="range" id="contrast" min="50" max="200" step="5" value="100">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Saturation
                  <span class="help-tooltip" title="Adjusts color saturation">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="saturation-value">100</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('saturation', 100)">↺</button>
                </div>
              </div>
              <input type="range" id="saturation" min="0" max="200" step="5" value="100">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Blur
                  <span class="help-tooltip" title="Applies blur effect">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="blur-value">0</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('blur', 0)">↺</button>
                </div>
              </div>
              <input type="range" id="blur" min="0" max="10" step="0.5" value="0">
            </div>
          </div>
        </div>
      </section>
    </div>
  </div>

  <div class="notification" id="notification"></div>

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      let parallaxConfig = {
        imageUrl: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&auto=format&fit=crop&w=2000&q=80',
        intensity: 0.2,
        zoomFactor: 1.4,
        transitionDuration: 0.3,
        brightness: 100,
        contrast: 100,
        saturation: 100,
        blur: 0
      };

      const defaultConfig = { ...parallaxConfig };

      let previewParallaxEffect = null;
      let previewState = {
        bgElement: null,
        container: null,
        currentImageUrl: null,
        scrollContainer: null,
        rafId: null,
        throttledParallax: null,
        handleResize: null
      };

      let updateTimeout = null;
      function debouncedUpdatePreview() {
        if (updateTimeout) clearTimeout(updateTimeout);
        updateTimeout = setTimeout(() => {
          updatePreview();
        }, 50);
      }

      function updateConfig() {
        if (!previewState.bgElement || !previewState.container) return;

        const isMobile = window.matchMedia("(max-width: 767px)").matches;
        
        previewState.bgElement.style.transition = `filter ${parallaxConfig.transitionDuration}s ease-in-out, background-size ${parallaxConfig.transitionDuration}s ease-out`;
        
        applyFilters(previewState.bgElement, parallaxConfig.brightness, parallaxConfig.contrast, parallaxConfig.saturation, parallaxConfig.blur);
        
        const img = new Image();
        img.onload = () => {
          const imageRatio = img.width / img.height;
          adjustBackgroundSize(previewState.bgElement, previewState.container, imageRatio, parallaxConfig.zoomFactor, isMobile);
          applyParallax(previewState.bgElement, previewState.container, parallaxConfig.intensity, isMobile);
        };
        img.src = parallaxConfig.imageUrl;
      }

      function initializePreview() {
        const container = document.getElementById('parallax-preview');
        if (!container.id) {
          container.id = 'parallax-preview';
        }
        
        updateContainerAttributes(container);
        
        previewParallaxEffect = createParallaxEffect(container);
        
        updatePreview();
      }

      function updateContainerAttributes(container) {
        container.setAttribute('data-parallax-image', parallaxConfig.imageUrl);
        container.setAttribute('data-parallax-intensity', parallaxConfig.intensity);
        container.setAttribute('data-parallax-zoom', parallaxConfig.zoomFactor);
        container.setAttribute('data-parallax-transition', parallaxConfig.transitionDuration + 's');
        container.setAttribute('data-parallax-brightness', parallaxConfig.brightness);
        container.setAttribute('data-parallax-contrast', parallaxConfig.contrast);
        container.setAttribute('data-parallax-saturation', parallaxConfig.saturation);
        container.setAttribute('data-parallax-blur', parallaxConfig.blur);
      }

      function updatePreview() {
        parallaxConfig.intensity = parseFloat(document.getElementById('parallax-intensity').value);
        parallaxConfig.zoomFactor = parseFloat(document.getElementById('zoom-factor').value);
        parallaxConfig.transitionDuration = parseFloat(document.getElementById('transition-duration').value);
        parallaxConfig.brightness = parseInt(document.getElementById('brightness').value);
        parallaxConfig.contrast = parseInt(document.getElementById('contrast').value);
        parallaxConfig.saturation = parseInt(document.getElementById('saturation').value);
        parallaxConfig.blur = parseFloat(document.getElementById('blur').value);
        
        const container = document.getElementById('parallax-preview');
        updateContainerAttributes(container);
        
        if (previewState.bgElement && previewState.currentImageUrl === parallaxConfig.imageUrl) {
          updateConfig();
        } else {
          if (previewParallaxEffect && typeof previewParallaxEffect.cleanup === 'function') {
            previewParallaxEffect.cleanup();
          }
          previewParallaxEffect = createParallaxEffect(container);
        }
      }

      function createParallaxEffect(element) {
        const existingBg = element.querySelector('.parallax-bg');
        if (existingBg) existingBg.remove();
        
        const existingContent = element.querySelector('.parallax-content');
        if (existingContent) {
          while (existingContent.firstChild) {
            element.appendChild(existingContent.firstChild);
          }
          existingContent.remove();
        }

        const imageUrl = element.getAttribute('data-parallax-image') || parallaxConfig.imageUrl;
        const intensity = parseFloat(element.getAttribute('data-parallax-intensity')) || parallaxConfig.intensity;
        const zoomFactor = parseFloat(element.getAttribute('data-parallax-zoom')) || parallaxConfig.zoomFactor;
        const transitionDuration = element.getAttribute('data-parallax-transition') || (parallaxConfig.transitionDuration + 's');
        const brightness = parseInt(element.getAttribute('data-parallax-brightness')) || parallaxConfig.brightness;
        const contrast = parseInt(element.getAttribute('data-parallax-contrast')) || parallaxConfig.contrast;
        const saturation = parseInt(element.getAttribute('data-parallax-saturation')) || parallaxConfig.saturation;
        const blur = parseFloat(element.getAttribute('data-parallax-blur')) || parallaxConfig.blur;

        const styles = getComputedStyle(element);
        if (styles.position === 'static') {
          element.style.position = 'relative';
        }

        const bgElement = document.createElement('div');
        bgElement.className = 'parallax-bg';
        Object.assign(bgElement.style, {
          position: 'absolute',
          top: '0',
          left: '0',
          width: '100%',
          height: '100%',
          backgroundPosition: 'center',
          backgroundRepeat: 'no-repeat',
          backgroundSize: 'cover',
          backgroundImage: `url('${imageUrl}')`,
          willChange: 'transform',
          WebkitBackfaceVisibility: 'hidden',
          backfaceVisibility: 'hidden',
          transform: 'translateZ(0)',
          transition: `filter ${transitionDuration} ease-in-out, background-size ${transitionDuration} ease-out`
        });

        const contentContainer = document.createElement('div');
        contentContainer.className = 'parallax-content';
        contentContainer.style.position = 'relative';
        contentContainer.style.zIndex = '1';
        contentContainer.style.width = '100%';
        
        while (element.firstChild) {
          contentContainer.appendChild(element.firstChild);
        }

        element.appendChild(bgElement);
        element.appendChild(contentContainer);

        previewState.bgElement = bgElement;
        previewState.container = element;
        previewState.currentImageUrl = imageUrl;

        const img = new Image();
        img.onload = () => {
          const imageRatio = img.width / img.height;
          const isMobile = window.matchMedia("(max-width: 767px)").matches;
          
          adjustBackgroundSize(bgElement, element, imageRatio, zoomFactor, isMobile);
          applyParallax(bgElement, element, intensity, isMobile);
          applyFilters(bgElement, brightness, contrast, saturation, blur);
        };
        img.src = imageUrl;

        let rafId = null;
        
        function throttledParallax() {
          if (rafId === null) {
            rafId = requestAnimationFrame(() => {
              const img = new Image();
              img.onload = () => {
                const imageRatio = img.width / img.height;
                const isMobile = window.matchMedia("(max-width: 767px)").matches;
                applyParallax(bgElement, element, intensity, isMobile);
              };
              img.src = imageUrl;
              rafId = null;
            });
          }
        }

        function handleResize() {
          const img = new Image();
          img.onload = () => {
            const imageRatio = img.width / img.height;
            const isMobile = window.matchMedia("(max-width: 767px)").matches;
            adjustBackgroundSize(bgElement, element, imageRatio, zoomFactor, isMobile);
            throttledParallax();
          };
          img.src = imageUrl;
        }

        const scrollContainer = element.closest('.preview-container') || window;
        const isPreview = scrollContainer !== window;
        
        scrollContainer.addEventListener('scroll', throttledParallax, { passive: true });
        window.addEventListener('resize', handleResize, { passive: true });

        return {
          cleanup: () => {
            if (isPreview) {
              scrollContainer.removeEventListener('scroll', throttledParallax);
            } else {
              window.removeEventListener('scroll', throttledParallax);
            }
            window.removeEventListener('resize', handleResize);
            if (rafId) {
              cancelAnimationFrame(rafId);
            }
            previewState.bgElement = null;
            previewState.container = null;
            previewState.currentImageUrl = null;
          }
        };
      }

      function adjustBackgroundSize(bgElement, container, imageRatio, zoomFactor, isMobile) {
        const containerWidth = container.offsetWidth;
        const containerHeight = container.offsetHeight;
        
        const horizontalExtra = isMobile ? 1.3 : 1.4;
        const baseWidth = containerWidth * horizontalExtra * zoomFactor;
        const baseHeight = containerHeight * zoomFactor;
        
        if (imageRatio > 1) {
          const width = Math.max(baseWidth, baseHeight * imageRatio);
          const height = Math.max(baseHeight, width / imageRatio);
          bgElement.style.backgroundSize = `${Math.ceil(width)}px ${Math.ceil(height)}px`;
        } else {
          const height = Math.max(baseHeight, baseWidth / imageRatio);
          const width = Math.max(baseWidth, height * imageRatio);
          bgElement.style.backgroundSize = `${Math.ceil(width)}px ${Math.ceil(height)}px`;
        }
      }

      function applyParallax(bgElement, container, intensity, isMobile) {
        const previewContainer = container.closest('.preview-container');
        
        if (previewContainer) {
          const scrollTop = previewContainer.scrollTop;
          const containerHeight = previewContainer.offsetHeight;
          const scrollableHeight = previewContainer.scrollHeight - containerHeight;
          
          const scrollProgress = scrollableHeight > 0 ? scrollTop / scrollableHeight : 0;
          
          const horizontalOffset = (scrollProgress - 0.5) * 2 * intensity * 100;
          const xPosition = 50 + (horizontalOffset * (isMobile ? 1.5 : 1));
          
          bgElement.style.backgroundPosition = `${xPosition}% center`;
        } else {
          const rect = container.getBoundingClientRect();
          const viewportHeight = window.innerHeight;
          const viewportCenter = window.innerHeight / 2;
          const elementCenter = rect.top + (rect.height / 2);
          
          const distanceFromCenter = (elementCenter - viewportCenter) / viewportHeight;
          const horizontalOffset = distanceFromCenter * intensity * 100;
          
          const xPosition = 50 + (horizontalOffset * (isMobile ? 1.5 : 1));
          bgElement.style.backgroundPosition = `${xPosition}% center`;
        }
      }

      function applyFilters(bgElement, brightness, contrast, saturation, blur) {
        const filters = [];
        
        if (brightness !== 100) {
          filters.push(`brightness(${brightness / 100})`);
        }
        if (contrast !== 100) {
          filters.push(`contrast(${contrast / 100})`);
        }
        if (saturation !== 100) {
          filters.push(`saturate(${saturation / 100})`);
        }
        if (blur > 0) {
          filters.push(`blur(${blur}px)`);
        }
        
        bgElement.style.filter = filters.join(' ');
      }

      function showNotification(message, type = 'success') {
        const notification = document.getElementById('notification');
        notification.textContent = message;
        notification.className = `notification ${type}`;
        
        notification.offsetHeight;
        
        notification.style.visibility = 'visible';
        notification.classList.add('show');
        
        setTimeout(() => {
          notification.classList.remove('show');
          
          setTimeout(() => {
            if (!notification.classList.contains('show')) {
              notification.style.visibility = 'hidden';
            }
          }, 400);
        }, 3000);
      }

      function generateUniqueId() {
        return Math.random().toString(36).substring(2, 8);
      }

      function generateFullSectionJSON() {
  // Generar IDs únicos para todos los elementos
  const sectionId = generateUniqueId();
  const containerId = generateUniqueId();
  const codeId = generateUniqueId();
  const sectionAttributeId = generateUniqueId();
  const containerAttributeId = generateUniqueId();
  
  // Obtener el JavaScript actual con la configuración del usuario
  const jsCode = generateJavaScriptCode();
  
  // Crear el objeto JSON completo de Bricks Builder
  const bricksJSON = {
    "content": [
      {
        "id": sectionId,
        "name": "section",
        "parent": 0,
        "children": [containerId, codeId],
        "settings": {
          "_justifyContent": "center",
          "_height": "800",
          "_attributes": [{"id": sectionAttributeId, "name": "data-horizontal-parallax"}]
        },
        "label": "Horizontal Parallax Section"
      },
      {
        "id": containerId,
        "name": "container",
        "parent": sectionId,
        "children": [],
        "settings": {
          "_alignSelf": "center",
          "_justifyContent": "center",
          "_alignItems": "center",
          "_direction": "row",
          "_attributes": [{"id": containerAttributeId, "name": "data-float-container"}]
        }
      },
      {
        "id": codeId,
        "name": "code",
        "parent": sectionId,
        "children": [],
        "settings": {
          "javascriptCode": jsCode,
          "executeCode": true,
          "_display": "none"
        },
        "label": "Horizontal Parallax JS"
      }
    ],
    "source": "bricksCopiedElements",
    "sourceUrl": "https://test.bricksfusion.com",
    "version": "2.0.1",
    "globalClasses": [],
    "globalElements": []
  };
  
  return JSON.stringify(bricksJSON, null, 2);
}

      function generateJavaScriptCode() {
        return `(function() {
  const defaultConfig = {
    imageUrl: '${parallaxConfig.imageUrl}',
    intensity: ${parallaxConfig.intensity},
    zoomFactor: ${parallaxConfig.zoomFactor},
    transitionDuration: ${parallaxConfig.transitionDuration},
    brightness: ${parallaxConfig.brightness},
    contrast: ${parallaxConfig.contrast},
    saturation: ${parallaxConfig.saturation},
    blur: ${parallaxConfig.blur}
  };

  const styleSheet = document.createElement('style');
  styleSheet.textContent = \`
    [data-horizontal-parallax] {
      position: relative;
      overflow: hidden;
      width: 100%;
      height: 100%;
      min-height: 200px;
    }

    [data-horizontal-parallax] .parallax-bg {
      will-change: transform;
      -webkit-backface-visibility: hidden;
      backface-visibility: hidden;
      transform: translateZ(0);
      min-width: 100%;
      min-height: 100%;
      transition: background-size 0.3s ease-out;
    }

    [data-horizontal-parallax] .parallax-content {
      position: relative;
      z-index: 1;
      width: 100%;
    }
  \`;
  document.head.appendChild(styleSheet);

  function adjustBackgroundSize(bgElement, container, imageRatio, zoomFactor, isMobile) {
    const containerWidth = container.offsetWidth;
    const containerHeight = container.offsetHeight;
    
    const horizontalExtra = isMobile ? 1.3 : 1.4;
    const baseWidth = containerWidth * horizontalExtra * zoomFactor;
    const baseHeight = containerHeight * zoomFactor;
    
    if (imageRatio > 1) {
      const width = Math.max(baseWidth, baseHeight * imageRatio);
      const height = Math.max(baseHeight, width / imageRatio);
      bgElement.style.backgroundSize = \`\${Math.ceil(width)}px \${Math.ceil(height)}px\`;
    } else {
      const height = Math.max(baseHeight, baseWidth / imageRatio);
      const width = Math.max(baseWidth, height * imageRatio);
      bgElement.style.backgroundSize = \`\${Math.ceil(width)}px \${Math.ceil(height)}px\`;
    }
  }

  function applyParallax(bgElement, container, intensity, isMobile) {
    const rect = container.getBoundingClientRect();
    const viewportHeight = window.innerHeight;
    const viewportCenter = window.innerHeight / 2;
    const elementCenter = rect.top + (rect.height / 2);
    
    const distanceFromCenter = (elementCenter - viewportCenter) / viewportHeight;
    const horizontalOffset = distanceFromCenter * intensity * 100;
    
    const xPosition = 50 + (horizontalOffset * (isMobile ? 1.5 : 1));
    bgElement.style.backgroundPosition = \`\${xPosition}% center\`;
  }

  function applyFilters(bgElement, brightness, contrast, saturation, blur) {
    const filters = [];
    
    if (brightness !== 100) {
      filters.push(\`brightness(\${brightness / 100})\`);
    }
    if (contrast !== 100) {
      filters.push(\`contrast(\${contrast / 100})\`);
    }
    if (saturation !== 100) {
      filters.push(\`saturate(\${saturation / 100})\`);
    }
    if (blur > 0) {
      filters.push(\`blur(\${blur}px)\`);
    }
    
    bgElement.style.filter = filters.join(' ');
  }

  function createParallaxEffect(element) {
    const existingBg = element.querySelector('.parallax-bg');
    if (existingBg) existingBg.remove();
    
    const existingContent = element.querySelector('.parallax-content');
    if (existingContent) {
      while (existingContent.firstChild) {
        element.appendChild(existingContent.firstChild);
      }
      existingContent.remove();
    }

    const imageUrl = element.getAttribute('data-parallax-image') || defaultConfig.imageUrl;
    const intensity = parseFloat(element.getAttribute('data-parallax-intensity')) || defaultConfig.intensity;
    const zoomFactor = parseFloat(element.getAttribute('data-parallax-zoom')) || defaultConfig.zoomFactor;
    const transitionDuration = element.getAttribute('data-parallax-transition') || (defaultConfig.transitionDuration + 's');
    const brightness = parseInt(element.getAttribute('data-parallax-brightness')) || defaultConfig.brightness;
    const contrast = parseInt(element.getAttribute('data-parallax-contrast')) || defaultConfig.contrast;
    const saturation = parseInt(element.getAttribute('data-parallax-saturation')) || defaultConfig.saturation;
    const blur = parseFloat(element.getAttribute('data-parallax-blur')) || defaultConfig.blur;

    if (!imageUrl) {
      return;
    }

    const styles = getComputedStyle(element);
    if (styles.position === 'static') {
      element.style.position = 'relative';
    }

    const bgElement = document.createElement('div');
    bgElement.className = 'parallax-bg';
    Object.assign(bgElement.style, {
      position: 'absolute',
      top: '0',
      left: '0',
      width: '100%',
      height: '100%',
      backgroundPosition: 'center',
      backgroundRepeat: 'no-repeat',
      backgroundSize: 'cover',
      backgroundImage: \`url('\${imageUrl}')\`,
      willChange: 'transform',
      WebkitBackfaceVisibility: 'hidden',
      backfaceVisibility: 'hidden',
      transform: 'translateZ(0)',
      transition: \`filter \${transitionDuration} ease-in-out, background-size \${transitionDuration} ease-out\`
    });

    const contentContainer = document.createElement('div');
    contentContainer.className = 'parallax-content';
    contentContainer.style.position = 'relative';
    contentContainer.style.zIndex = '1';
    contentContainer.style.width = '100%';
    
    while (element.firstChild) {
      contentContainer.appendChild(element.firstChild);
    }

    element.appendChild(bgElement);
    element.appendChild(contentContainer);

    const img = new Image();
    img.onload = () => {
      const imageRatio = img.width / img.height;
      const isMobile = window.matchMedia("(max-width: 767px)").matches;
      
      adjustBackgroundSize(bgElement, element, imageRatio, zoomFactor, isMobile);
      applyParallax(bgElement, element, intensity, isMobile);
      applyFilters(bgElement, brightness, contrast, saturation, blur);
    };
    img.onerror = (e) => {
      bgElement.style.backgroundColor = '#ccc';
    };
    img.src = imageUrl;

    let rafId = null;
    
    function throttledParallax() {
      if (rafId === null) {
        rafId = requestAnimationFrame(() => {
          const img = new Image();
          img.onload = () => {
            const imageRatio = img.width / img.height;
            const isMobile = window.matchMedia("(max-width: 767px)").matches;
            applyParallax(bgElement, element, intensity, isMobile);
          };
          img.src = imageUrl;
          rafId = null;
        });
      }
    }

    function handleResize() {
      const img = new Image();
      img.onload = () => {
        const imageRatio = img.width / img.height;
        const isMobile = window.matchMedia("(max-width: 767px)").matches;
        adjustBackgroundSize(bgElement, element, imageRatio, zoomFactor, isMobile);
        throttledParallax();
      };
      img.src = imageUrl;
    }

    window.addEventListener('scroll', throttledParallax, { passive: true });
    window.addEventListener('resize', handleResize, { passive: true });

    return () => {
      window.removeEventListener('scroll', throttledParallax);
      window.removeEventListener('resize', handleResize);
      if (rafId) {
        cancelAnimationFrame(rafId);
      }
    };
  }

  function initializeParallaxEffects() {
    document.querySelectorAll('[data-horizontal-parallax]').forEach(element => {
      if (!element.id) {
        element.id = \`parallax-\${Math.random().toString(36).substr(2, 9)}\`;
      }
      element._parallaxCleanup = createParallaxEffect(element);
    });
  }

  const initWithDelay = () => {
    setTimeout(initializeParallaxEffects, 100);
  };

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initWithDelay);
  } else {
    initWithDelay();
  }
  
  window.addEventListener('load', initializeParallaxEffects);
  
  const observer = new MutationObserver((mutations) => {
    let shouldInit = false;
    
    mutations.forEach((mutation) => {
      if (mutation.type === 'childList') {
        mutation.addedNodes.forEach(node => {
          if (node.nodeType === 1) {
            if (node.hasAttribute && node.hasAttribute('data-horizontal-parallax')) {
              shouldInit = true;
            } else if (node.querySelectorAll) {
              const parallaxElements = node.querySelectorAll('[data-horizontal-parallax]');
              if (parallaxElements.length > 0) {
                shouldInit = true;
              }
            }
          }
        });
      } else if (mutation.type === 'attributes' && mutation.attributeName === 'data-horizontal-parallax') {
        shouldInit = true;
      }
    });
    
    if (shouldInit) {
      setTimeout(initializeParallaxEffects, 100);
    }
  });
  
  observer.observe(document.body, { 
    childList: true, 
    subtree: true,
    attributes: true,
    attributeFilter: ['data-horizontal-parallax', 'data-parallax-image', 'data-parallax-intensity', 'data-parallax-zoom', 'data-parallax-transition', 'data-parallax-brightness', 'data-parallax-contrast', 'data-parallax-saturation', 'data-parallax-blur']
  });
})();`;
      }

      function copyJsToClipboard() {
        const jsCode = generateJavaScriptCode();
        
        navigator.clipboard.writeText(jsCode)
          .then(() => {
            showNotification('JavaScript code copied to clipboard!');
          })
          .catch(err => {
            try {
              const textArea = document.createElement('textarea');
              textArea.value = jsCode;
              textArea.style.position = 'fixed';
              textArea.style.opacity = '0';
              document.body.appendChild(textArea);
              textArea.select();
              document.execCommand('copy');
              document.body.removeChild(textArea);
              showNotification('JavaScript code copied to clipboard!');
            } catch (fallbackErr) {
              showNotification('Failed to copy to clipboard. Please try again.', 'error');
            }
          });
      }

      function copyFullSectionToClipboard() {
        const sectionJSON = generateFullSectionJSON();
        
        navigator.clipboard.writeText(sectionJSON)
          .then(() => {
            showNotification('Full section JSON copied to clipboard!');
          })
          .catch(err => {
            try {
              const textArea = document.createElement('textarea');
              textArea.value = sectionJSON;
              textArea.style.position = 'fixed';
              textArea.style.opacity = '0';
              document.body.appendChild(textArea);
              textArea.select();
              document.execCommand('copy');
              document.body.removeChild(textArea);
              showNotification('Full section JSON copied to clipboard!');
            } catch (fallbackErr) {
              showNotification('Failed to copy to clipboard. Please try again.', 'error');
            }
          });
      }

      function copyToClipboard(text) {
        navigator.clipboard.writeText(text)
          .then(() => {
            showNotification('Copied to clipboard!');
          })
          .catch(err => {
            showNotification('Failed to copy to clipboard', 'error');
          });
      }

      window.resetParameter = function(parameterId, defaultValue) {
        const element = document.getElementById(parameterId);
        if (element) {
          element.value = defaultValue;
          const valueElement = document.getElementById(`${parameterId}-value`);
          if (valueElement) {
            valueElement.textContent = defaultValue;
          }
          
          switch (parameterId) {
            case 'parallax-intensity':
              parallaxConfig.intensity = defaultValue;
              break;
            case 'zoom-factor':
              parallaxConfig.zoomFactor = defaultValue;
              break;
            case 'transition-duration':
              parallaxConfig.transitionDuration = defaultValue;
              break;
            case 'brightness':
              parallaxConfig.brightness = defaultValue;
              break;
            case 'contrast':
              parallaxConfig.contrast = defaultValue;
              break;
            case 'saturation':
              parallaxConfig.saturation = defaultValue;
              break;
            case 'blur':
              parallaxConfig.blur = defaultValue;
              break;
          }
          
          debouncedUpdatePreview();
          showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
        }
      };

      function updateImagePreview() {
        const imagePreview = document.getElementById('image-preview');
        if (parallaxConfig.imageUrl) {
          imagePreview.style.backgroundImage = `url('${parallaxConfig.imageUrl}')`;
          imagePreview.classList.add('has-image');
          imagePreview.innerHTML = '';
        } else {
          imagePreview.style.backgroundImage = '';
          imagePreview.classList.remove('has-image');
          imagePreview.innerHTML = '<span>Enter image URL below</span>';
        }
      }

      function initializeUI() {
        initializePreview();
        
        const instructionsToggle = document.getElementById('instructions-toggle');
        const instructionsContent = document.getElementById('instructions-content');
        const instructionsCard = document.getElementById('instructions-card');
        const toggleIcon = instructionsToggle.querySelector('.toggle-icon');
        
        instructionsToggle.addEventListener('click', () => {
          const isVisible = instructionsContent.classList.contains('show');
          
          if (isVisible) {
            instructionsContent.classList.remove('show');
            instructionsCard.classList.remove('expanded');
            toggleIcon.classList.remove('expanded');
          } else {
            instructionsContent.classList.add('show');
            instructionsCard.classList.add('expanded');
            toggleIcon.classList.add('expanded');
          }
        });

        document.getElementById('quick-attribute').addEventListener('click', () => {
          copyToClipboard('data-horizontal-parallax');
        });

        document.getElementById('download-config').addEventListener('click', () => {
          copyJsToClipboard();
        });

        document.getElementById('copy-full-section').addEventListener('click', () => {
          copyFullSectionToClipboard();
        });

        const imageUrlInput = document.getElementById('image-url');
        imageUrlInput.addEventListener('input', function() {
          parallaxConfig.imageUrl = this.value;
          updateImagePreview();
          debouncedUpdatePreview();
        });

        document.getElementById('reset-image').addEventListener('click', () => {
          parallaxConfig.imageUrl = defaultConfig.imageUrl;
          document.getElementById('image-url').value = defaultConfig.imageUrl;
          updateImagePreview();
          debouncedUpdatePreview();
          showNotification('Image reset to default');
        });

        document.getElementById('reset-parallax').addEventListener('click', () => {
          parallaxConfig.intensity = defaultConfig.intensity;
          parallaxConfig.zoomFactor = defaultConfig.zoomFactor;
          parallaxConfig.transitionDuration = defaultConfig.transitionDuration;
          
          document.getElementById('parallax-intensity').value = defaultConfig.intensity;
          document.getElementById('zoom-factor').value = defaultConfig.zoomFactor;
          document.getElementById('transition-duration').value = defaultConfig.transitionDuration;
          
          document.getElementById('intensity-value').textContent = defaultConfig.intensity;
          document.getElementById('zoom-value').textContent = defaultConfig.zoomFactor;
          document.getElementById('transition-value').textContent = defaultConfig.transitionDuration;
          
          debouncedUpdatePreview();
          showNotification('Parallax settings reset');
        });

        document.getElementById('reset-filters').addEventListener('click', () => {
          parallaxConfig.brightness = defaultConfig.brightness;
          parallaxConfig.contrast = defaultConfig.contrast;
          parallaxConfig.saturation = defaultConfig.saturation;
          parallaxConfig.blur = defaultConfig.blur;
          
          document.getElementById('brightness').value = defaultConfig.brightness;
          document.getElementById('contrast').value = defaultConfig.contrast;
          document.getElementById('saturation').value = defaultConfig.saturation;
          document.getElementById('blur').value = defaultConfig.blur;
          
          document.getElementById('brightness-value').textContent = defaultConfig.brightness;
          document.getElementById('contrast-value').textContent = defaultConfig.contrast;
          document.getElementById('saturation-value').textContent = defaultConfig.saturation;
          document.getElementById('blur-value').textContent = defaultConfig.blur;
          
          debouncedUpdatePreview();
          showNotification('Visual filters reset');
        });

        const rangeInputs = document.querySelectorAll('input[type="range"]');
        rangeInputs.forEach(input => {
          const valueElement = document.getElementById(`${input.id}-value`);
          
          if (valueElement) {
            valueElement.textContent = input.value;
          }
          
          input.addEventListener('input', () => {
            if (valueElement) {
              valueElement.textContent = input.value;
            }
            
            switch (input.id) {
              case 'parallax-intensity':
                parallaxConfig.intensity = parseFloat(input.value);
                break;
              case 'zoom-factor':
                parallaxConfig.zoomFactor = parseFloat(input.value);
                break;
              case 'transition-duration':
                parallaxConfig.transitionDuration = parseFloat(input.value);
                break;
              case 'brightness':
                parallaxConfig.brightness = parseInt(input.value);
                break;
              case 'contrast':
                parallaxConfig.contrast = parseInt(input.value);
                break;
              case 'saturation':
                parallaxConfig.saturation = parseInt(input.value);
                break;
              case 'blur':
                parallaxConfig.blur = parseFloat(input.value);
                break;
            }
            
            debouncedUpdatePreview();
          });
        });

        document.addEventListener('keydown', (e) => {
          if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
            return;
          }
          
          if (e.ctrlKey || e.metaKey) {
            switch (e.key.toLowerCase()) {
              case 'd':
                e.preventDefault();
                const downloadBtn = document.getElementById('download-config');
                if (downloadBtn && downloadBtn.hasAttribute('data-protection-animation')) {
                  downloadBtn.click();
                } else {
                  copyJsToClipboard();
                }
                break;
              case 's':
                e.preventDefault();
                const fullSectionBtn = document.getElementById('copy-full-section');
                if (fullSectionBtn && fullSectionBtn.hasAttribute('data-protection-animation')) {
                  fullSectionBtn.click();
                } else {
                  copyFullSectionToClipboard();
                }
                break;
            }
          }
        });

        updateImagePreview();
        
        setTimeout(() => {
          showNotification('BricksFusion Horizontal Parallax Configurator loaded!');
        }, 500);

        function saveConfiguration() {
          try {
            localStorage.setItem('bricksfusion-parallax-config', JSON.stringify(parallaxConfig));
          } catch (e) {
          }
        }

        function loadConfiguration() {
          try {
            const saved = localStorage.getItem('bricksfusion-parallax-config');
            if (saved) {
              const savedConfig = JSON.parse(saved);
              Object.assign(parallaxConfig, savedConfig);
              
              document.getElementById('image-url').value = savedConfig.imageUrl;
              document.getElementById('parallax-intensity').value = savedConfig.intensity;
              document.getElementById('zoom-factor').value = savedConfig.zoomFactor;
              document.getElementById('transition-duration').value = savedConfig.transitionDuration;
              document.getElementById('brightness').value = savedConfig.brightness;
              document.getElementById('contrast').value = savedConfig.contrast;
              document.getElementById('saturation').value = savedConfig.saturation;
              document.getElementById('blur').value = savedConfig.blur;
              
              document.getElementById('intensity-value').textContent = savedConfig.intensity;
              document.getElementById('zoom-value').textContent = savedConfig.zoomFactor;
              document.getElementById('transition-value').textContent = savedConfig.transitionDuration;
              document.getElementById('brightness-value').textContent = savedConfig.brightness;
              document.getElementById('contrast-value').textContent = savedConfig.contrast;
              document.getElementById('saturation-value').textContent = savedConfig.saturation;
              document.getElementById('blur-value').textContent = savedConfig.blur;
              
              updateImagePreview();
              debouncedUpdatePreview();
            }
          } catch (e) {
          }
        }

        const originalUpdatePreview = debouncedUpdatePreview;
        debouncedUpdatePreview = function() {
          originalUpdatePreview();
          saveConfiguration();
        };

        loadConfiguration();
      }
      
      initializeUI();
    });
  </script>
</body>
</html>
Horizontal Parallax - Bricksfusion
LIGHT

Horizontal Parallax

Creates smooth horizontal parallax scrolling effect on background images. Image shifts left/right based on viewport position for depth perception. Features adjustable intensity, zoom, and image filters. Uses CSS transforms with requestAnimationFrame for 60fps performance. Perfect for hero sections, feature blocks, or adding dynamic visual interest.

Horizontal Parallax

Scroll the page to see the horizontal parallax effect.

Horizontal Parallax

The background moves as you scroll

↕ Scroll to see the effect ↕

Image

Image URL URL string

Background image for parallax effect. Automatically sized and positioned for optimal display.

Required

Zoom 1.0-2.0

Initial zoom level of background image. Higher creates closer crop with more parallax range.

Default: 1.4

Parallax Effect

Intensity 0.1-1.0

Strength of horizontal movement. Higher creates more dramatic shift as viewport scrolls.

Default: 0.2

Transition CSS duration

Duration of smooth transitions for filters and size changes. Standard CSS time values.

Default: 0.3s

Image Filters

Brightness 0-200

Image brightness level. 100 is normal, lower is darker, higher is brighter.

Default: 100

Contrast 0-200

Image contrast level. 100 is normal, lower is flatter, higher is more dramatic.

Default: 100

Saturation 0-200

Color saturation level. 0 is grayscale, 100 is normal, higher is more vibrant.

Default: 100

Blur 0-20 pixels

Gaussian blur intensity. Creates soft focus or depth-of-field effect.

Default: 0

Performance

This element uses CSS transforms with background-position for parallax effect. Calculates horizontal offset based on viewport distance using requestAnimationFrame with throttling. Features passive scroll listeners and hardware-accelerated transforms with backface-visibility optimization. Responsive with mobile-specific adjustments. Very lightweight - suitable for all devices and multiple instances per page with smooth 60fps performance.