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>Vertical 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);
  }
}

.preview-content {
  color: white;
  text-align: center;
  font-weight: bold;
  font-size: var(--text-s);
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.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);
}

select {
  cursor: pointer;
}

.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);
}

.sample-images {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
  flex-wrap: wrap;
}

.sample-image {
  width: 60px;
  height: 40px;
  border-radius: 4px;
  background-size: cover;
  background-position: center;
  cursor: pointer;
  border: 2px solid transparent;
  transition: var(--transition);
}

.sample-image:hover {
  border-color: var(--accent);
  transform: scale(1.05);
}

.sample-image.active {
  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">Vertical Parallax</span>
    </nav>
    
    <div class="action-buttons">
      <div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
        data-vertical-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">Vertical Parallax</h1>
      <p class="page-subtitle">Interactive scrolling 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 vertical 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-vertical-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-vertical-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 Settings">↺</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 or click a sample</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 class="sample-images">
                  <div class="sample-image" data-url="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&auto=format&fit=crop&w=2000&q=80" style="background-image: url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80')"></div>
                  <div class="sample-image" data-url="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?ixlib=rb-4.0.3&auto=format&fit=crop&w=2000&q=80" style="background-image: url('https://images.unsplash.com/photo-1441974231531-c6227db76b6e?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80')"></div>
                  <div class="sample-image" data-url="https://images.unsplash.com/photo-1547036967-23d11aacaee0?ixlib=rb-4.0.3&auto=format&fit=crop&w=2000&q=80" style="background-image: url('https://images.unsplash.com/photo-1547036967-23d11aacaee0?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80')"></div>
                  <div class="sample-image" data-url="https://images.unsplash.com/photo-1519904981063-b0cf448d479e?ixlib=rb-4.0.3&auto=format&fit=crop&w=2000&q=80" style="background-image: url('https://images.unsplash.com/photo-1519904981063-b0cf448d479e?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80')"></div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Animation Settings
            <div class="card-actions">
              <button class="card-action-btn" id="reset-animation" title="Reset Animation 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 strong the parallax effect is">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="intensity-value">0.25</span></span>
                  <button class="reset-btn" onclick="resetParameter('parallax-intensity', 0.25)">↺</button>
                </div>
              </div>
              <input type="range" id="parallax-intensity" min="0.05" max="0.8" step="0.05" value="0.25">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Zoom Factor
                  <span class="help-tooltip" title="Background image zoom level">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="zoom-value">1.6</span></span>
                  <button class="reset-btn" onclick="resetParameter('zoom-factor', 1.6)">↺</button>
                </div>
              </div>
              <input type="range" id="zoom-factor" min="1.2" max="3.0" step="0.1" value="1.6">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Transition Duration
                  <span class="help-tooltip" title="Animation transition timing">ℹ</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">
            Advanced Options
            <div class="card-actions">
              <button class="card-action-btn" id="reset-advanced" title="Reset Advanced Settings">↺</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="Image brightness level">ℹ</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="Image contrast level">ℹ</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="Image 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="Image 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 class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Sepia
                  <span class="help-tooltip" title="Sepia filter effect">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="sepia-value">0</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('sepia', 0)">↺</button>
                </div>
              </div>
              <input type="range" id="sepia" min="0" max="100" step="5" value="0">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Grayscale
                  <span class="help-tooltip" title="Grayscale filter effect">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="grayscale-value">0</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('grayscale', 0)">↺</button>
                </div>
              </div>
              <input type="range" id="grayscale" min="0" max="100" step="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.25,
        zoomFactor: 1.6,
        transitionDuration: 0.3,
        brightness: 100,
        contrast: 100,
        saturation: 100,
        blur: 0,
        sepia: 0,
        grayscale: 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 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 attributeId1 = generateUniqueId();
  const attributeId2 = 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": attributeId1,
              "name": "data-vertical-parallax"
            }
          ]
        },
        "label": "Vertical Parallax Section"
      },
      {
        "id": containerId,
        "name": "container",
        "parent": sectionId,
        "children": [],
        "settings": {
          "_alignSelf": "center",
          "_justifyContent": "center",
          "_alignItems": "center",
          "_direction": "row",
          "_attributes": [
            {
              "id": attributeId2,
              "name": "data-float-container"
            }
          ]
        }
      },
      {
        "id": codeId,
        "name": "code",
        "parent": sectionId,
        "children": [],
        "settings": {
          "javascriptCode": jsCode,
          "executeCode": true,
          "_display": "none"
        },
        "label": "Vertical 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 ATTR_NAME = 'data-vertical-parallax';
  
  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},
    sepia: ${parallaxConfig.sepia},
    grayscale: ${parallaxConfig.grayscale}
  };

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

    [\${ATTR_NAME}] .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;
    }

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

  function getParallaxIntensity() {
    if (window.matchMedia("(max-width: 480px)").matches) return defaultConfig.intensity * 0.7;
    if (window.matchMedia("(max-width: 767px)").matches) return defaultConfig.intensity * 0.8;
    if (window.matchMedia("(min-width: 768px) and (max-width: 1024px)").matches) return defaultConfig.intensity * 0.9;
    return defaultConfig.intensity;
  }

  function adjustBackgroundSize(bgElement, state) {
    const containerWidth = bgElement.offsetWidth;
    const containerHeight = bgElement.offsetHeight;
    
    const mobileZoom = state.isMobile ? (defaultConfig.zoomFactor * 1.2) : defaultConfig.zoomFactor;
    
    const minHeight = containerHeight * mobileZoom;
    
    if (state.imageRatio > 1) {
      const width = minHeight * state.imageRatio;
      const finalWidth = Math.max(width, containerWidth * 1.6);
      bgElement.style.backgroundSize = \`\${Math.ceil(finalWidth)}px \${Math.ceil(minHeight)}px\`;
    } else {
      const minWidth = containerWidth * (state.isMobile ? 1 : 1.6);
      const height = minWidth / state.imageRatio;
      const finalHeight = Math.max(height, minHeight);
      bgElement.style.backgroundSize = \`\${Math.ceil(minWidth)}px \${Math.ceil(finalHeight)}px\`;
    }
  }

  function applyParallax(bgElement, state) {
    const previewContainer = bgElement.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 parallaxOffset = (scrollProgress * state.intensity * 100);
      const yPosition = 50 - parallaxOffset;
      
      bgElement.style.backgroundPosition = \`center \${yPosition}%\`;
    } else {
      const rect = bgElement.getBoundingClientRect();
      const viewportHeight = window.innerHeight;
      const elementTop = rect.top;
      const elementBottom = rect.bottom;
      
      if (elementBottom >= 0 && elementTop <= viewportHeight) {
        const scrollPosition = window.pageYOffset;
        const elementOffsetTop = rect.top + scrollPosition;
        const viewportCenter = viewportHeight / 2;
        const elementCenter = elementOffsetTop + (rect.height / 2);
        
        const distanceFromCenter = (elementCenter - (scrollPosition + viewportCenter)) / viewportHeight;
        const parallaxOffset = distanceFromCenter * state.intensity * 100;
        
        const yPosition = 50 + parallaxOffset;
        bgElement.style.backgroundPosition = \`center \${yPosition}%\`;
      }
    }
  }

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

  function throttledParallax(bgElement, state) {
    if (state.rafId === null) {
      state.rafId = requestAnimationFrame(() => {
        applyParallax(bgElement, state);
        state.rafId = null;
      });
    }
  }

  function loadBackgroundImage(bgElement, state) {
    const img = new Image();
    img.onload = () => {
      state.imageRatio = img.width / img.height;
      adjustBackgroundSize(bgElement, state);
      applyParallax(bgElement, state);
      applyFilters(bgElement, state.config);
    };
    img.onerror = (e) => {
      bgElement.style.backgroundColor = '#ccc';
    };
    const imageUrl = bgElement.style.backgroundImage.replace(/^url\\(['"]?(.+?)['"]?\\)$/, '$1');
    img.src = imageUrl;
  }

  function setupParallaxElement(element) {
    const customConfig = {
      intensity: defaultConfig.intensity,
      zoomFactor: defaultConfig.zoomFactor,
      brightness: defaultConfig.brightness,
      contrast: defaultConfig.contrast,
      saturation: defaultConfig.saturation,
      blur: defaultConfig.blur,
      sepia: defaultConfig.sepia,
      grayscale: defaultConfig.grayscale
    };

    const state = {
      intensity: getParallaxIntensity(),
      rafId: null,
      imageRatio: 1,
      isMobile: window.matchMedia("(max-width: 767px)").matches,
      config: customConfig
    };

    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',
      transition: 'filter ' + defaultConfig.transitionDuration + 's ease-in-out'
    });

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

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

    const imageUrl = defaultConfig.imageUrl;
    if (imageUrl) {
      bgElement.style.backgroundImage = 'url(\\'' + imageUrl + '\\')';
      loadBackgroundImage(bgElement, state);
    }

    const scrollContainer = element.closest('.preview-container') || window;
    const isPreview = scrollContainer !== window;
    
    scrollContainer.addEventListener('scroll', () => throttledParallax(bgElement, state), { passive: true });
    window.addEventListener('resize', () => {
      state.intensity = getParallaxIntensity();
      state.isMobile = window.matchMedia("(max-width: 767px)").matches;
      adjustBackgroundSize(bgElement, state);
      throttledParallax(bgElement, state);
    }, { passive: true });
  }

  function initParallaxEffect() {
    const parallaxElements = document.querySelectorAll('[' + ATTR_NAME + ']');
    parallaxElements.forEach(setupParallaxElement);
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initParallaxEffect);
  } else {
    initParallaxEffect();
  }

  window.addEventListener('load', initParallaxEffect);
})();`;
      }

      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();
        
        if (!sectionJSON) {
          showNotification('Full section feature coming soon!', 'warning');
          return;
        }
        
        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');
          });
      }

      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 = parallaxConfig.imageUrl;
        const intensity = parallaxConfig.intensity;
        const zoomFactor = parallaxConfig.zoomFactor;
        const transitionDuration = parallaxConfig.transitionDuration + 's';
        const brightness = parallaxConfig.brightness;
        const contrast = parallaxConfig.contrast;
        const saturation = parallaxConfig.saturation;
        const blur = parallaxConfig.blur;
        const sepia = parallaxConfig.sepia;
        const grayscale = parallaxConfig.grayscale;

        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, sepia, grayscale);
        };
        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 mobileZoom = isMobile ? 2.5 : zoomFactor;
        
        const minHeight = containerHeight * mobileZoom;
        
        if (imageRatio > 1) {
          const width = minHeight * imageRatio;
          const finalWidth = Math.max(width, containerWidth * 1.6);
          bgElement.style.backgroundSize = `${Math.ceil(finalWidth)}px ${Math.ceil(minHeight)}px`;
        } else {
          const minWidth = containerWidth * (isMobile ? 1 : 1.6);
          const height = minWidth / imageRatio;
          const finalHeight = Math.max(height, minHeight);
          bgElement.style.backgroundSize = `${Math.ceil(minWidth)}px ${Math.ceil(finalHeight)}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 parallaxOffset = (scrollProgress * intensity * 100);
          const yPosition = 50 - parallaxOffset;
          
          bgElement.style.backgroundPosition = `center ${yPosition}%`;
        } else {
          const rect = container.getBoundingClientRect();
          const viewportHeight = window.innerHeight;
          const elementTop = rect.top;
          const elementBottom = rect.bottom;
          
          if (elementBottom >= 0 && elementTop <= viewportHeight) {
            const scrollPosition = window.pageYOffset;
            const elementOffsetTop = rect.top + scrollPosition;
            const viewportCenter = viewportHeight / 2;
            const elementCenter = elementOffsetTop + (rect.height / 2);
            
            const distanceFromCenter = (elementCenter - (scrollPosition + viewportCenter)) / viewportHeight;
            const parallaxOffset = distanceFromCenter * intensity * 100;
            
            const yPosition = 50 + parallaxOffset;
            bgElement.style.backgroundPosition = `center ${yPosition}%`;
          }
        }
      }

      function applyFilters(bgElement, brightness, contrast, saturation, blur, sepia, grayscale) {
        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)`);
        }
        if (sepia > 0) {
          filters.push(`sepia(${sepia / 100})`);
        }
        if (grayscale > 0) {
          filters.push(`grayscale(${grayscale / 100})`);
        }
        
        bgElement.style.filter = filters.join(' ');
      }

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

      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);
        parallaxConfig.sepia = parseInt(document.getElementById('sepia').value);
        parallaxConfig.grayscale = parseInt(document.getElementById('grayscale').value);
        
        const container = document.getElementById('parallax-preview');
        updateContainerAttributes(container);
        
        if (previewState.bgElement && previewState.currentImageUrl === parallaxConfig.imageUrl) {
          updatePreviewParameters();
        } else {
          if (previewParallaxEffect && typeof previewParallaxEffect.cleanup === 'function') {
            previewParallaxEffect.cleanup();
          }
          previewParallaxEffect = createParallaxEffect(container);
        }
      }

      function updatePreviewParameters() {
        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, parallaxConfig.sepia, parallaxConfig.grayscale);
        
        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 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 or click a sample</span>';
        }
      }

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

      window.resetParameter = function(parameterId, defaultValue) {
        const element = document.getElementById(parameterId);
        if (element) {
          element.value = defaultValue;
          const valueElement = document.getElementById(`${parameterId}-value`) || 
                              document.getElementById(parameterId.replace('-', '') + '-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;
            case 'sepia':
              parallaxConfig.sepia = defaultValue;
              break;
            case 'grayscale':
              parallaxConfig.grayscale = defaultValue;
              break;
          }
          
          debouncedUpdatePreview();
          showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
        }
      };

      function initializeUI() {
        initializePreview();
        updateImagePreview();
        
        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-vertical-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();
        });

        const sampleImages = document.querySelectorAll('.sample-image');
        sampleImages.forEach(img => {
          img.addEventListener('click', function() {
            const url = this.getAttribute('data-url');
            document.getElementById('image-url').value = url;
            parallaxConfig.imageUrl = url;
            updateImagePreview();
            debouncedUpdatePreview();
            
            sampleImages.forEach(i => i.classList.remove('active'));
            this.classList.add('active');
          });
        });
        
        if (sampleImages.length > 0) {
          sampleImages[0].classList.add('active');
        }

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

        document.getElementById('reset-animation').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('Animation settings reset');
        });

        document.getElementById('reset-advanced').addEventListener('click', () => {
          parallaxConfig.brightness = defaultConfig.brightness;
          parallaxConfig.contrast = defaultConfig.contrast;
          parallaxConfig.saturation = defaultConfig.saturation;
          parallaxConfig.blur = defaultConfig.blur;
          parallaxConfig.sepia = defaultConfig.sepia;
          parallaxConfig.grayscale = defaultConfig.grayscale;
          
          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('sepia').value = defaultConfig.sepia;
          document.getElementById('grayscale').value = defaultConfig.grayscale;
          
          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;
          document.getElementById('sepia-value').textContent = defaultConfig.sepia;
          document.getElementById('grayscale-value').textContent = defaultConfig.grayscale;
          
          debouncedUpdatePreview();
          showNotification('Advanced settings reset');
        });

        const rangeInputs = document.querySelectorAll('input[type="range"]');
        rangeInputs.forEach(input => {
          const valueElement = document.getElementById(`${input.id}-value`) || 
                              document.getElementById(input.id.replace('-', '') + '-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;
              case 'sepia':
                parallaxConfig.sepia = parseInt(input.value);
                break;
              case 'grayscale':
                parallaxConfig.grayscale = parseInt(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;
            }
          }
        });

        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('sepia').value = savedConfig.sepia;
              document.getElementById('grayscale').value = savedConfig.grayscale;
              
              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;
              document.getElementById('sepia-value').textContent = savedConfig.sepia;
              document.getElementById('grayscale-value').textContent = savedConfig.grayscale;
              
              updateImagePreview();
              debouncedUpdatePreview();
            }
          } catch (e) {
          }
        }

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

        loadConfiguration();
        
        setTimeout(() => {
          showNotification('BricksFusion Vertical Parallax Configurator loaded!');
        }, 500);
      }
      
      initializeUI();
    });
  </script>
</body>
</html>
Vertical Flow - Bricksfusion
MEDIUM

Vertical Flow

Creates an infinite vertical scrolling loop of content. Perfect for testimonials, logos, team members, or any repeating content that needs continuous movement.

Item 1
Item 2
Item 3
Item 4

Animation

Speed 1-50

How fast items scroll. Lower = slow and relaxed, higher = quick and energetic. Sweet spot is usually 10-20 for readability.

Default: 10

Direction up / down

Which way items move. Up scrolls content upward (items come from bottom), Down scrolls downward (items come from top).

Default: Up

Spacing

Item Gap 0-100px

Space between each item in the flow. Larger gaps give items breathing room, smaller gaps create a tighter packed look.

Default: 20px

Fade Effect

Fade Height 0-50%

How much of the top and bottom edges fade out. Creates a smooth blend where items appear and disappear. 15-25% works great for most cases.

Default: 15%

Fade Opacity 0.0-1.0

Strength of the fade effect. 1.0 is full fade to solid, 0.5 is semi-transparent. Higher values hide edges better.

Default: 0.95

Fade Color color picker

Color that the fade blends into. Should match your background so items blend seamlessly at the edges.

Default: Black (#000000)

Overlay

Overlay Enabled on/off

Adds a color tint over each item. Useful for creating uniform look across different images or reducing visual intensity.

Default: Off

Overlay Color color picker

Color of the overlay tint. Black darkens items, white lightens them, colors add brand tinting.

Default: Black (#000000)

Overlay Opacity 0.0-1.0

How strong the overlay tint is. 0.3 is subtle, 0.7 is strong. Keep it low to maintain item visibility.

Default: 0.3

Mobile

Mobile Speed 0.1-2.0

Speed multiplier for mobile devices. 0.5 means half speed, 1.0 means same as desktop. Slower speeds work better on small screens.

Default: 0.5

Performance

This element uses requestAnimationFrame for smooth 60fps animations. It automatically pauses when scrolled out of view to save resources. The infinite loop is created by cloning your items, so memory usage scales with the number of items you add.