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>ScrollStack 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: 500px;
  width: 100%;
  position: relative;
  overflow-y: auto;
  border-radius: var(--card-radius);
  background-color: #252525;
  display: block;
  padding: 0;
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
}

.preview-container::before {
  content: "↓ Scroll to see animation";
  position: absolute;
  top: 20px;
  right: 20px;
  background: rgba(239, 96, 19, 0.9);
  color: white;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 500;
  z-index: 100;
  opacity: 0.8;
  animation: fadeInOut 3s ease-in-out infinite;
  pointer-events: none;
  display: var(--show-scroll-hint, block);
  transition: opacity 0.5s ease;
}

@keyframes fadeInOut {
  0%, 100% { opacity: 0.8; }
  50% { opacity: 0.4; }
}

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

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

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

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

.preview-controls {
  position: absolute;
  top: 1rem;
  right: 1rem;
  display: flex;
  gap: 0.5rem;
  z-index: 10;
}

.preview-btn {
  padding: 0.5rem;
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 6px;
  cursor: pointer;
  transition: var(--transition);
  font-size: var(--text-xs);
  backdrop-filter: blur(5px);
}

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

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

.toggle-switch {
  position: relative;
  display: inline-block;
  width: 50px;
  height: 24px;
  margin-left: 8px;
}

.toggle-switch input { 
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: var(--track);
  transition: var(--transition);
  border-radius: 34px;
}

.toggle-slider:before {
  position: absolute;
  content: "";
  height: 18px;
  width: 18px;
  left: 3px;
  bottom: 3px;
  background-color: white;
  transition: var(--transition);
  border-radius: 50%;
}

input:checked + .toggle-slider {
  background-color: var(--accent);
}

input:checked + .toggle-slider:before {
  transform: translateX(26px);
}

.toggle-label {
  display: flex;
  align-items: center;
}

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

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

.scrollstack-container {
  width: 100%;
  height: auto;
  min-height: 800px;
  padding: 40px 20px 100px 20px;
  position: relative;
}

.scrollstack-card {
  background-color: #2a2a2a;
  border-radius: 12px;
  margin-bottom: 40px;
  padding: 20px;
  color: white;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  transition: all 0.3s ease;
  min-height: 120px;
}

.scrollstack-card-title {
  font-weight: 600;
  font-size: 18px;
  margin-bottom: 8px;
}

.scrollstack-card-content {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.7);
  line-height: 1.5;
}

.updating {
  position: relative;
}

.updating::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: updatePulse 0.6s ease-out;
}

@keyframes updatePulse {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

@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: 400px;
  }
  
  .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/showcase/" class="breadcrumb-item">Showcase</a>
      <span class="breadcrumb-separator">›</span>
      <span class="breadcrumb-item active">ScrollStack</span>
    </nav>
    
    <div class="action-buttons">
      <div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
        data-scrollstack-container
      </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">ScrollStack</h1>
      <p class="page-subtitle">Interactive 3D card stacking 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 ScrollStack 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 the JavaScript code</li>
                <li>To add the effect to any section: go to <strong>Section → Style → Attributes</strong>, add <code>data-scrollstack-container</code> as attribute name (leave value empty)</li>
                <li>Add <code>data-scrollstack-card</code> to each card element inside the container</li>
                
              </ol>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="content">
      <section class="preview-section">
        <div class="preview-container" id="scrollstack-preview">
          <div class="preview-controls">
            <button class="preview-btn" id="generate-random-palette" title="Generate Random Palette (🎲)">🎲</button>
          </div>
          <div class="scrollstack-container" data-scrollstack-container>
            <div class="scrollstack-card" data-scrollstack-card>
              <div class="scrollstack-card-title">Card 1</div>
              <div class="scrollstack-card-content">This is the first card in the stack. As you scroll, it will animate with 3D transformations.</div>
            </div>
            <div class="scrollstack-card" data-scrollstack-card>
              <div class="scrollstack-card-title">Card 2</div>
              <div class="scrollstack-card-content">This is the second card in the stack. Notice how it scales and rotates as you scroll.</div>
            </div>
            <div class="scrollstack-card" data-scrollstack-card>
              <div class="scrollstack-card-title">Card 3</div>
              <div class="scrollstack-card-content">This is the third card in the stack. The shadow and darkening effects add depth to the animation.</div>
            </div>
          </div>
        </div>
      </section>

      <section class="controls-section">
        <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">
                  Animation Duration
                  <span class="help-tooltip" title="Speed of transition animations">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="animation-duration-value">0.8</span>s</span>
                  <button class="reset-btn" onclick="resetParameter('animation-duration', 0.8)">↺</button>
                </div>
              </div>
              <input type="range" id="animation-duration" min="0.1" max="2" step="0.1" value="0.8">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Stagger Offset
                  <span class="help-tooltip" title="Distance between stacked cards">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="stagger-offset-value">20</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('stagger-offset', 20)">↺</button>
                </div>
              </div>
              <input type="range" id="stagger-offset" min="0" max="50" step="1" value="20">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Scale Factor
                  <span class="help-tooltip" title="How much cards scale down when stacked">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="scale-value">0.92</span></span>
                  <button class="reset-btn" onclick="resetParameter('scale', 0.92)">↺</button>
                </div>
              </div>
              <input type="range" id="scale" min="0.8" max="0.99" step="0.01" value="0.92">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Scale Increment
                  <span class="help-tooltip" title="Progressive scale reduction for each card">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="scale-increment-value">0.04</span></span>
                  <button class="reset-btn" onclick="resetParameter('scale-increment', 0.04)">↺</button>
                </div>
              </div>
              <input type="range" id="scale-increment" min="0.01" max="0.1" step="0.01" value="0.04">
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Visual Effects
            <div class="card-actions">
              <button class="card-action-btn" id="reset-effects" title="Reset Visual Effects">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div class="control-group">
              <div class="checkbox-group">
                <div class="toggle-label">
                  <span class="label-text">Enable 3D Rotation</span>
                  <label class="toggle-switch">
                    <input type="checkbox" id="rotate-toggle" checked>
                    <span class="toggle-slider"></span>
                  </label>
                </div>
              </div>
            </div>

            <div class="control-group">
              <div class="checkbox-group">
                <div class="toggle-label">
                  <span class="label-text">Enable Shadow Effect</span>
                  <label class="toggle-switch">
                    <input type="checkbox" id="shadow-toggle" checked>
                    <span class="toggle-slider"></span>
                  </label>
                </div>
              </div>
            </div>
            
            <div class="control-group">
              <div class="checkbox-group">
                <div class="toggle-label">
                  <span class="label-text">Enable Darkening Effect</span>
                  <label class="toggle-switch">
                    <input type="checkbox" id="darken-toggle" checked>
                    <span class="toggle-slider"></span>
                  </label>
                </div>
              </div>
            </div>
            
            <div class="control-group">
              <div class="checkbox-group">
                <div class="toggle-label">
                  <span class="label-text">Stack on Top</span>
                  <label class="toggle-switch">
                    <input type="checkbox" id="stack-top-toggle">
                    <span class="toggle-slider"></span>
                  </label>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Advanced Settings
            <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">
                  Perspective
                  <span class="help-tooltip" title="3D perspective intensity">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="perspective-value">1000</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('perspective', 1000)">↺</button>
                </div>
              </div>
              <input type="range" id="perspective" min="500" max="2000" step="50" value="1000">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Vertical Offset
                  <span class="help-tooltip" title="Vertical distance between cards">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="offset-value">16</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('offset', 16)">↺</button>
                </div>
              </div>
              <input type="range" id="offset" min="0" max="40" step="1" value="16">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Container Padding
                  <span class="help-tooltip" title="Internal padding of the container">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="padding-value">50</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('padding', 50)">↺</button>
                </div>
              </div>
              <input type="range" id="padding" min="0" max="100" step="5" value="50">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Mobile Scale
                  <span class="help-tooltip" title="Scale factor for mobile devices">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="mobile-scale-value">0.6</span></span>
                  <button class="reset-btn" onclick="resetParameter('mobile-scale', 0.6)">↺</button>
                </div>
              </div>
              <input type="range" id="mobile-scale" min="0.4" max="0.9" step="0.05" value="0.6">
            </div>
            
            <div class="control-group">
              <div class="checkbox-group">
                <div class="toggle-label">
                  <span class="label-text">Disable on Mobile</span>
                  <label class="toggle-switch">
                    <input type="checkbox" id="disable-mobile-toggle">
                    <span class="toggle-slider"></span>
                  </label>
                </div>
              </div>
            </div>

            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Mobile Breakpoint
                  <span class="help-tooltip" title="Screen width below which mobile settings apply">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="threshold-value">768</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('threshold', 768)">↺</button>
                </div>
              </div>
              <input type="range" id="threshold" min="480" max="1200" step="1" value="768">
            </div>
          </div>
        </div>
      </section>
    </div>
  </div>

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

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      let scrollStackConfig = {
        offset: 16,
        padding: 50,
        anchor: 'top',
        disabled: false,
        threshold: 768,
        scale: 0.92,
        scaleIncrement: 0.04,
        shadow: true,
        darken: true,
        rotate: true,
        perspective: 1000,
        animationDuration: 0.8,
        staggerOffset: 20,
        mobileScale: 0.6,
        disableMobile: false,
        stackOnTop: false
      };

      const defaultConfig = { ...scrollStackConfig };
      
      function debounce(func, wait) {
        let timeout;
        return function() {
          const context = this, args = arguments;
          clearTimeout(timeout);
          timeout = setTimeout(() => {
            func.apply(context, args);
          }, wait);
        };
      }
      
      function createPreviewContent() {
        const preview = document.getElementById('scrollstack-preview');
        if (!preview) return;
        
        const currentScroll = preview.scrollTop || 0;
        
        const container = preview.querySelector('[data-scrollstack-container]');
        if (!container) return;
        
        container.innerHTML = '';
        
        const cardTitles = [
          'Card 1', 'Card 2', 'Card 3'
        ];
        const cardContents = [
          'This is the first card in the stack. As you scroll, it will animate with 3D transformations.',
          'This is the second card in the stack. Notice how it scales and rotates as you scroll.',
          'This is the third card in the stack. The shadow and darkening effects add depth to the animation.'
        ];
        
        for (let i = 0; i < cardTitles.length; i++) {
          const card = document.createElement('div');
          card.className = 'scrollstack-card';
          card.setAttribute('data-scrollstack-card', '');
          
          const titleEl = document.createElement('div');
          titleEl.className = 'scrollstack-card-title';
          titleEl.textContent = cardTitles[i];
          
          const contentEl = document.createElement('div');
          contentEl.className = 'scrollstack-card-content';
          contentEl.textContent = cardContents[i];
          
          card.appendChild(titleEl);
          card.appendChild(contentEl);
          container.appendChild(card);
        }
        
        initPreviewScrollStack();
        
        if (currentScroll > 0) {
          setTimeout(() => {
            preview.scrollTop = currentScroll;
          }, 10);
        }
      }
      
      function initPreviewScrollStack() {
        const previewContainer = document.getElementById('scrollstack-preview');
        const container = previewContainer.querySelector('[data-scrollstack-container]');
        if (!container) return;
        
        const cards = Array.from(container.querySelectorAll('[data-scrollstack-card]'));
        if (!cards.length) return;
        
        const config = scrollStackConfig;
        
        container.style.perspective = `${config.perspective}px`;
        
        container.style.minHeight = (cards.length * 200) + 200 + 'px';
        
        cards.forEach((card, index) => {
          if (!card.dataset.originalHeight) {
            card.dataset.originalHeight = card.offsetHeight;
            card.dataset.originalWidth = card.offsetWidth;
            card.dataset.initialTop = card.offsetTop;
          }
          
          card.dataset.targetScale = config.scale - (index * config.scaleIncrement);
          
          card.style.position = 'relative';
          
          if (config.stackOnTop) {
            card.style.zIndex = index + 1;
          } else {
            card.style.zIndex = cards.length - index;
          }
          
          card.style.transition = `transform ${config.animationDuration}s ease, box-shadow ${config.animationDuration}s ease`;
          
          if (config.darken && !card._darkenOverlay) {
            const overlay = document.createElement('div');
            overlay.className = 'scrollstack-card-overlay';
            overlay.style.position = 'absolute';
            overlay.style.top = '0';
            overlay.style.left = '0';
            overlay.style.width = '100%';
            overlay.style.height = '100%';
            overlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
            overlay.style.pointerEvents = 'none';
            overlay.style.zIndex = '1';
            overlay.style.borderRadius = 'inherit';
            
            card.insertBefore(overlay, card.firstChild);
            card._darkenOverlay = overlay;
            
            Array.from(card.children).forEach(child => {
              if (child !== overlay) {
                child.style.position = 'relative';
                child.style.zIndex = '2';
              }
            });
          }
        });
        
        previewContainer.onscroll = function() {
          handlePreviewScroll(container, cards, config);
        };
        
        setTimeout(() => {
          previewContainer.scrollTop = 30;
          handlePreviewScroll(container, cards, config);
        }, 100);
      }
      
      function handlePreviewScroll(container, cards, config) {
        const previewContainer = document.getElementById('scrollstack-preview');
        const scrollTop = previewContainer.scrollTop;
        
        cards.forEach((card, index) => {
          if (!card.dataset.originalHeight || !card.dataset.initialTop) {
            card.dataset.originalHeight = card.offsetHeight;
            card.dataset.initialTop = card.offsetTop;
          }
        });
        
        const firstCardStackStart = 10;
        
        cards.forEach((card, index) => {
          const cardHeight = parseInt(card.dataset.originalHeight);
          const stackStart = index === 0 ? firstCardStackStart : parseInt(card.dataset.initialTop) - config.padding;
          
          if (scrollTop >= stackStart) {
            card.style.position = 'sticky';
            card.style.top = `${config.offset + (index * config.staggerOffset)}px`;
            
            const maxScroll = 200;
            const scrollProgress = Math.min(1, (scrollTop - stackStart) / maxScroll);
            
            applyCardEffects(card, scrollProgress, config);
          } else {
            card.style.position = 'relative';
            card.style.top = '0';
            card.style.transform = 'scale(1) rotateX(0deg)';
            card.style.boxShadow = 'none';
            if (card._darkenOverlay) {
              card._darkenOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
            }
          }
        });
      }
      
      function applyCardEffects(card, progress, config) {
        const scaleAmount = (1 - config.scale) * progress;
        const scaleFactor = 1 - scaleAmount;
        
        let transformStr = `scale(${scaleFactor})`;
        
        if (config.rotate) {
          const rotateX = 5 * progress;
          transformStr += ` rotateX(${rotateX}deg)`;
        }
        
        card.style.transform = transformStr;
        
        if (config.shadow) {
          const shadowOpacity = Math.min(0.25, 0.05 + (0.2 * progress));
          const shadowBlur = Math.min(20, 5 + (15 * progress));
          const shadowY = Math.min(15, 2 + (13 * progress));
          
          card.style.boxShadow = `0 ${shadowY}px ${shadowBlur}px rgba(0, 0, 0, ${shadowOpacity})`;
        } else {
          card.style.boxShadow = 'none';
        }
        
        if (config.darken && card._darkenOverlay) {
          const darkness = Math.min(0.2, 0.05 + (0.15 * progress));
          card._darkenOverlay.style.backgroundColor = `rgba(0, 0, 0, ${darkness})`;
        } else if (card._darkenOverlay) {
          card._darkenOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
        }
      }
      
      function updatePreview() {
        const previewContainer = document.getElementById('scrollstack-preview');
        const currentScroll = previewContainer ? previewContainer.scrollTop : 0;
        
        createPreviewContent();
        
        setTimeout(() => {
          const container = document.getElementById('scrollstack-preview');
          if (container) {
            container.scrollTop = Math.max(30, currentScroll);
          }
        }, 50);
      }
      
      function generateRandomPalette() {
        scrollStackConfig.animationDuration = Math.round((Math.random() * 1.9 + 0.1) * 10) / 10;
        scrollStackConfig.staggerOffset = Math.floor(Math.random() * 50);
        scrollStackConfig.scale = Math.round((Math.random() * 0.19 + 0.8) * 100) / 100;
        scrollStackConfig.scaleIncrement = Math.round((Math.random() * 0.09 + 0.01) * 100) / 100;
        scrollStackConfig.perspective = Math.floor(Math.random() * 1500) + 500;
        scrollStackConfig.offset = Math.floor(Math.random() * 40);
        scrollStackConfig.padding = Math.floor(Math.random() * 95) + 5;
        scrollStackConfig.mobileScale = Math.round((Math.random() * 0.5 + 0.4) * 100) / 100;
        scrollStackConfig.threshold = Math.floor(Math.random() * 720) + 480;
        scrollStackConfig.rotate = Math.random() > 0.5;
        scrollStackConfig.shadow = Math.random() > 0.3;
        scrollStackConfig.darken = Math.random() > 0.3;
        scrollStackConfig.stackOnTop = Math.random() > 0.5;
        scrollStackConfig.disableMobile = Math.random() > 0.7;
        
        document.getElementById('animation-duration').value = scrollStackConfig.animationDuration;
        document.getElementById('stagger-offset').value = scrollStackConfig.staggerOffset;
        document.getElementById('scale').value = scrollStackConfig.scale;
        document.getElementById('scale-increment').value = scrollStackConfig.scaleIncrement;
        document.getElementById('perspective').value = scrollStackConfig.perspective;
        document.getElementById('offset').value = scrollStackConfig.offset;
        document.getElementById('padding').value = scrollStackConfig.padding;
        document.getElementById('mobile-scale').value = scrollStackConfig.mobileScale;
        document.getElementById('threshold').value = scrollStackConfig.threshold;
        document.getElementById('rotate-toggle').checked = scrollStackConfig.rotate;
        document.getElementById('shadow-toggle').checked = scrollStackConfig.shadow;
        document.getElementById('darken-toggle').checked = scrollStackConfig.darken;
        document.getElementById('stack-top-toggle').checked = scrollStackConfig.stackOnTop;
        document.getElementById('disable-mobile-toggle').checked = scrollStackConfig.disableMobile;
        
        document.getElementById('animation-duration-value').textContent = scrollStackConfig.animationDuration;
        document.getElementById('stagger-offset-value').textContent = scrollStackConfig.staggerOffset;
        document.getElementById('scale-value').textContent = scrollStackConfig.scale;
        document.getElementById('scale-increment-value').textContent = scrollStackConfig.scaleIncrement;
        document.getElementById('perspective-value').textContent = scrollStackConfig.perspective;
        document.getElementById('offset-value').textContent = scrollStackConfig.offset;
        document.getElementById('padding-value').textContent = scrollStackConfig.padding;
        document.getElementById('mobile-scale-value').textContent = scrollStackConfig.mobileScale;
        document.getElementById('threshold-value').textContent = scrollStackConfig.threshold;
        
        updatePreview();
        showNotification('Random palette generated!');
      }

      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
  const sectionId = generateUniqueId();
  const containerId = generateUniqueId();
  const card1Id = generateUniqueId();
  const card2Id = generateUniqueId();
  const card3Id = generateUniqueId();
  const codeId = generateUniqueId();
  const containerAttributeId = generateUniqueId();
  const card1AttributeId = generateUniqueId();
  const card2AttributeId = generateUniqueId();
  const card3AttributeId = generateUniqueId();
  
  // Obtener el código JavaScript generado
  const jsCode = generateJavaScriptCode();
  
  // Crear la estructura JSON completa
  const fullSectionData = {
    "content": [
      {
        "id": sectionId,
        "name": "section",
        "parent": 0,
        "children": [containerId, codeId],
        "settings": {
          "_justifyContent": "center",
          "_background": {
            "color": {
              "hex": "#ffffff"
            }
          }
        }
      },
      {
        "id": containerId,
        "name": "container",
        "parent": sectionId,
        "children": [card1Id, card2Id, card3Id],
        "settings": {
          "_alignItems": "center",
          "_attributes": [
            {
              "id": containerAttributeId,
              "name": "data-scrollstack-container"
            }
          ],
          "_width": "100%"
        },
        "label": "Scroll Stack Container"
      },
      {
        "id": card1Id,
        "name": "div",
        "parent": containerId,
        "children": [],
        "settings": {
          "_width": "600",
          "_height": "250",
          "_border": {
            "radius": {
              "top": "15",
              "right": "15",
              "bottom": "15",
              "left": "15"
            }
          },
          "_background": {
            "color": {
              "hex": "#c0c4f7"
            }
          },
          "_attributes": [
            {
              "id": card1AttributeId,
              "name": "data-scrollstack-card"
            }
          ]
        }
      },
      {
        "id": card2Id,
        "name": "div",
        "parent": containerId,
        "children": [],
        "settings": {
          "_width": "600",
          "_height": "250",
          "_border": {
            "radius": {
              "top": "15",
              "right": "15",
              "bottom": "15",
              "left": "15"
            }
          },
          "_background": {
            "color": {
              "hex": "#c4f8ea"
            }
          },
          "_attributes": [
            {
              "id": card2AttributeId,
              "name": "data-scrollstack-card"
            }
          ]
        }
      },
      {
        "id": card3Id,
        "name": "div",
        "parent": containerId,
        "children": [],
        "settings": {
          "_width": "600",
          "_height": "250",
          "_border": {
            "radius": {
              "top": "15",
              "right": "15",
              "bottom": "15",
              "left": "15"
            }
          },
          "_background": {
            "color": {
              "hex": "#f8f5c4"
            }
          },
          "_attributes": [
            {
              "id": card3AttributeId,
              "name": "data-scrollstack-card"
            }
          ]
        }
      },
      {
        "id": codeId,
        "name": "code",
        "parent": sectionId,
        "children": [],
        "settings": {
          "javascriptCode": jsCode,
          "executeCode": true,
          "_display": "none"
        },
        "label": "Scroll Stack JS"
      }
    ],
    "source": "bricksCopiedElements",
    "sourceUrl": "https://test.bricksfusion.com",
    "version": "2.0.1",
    "globalClasses": [],
    "globalElements": []
  };
  
  return JSON.stringify(fullSectionData, null, 2);
}

      function generateJavaScriptCode() {
        return `(function() {
     const config = {
       offset: ${scrollStackConfig.offset},
       padding: ${scrollStackConfig.padding},
       anchor: '${scrollStackConfig.anchor}',
       disabled: ${scrollStackConfig.disabled},
       threshold: ${scrollStackConfig.threshold},
       scale: ${scrollStackConfig.scale},
       scaleIncrement: ${scrollStackConfig.scaleIncrement},
       shadow: ${scrollStackConfig.shadow},
       darken: ${scrollStackConfig.darken},
       rotate: ${scrollStackConfig.rotate},
       perspective: ${scrollStackConfig.perspective},
       animationDuration: ${scrollStackConfig.animationDuration},
       staggerOffset: ${scrollStackConfig.staggerOffset},
       mobileScale: ${scrollStackConfig.mobileScale},
       disableMobile: ${scrollStackConfig.disableMobile},
       stackOnTop: ${scrollStackConfig.stackOnTop}
     };
   
     const state = {
       lastScrollY: 0,
       scrollDirection: 'down'
     };
   
     function initScrollStack() {
       const containers = document.querySelectorAll('[data-scrollstack-container]');
       
       if (!containers.length) return;
       
       containers.forEach(container => {
         const cards = Array.from(container.querySelectorAll('[data-scrollstack-card]'));
         if (!cards.length) return;
         
         const containerConfig = {
           offset: parseFloat(container.getAttribute('data-scrollstack-offset')) || config.offset,
           padding: parseFloat(container.getAttribute('data-scrollstack-padding')) || config.padding,
           anchor: container.getAttribute('data-scrollstack-anchor') || config.anchor,
           disabled: container.getAttribute('data-scrollstack-disabled') === 'true' || config.disabled,
           threshold: parseInt(container.getAttribute('data-scrollstack-threshold')) || config.threshold,
           scale: parseFloat(container.getAttribute('data-scrollstack-scale')) || config.scale,
           scaleIncrement: parseFloat(container.getAttribute('data-scrollstack-scale-increment')) || config.scaleIncrement,
           shadow: container.getAttribute('data-scrollstack-shadow') !== 'false' && config.shadow,
           darken: container.getAttribute('data-scrollstack-darken') !== 'false' && config.darken,
           rotate: container.getAttribute('data-scrollstack-rotate') !== 'false' && config.rotate,
           perspective: parseInt(container.getAttribute('data-scrollstack-perspective')) || config.perspective,
           animationDuration: parseFloat(container.getAttribute('data-scrollstack-animation-duration')) || config.animationDuration,
           staggerOffset: parseFloat(container.getAttribute('data-scrollstack-stagger-offset')) || config.staggerOffset,
           mobileScale: parseFloat(container.getAttribute('data-scrollstack-mobile-scale')) || config.mobileScale,
           disableMobile: container.getAttribute('data-scrollstack-disable-mobile') === 'true' || config.disableMobile,
           stackOnTop: container.getAttribute('data-scrollstack-stack-on-top') === 'true' || config.stackOnTop
         };
         
         if (containerConfig.rotate) {
           container.style.perspective = \`\${containerConfig.perspective}px\`;
         }
         
         setupCards(container, cards, containerConfig);
         
         handleScrollEvent(container, cards, containerConfig);
         
         window.addEventListener('scroll', () => {
           const currentScrollY = window.scrollY;
           state.scrollDirection = currentScrollY > state.lastScrollY ? 'down' : 'up';
           state.lastScrollY = currentScrollY;
           
           handleScrollEvent(container, cards, containerConfig);
         });
         
         let resizeTimeout;
         window.addEventListener('resize', () => {
           clearTimeout(resizeTimeout);
           resizeTimeout = setTimeout(() => {
             setupCards(container, cards, containerConfig);
             handleScrollEvent(container, cards, containerConfig);
           }, 200);
         });
       });
     }
     
     function setupCards(container, cards, config) {
       const isMobile = window.innerWidth < config.threshold;
       
       if ((config.disabled) || (config.disableMobile && isMobile)) {
         cards.forEach(card => {
           card.style.position = '';
           card.style.top = '';
           card.style.zIndex = '';
           card.style.transform = '';
           card.style.boxShadow = '';
           card.style.transition = '';
           if (card._darkenOverlay) {
             card.removeChild(card._darkenOverlay);
             delete card._darkenOverlay;
           }
         });
         return;
       }
       
       container.dataset.containerHeight = container.offsetHeight;
       
       cards.forEach((card, index) => {
         const targetScale = Math.max(0.7, 1 - (index * config.scaleIncrement));
         card.dataset.targetScale = targetScale;
         
         if (!card.dataset.originalHeight) {
           card.dataset.originalHeight = card.offsetHeight;
           card.dataset.originalWidth = card.offsetWidth;
         }
         
         card.style.position = 'relative';
         
         if (config.stackOnTop) {
           card.style.zIndex = index + 1;
         } else {
           card.style.zIndex = cards.length - index;
         }
         
         card.style.transition = \`transform \${config.animationDuration}s ease, box-shadow \${config.animationDuration}s ease\`;
         card.style.willChange = 'transform, box-shadow';
         
         card.style.transformOrigin = 'center top';
         
         if (window.getComputedStyle(card).position === 'static') {
           card.style.position = 'relative';
         }
         
         if (config.darken && !card._darkenOverlay) {
           const overlay = document.createElement('div');
           overlay.style.position = 'absolute';
           overlay.style.top = '0';
           overlay.style.left = '0';
           overlay.style.width = '100%';
           overlay.style.height = '100%';
           overlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
           overlay.style.transition = \`background-color \${config.animationDuration}s ease\`;
           overlay.style.pointerEvents = 'none';
           overlay.style.zIndex = '1';
           overlay.style.borderRadius = 'inherit';
           overlay.className = 'scrollstack-card-overlay';
           
           if (card.firstChild) {
             card.insertBefore(overlay, card.firstChild);
           } else {
             card.appendChild(overlay);
           }
           
           card._darkenOverlay = overlay;
           
           Array.from(card.children).forEach(child => {
             if (child !== overlay) {
               if (window.getComputedStyle(child).position === 'static') {
                 child.style.position = 'relative';
               }
               child.style.zIndex = '2';
             }
           });
         }
         
         card.dataset.initialTop = card.offsetTop;
       });
     }
     
     function calculateStackingProgress(scrollY, triggerPoint, cardHeight) {
       const longAnimationRange = cardHeight * 1.5;
       const scrollPastTrigger = Math.max(0, scrollY - triggerPoint);
       const progress = Math.min(scrollPastTrigger / longAnimationRange, 1);
       return progress;
     }
     
     function applyVisualEffectsToParent(parentCard, childCard, progress, config) {
       const isMobile = window.innerWidth < config.threshold;
       
       if (config.disableMobile && isMobile) {
         resetCardEffects(parentCard);
         return;
       }
       
       const targetScale = parseFloat(parentCard.dataset.targetScale) || config.scale;
       const scaleFactor = 1 - ((1 - targetScale) * progress);
       
       let shadowOpacity = 0;
       let shadowBlur = 0;
       let shadowY = 0;
       
       if (config.shadow) {
         shadowOpacity = Math.min(0.25, 0.05 + (0.2 * progress));
         shadowBlur = Math.min(20, 5 + (15 * progress)) * (isMobile ? 0.7 : 1);
         shadowY = Math.min(15, 2 + (13 * progress)) * (isMobile ? 0.7 : 1);
       }
       
       const rotateX = config.rotate ? Math.min(5, 5 * progress) * (isMobile ? 0.7 : 1) : 0;
       
       parentCard.style.transform = \`scale(\${scaleFactor}) \${config.rotate ? \`rotateX(\${rotateX}deg)\` : ''}\`;
       
       if (config.shadow) {
         parentCard.style.boxShadow = \`0 \${shadowY}px \${shadowBlur}px rgba(0, 0, 0, \${shadowOpacity})\`;
       } else {
         parentCard.style.boxShadow = '';
       }
       
       if (config.darken && parentCard._darkenOverlay) {
         if (!isMobile) {
           const darkness = Math.min(0.2, 0.05 + (0.15 * progress));
           parentCard._darkenOverlay.style.backgroundColor = \`rgba(0, 0, 0, \${darkness})\`;
         } else {
           parentCard._darkenOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
         }
       }
     }
     
     function resetCardEffects(card) {
       card.style.transform = 'scale(1) rotateX(0deg)';
       card.style.boxShadow = '';
       
       if (card._darkenOverlay) {
         card._darkenOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
       }
     }
     
     function getCardStackStart(card, config) {
       return parseInt(card.dataset.initialTop) - config.padding;
     }
     
     function handleScrollEvent(container, cards, config) {
       const isMobile = window.innerWidth < config.threshold;
       
       if ((config.disabled) || (config.disableMobile && isMobile)) {
         cards.forEach(card => {
           card.style.position = 'relative';
           card.style.top = '0';
           resetCardEffects(card);
         });
         return;
       }
       
       const scrollY = window.scrollY;
       const lastCardIndex = cards.length - 1;
       
       cards.forEach((card, index) => {
         const cardHeight = parseInt(card.dataset.originalHeight);
         const stackStart = getCardStackStart(card, config);
         
         if (scrollY >= stackStart) {
           card.style.position = 'sticky';
           
           const offsetAdjustment = isMobile ? config.mobileScale : 1;
           const offset = (config.offset + (index * config.staggerOffset)) * offsetAdjustment;
           card.style.top = \`\${offset}px\`;
         } else {
           card.style.position = 'relative';
           card.style.top = '0';
           resetCardEffects(card);
         }
       });
       
       for (let i = 0; i < cards.length; i++) {
         const card = cards[i];
         
         if (i < lastCardIndex) {
           const nextCard = cards[i + 1];
           
           if (nextCard.style.position === 'sticky') {
             const cardHeight = parseInt(card.dataset.originalHeight);
             const nextCardStackStart = getCardStackStart(nextCard, config);
             
             const progress = calculateStackingProgress(scrollY, nextCardStackStart, cardHeight);
             
             applyVisualEffectsToParent(card, nextCard, progress, config);
           }
         }
       }
     }
     
     function shouldReduceEffects() {
       const navigatorInfo = window.navigator;
       const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigatorInfo.userAgent);
       const isSlowConnection = navigatorInfo.connection && 
         (navigatorInfo.connection.saveData === true || 
         ['slow-2g', '2g', '3g'].includes(navigatorInfo.connection.effectiveType));
       const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
       
       return isMobile || isSlowConnection || prefersReducedMotion;
   }
   
   function initWithAccessibility() {
     if (shouldReduceEffects()) {
       config.shadow = false;
       config.rotate = false;
       config.animationDuration = 0.4;
       config.staggerOffset = 10;
     }
     
     initScrollStack();
   }
   
   if (document.readyState === 'loading') {
     document.addEventListener('DOMContentLoaded', initWithAccessibility);
   } else {
     initWithAccessibility();
   }
   
   window.scrollStack = {
     init: initWithAccessibility,
     toggleAnimations: function(enable) {
       const containers = document.querySelectorAll('[data-scrollstack-container]');
       containers.forEach(container => {
         container.setAttribute('data-scrollstack-disabled', enable ? 'false' : 'true');
         this.init();
       });
     },
     toggleMobileAnimations: function(enable) {
       const containers = document.querySelectorAll('[data-scrollstack-container]');
       containers.forEach(container => {
         container.setAttribute('data-scrollstack-disable-mobile', enable ? 'false' : 'true');
         this.init();
       });
     }
   };
})();`;
      }

      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 'animation-duration':
              scrollStackConfig.animationDuration = defaultValue;
              updatePreview();
              break;
            case 'stagger-offset':
              scrollStackConfig.staggerOffset = defaultValue;
              updatePreview();
              break;
            case 'scale':
              scrollStackConfig.scale = defaultValue;
              updatePreview();
              break;
            case 'scale-increment':
              scrollStackConfig.scaleIncrement = defaultValue;
              updatePreview();
              break;
            case 'perspective':
              scrollStackConfig.perspective = defaultValue;
              updatePreview();
              break;
            case 'offset':
              scrollStackConfig.offset = defaultValue;
              updatePreview();
              break;
            case 'padding':
              scrollStackConfig.padding = defaultValue;
              updatePreview();
              break;
            case 'mobile-scale':
              scrollStackConfig.mobileScale = defaultValue;
              updatePreview();
              break;
            case 'threshold':
              scrollStackConfig.threshold = defaultValue;
              updatePreview();
              break;
          }
          
          showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
        }
      };

      function initializeUI() {
        setTimeout(() => {
          createPreviewContent();
        }, 100);
        
        const instructionsToggle = document.getElementById('instructions-toggle');
        const instructionsContent = document.getElementById('instructions-content');
        const instructionsCard = document.getElementById('instructions-card');
        const toggleIcon = instructionsToggle?.querySelector('.toggle-icon');
        
        if (instructionsToggle && instructionsContent && instructionsCard && toggleIcon) {
          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');
            }
          });
        }

        const quickAttribute = document.getElementById('quick-attribute');
        if (quickAttribute) {
          quickAttribute.addEventListener('click', () => {
            copyToClipboard('data-scrollstack-container');
          });
        }

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

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

        const generateRandomPaletteBtn = document.getElementById('generate-random-palette');
        if (generateRandomPaletteBtn) {
          generateRandomPaletteBtn.addEventListener('click', () => {
            generateRandomPalette();
          });
        }

        const resetAnimation = document.getElementById('reset-animation');
        if (resetAnimation) {
          resetAnimation.addEventListener('click', () => {
            scrollStackConfig.animationDuration = defaultConfig.animationDuration;
            scrollStackConfig.staggerOffset = defaultConfig.staggerOffset;
            scrollStackConfig.scale = defaultConfig.scale;
            scrollStackConfig.scaleIncrement = defaultConfig.scaleIncrement;
            
            const animationDuration = document.getElementById('animation-duration');
            const staggerOffset = document.getElementById('stagger-offset');
            const scale = document.getElementById('scale');
            const scaleIncrement = document.getElementById('scale-increment');
            const animationDurationValue = document.getElementById('animation-duration-value');
            const staggerOffsetValue = document.getElementById('stagger-offset-value');
            const scaleValue = document.getElementById('scale-value');
            const scaleIncrementValue = document.getElementById('scale-increment-value');
            
            if (animationDuration) animationDuration.value = defaultConfig.animationDuration;
            if (staggerOffset) staggerOffset.value = defaultConfig.staggerOffset;
            if (scale) scale.value = defaultConfig.scale;
            if (scaleIncrement) scaleIncrement.value = defaultConfig.scaleIncrement;
            if (animationDurationValue) animationDurationValue.textContent = defaultConfig.animationDuration;
            if (staggerOffsetValue) staggerOffsetValue.textContent = defaultConfig.staggerOffset;
            if (scaleValue) scaleValue.textContent = defaultConfig.scale;
            if (scaleIncrementValue) scaleIncrementValue.textContent = defaultConfig.scaleIncrement;
            
            updatePreview();
            showNotification('Animation settings reset');
          });
        }

        const resetEffects = document.getElementById('reset-effects');
        if (resetEffects) {
          resetEffects.addEventListener('click', () => {
            scrollStackConfig.rotate = defaultConfig.rotate;
            scrollStackConfig.shadow = defaultConfig.shadow;
            scrollStackConfig.darken = defaultConfig.darken;
            scrollStackConfig.stackOnTop = defaultConfig.stackOnTop;
            
            const rotateToggle = document.getElementById('rotate-toggle');
            const shadowToggle = document.getElementById('shadow-toggle');
            const darkenToggle = document.getElementById('darken-toggle');
            const stackTopToggle = document.getElementById('stack-top-toggle');
            
            if (rotateToggle) rotateToggle.checked = defaultConfig.rotate;
            if (shadowToggle) shadowToggle.checked = defaultConfig.shadow;
            if (darkenToggle) darkenToggle.checked = defaultConfig.darken;
            if (stackTopToggle) stackTopToggle.checked = defaultConfig.stackOnTop;
            
            updatePreview();
            showNotification('Visual effects reset');
          });
        }

        const resetAdvanced = document.getElementById('reset-advanced');
        if (resetAdvanced) {
          resetAdvanced.addEventListener('click', () => {
            scrollStackConfig.perspective = defaultConfig.perspective;
            scrollStackConfig.offset = defaultConfig.offset;
            scrollStackConfig.padding = defaultConfig.padding;
            scrollStackConfig.mobileScale = defaultConfig.mobileScale;
            scrollStackConfig.disableMobile = defaultConfig.disableMobile;
            scrollStackConfig.threshold = defaultConfig.threshold;
            
            const perspective = document.getElementById('perspective');
            const offset = document.getElementById('offset');
            const padding = document.getElementById('padding');
            const mobileScale = document.getElementById('mobile-scale');
            const disableMobileToggle = document.getElementById('disable-mobile-toggle');
            const threshold = document.getElementById('threshold');
            const perspectiveValue = document.getElementById('perspective-value');
            const offsetValue = document.getElementById('offset-value');
            const paddingValue = document.getElementById('padding-value');
            const mobileScaleValue = document.getElementById('mobile-scale-value');
            const thresholdValue = document.getElementById('threshold-value');
            
            if (perspective) perspective.value = defaultConfig.perspective;
            if (offset) offset.value = defaultConfig.offset;
            if (padding) padding.value = defaultConfig.padding;
            if (mobileScale) mobileScale.value = defaultConfig.mobileScale;
            if (disableMobileToggle) disableMobileToggle.checked = defaultConfig.disableMobile;
            if (threshold) threshold.value = defaultConfig.threshold;
            if (perspectiveValue) perspectiveValue.textContent = defaultConfig.perspective;
            if (offsetValue) offsetValue.textContent = defaultConfig.offset;
            if (paddingValue) paddingValue.textContent = defaultConfig.padding;
            if (mobileScaleValue) mobileScaleValue.textContent = defaultConfig.mobileScale;
            if (thresholdValue) thresholdValue.textContent = defaultConfig.threshold;
            
            updatePreview();
            showNotification('Advanced settings reset');
          });
        }

        const controlMapping = {
          'animation-duration': 'animationDuration',
          'stagger-offset': 'staggerOffset',
          'scale': 'scale',
          'scale-increment': 'scaleIncrement',
          'perspective': 'perspective',
          'offset': 'offset',
          'padding': 'padding',
          'mobile-scale': 'mobileScale',
          'threshold': 'threshold'
        };

        document.querySelectorAll('input[type="range"]').forEach(slider => {
          const id = slider.id;
          const valueDisplay = document.getElementById(`${id}-value`);
          const configProp = controlMapping[id] || id;
          
          if (valueDisplay) {
            valueDisplay.textContent = slider.value;
            
            slider.addEventListener('input', function() {
              valueDisplay.textContent = this.value;
              scrollStackConfig[configProp] = parseFloat(this.value);
              updatePreview();
            });
          }
        });

        const checkboxes = [
          { id: 'rotate-toggle', prop: 'rotate' },
          { id: 'shadow-toggle', prop: 'shadow' },
          { id: 'darken-toggle', prop: 'darken' },
          { id: 'disable-mobile-toggle', prop: 'disableMobile' },
          { id: 'stack-top-toggle', prop: 'stackOnTop' }
        ];
        
        checkboxes.forEach(({ id, prop }) => {
          const element = document.getElementById(id);
          if (element) {
            element.addEventListener('change', function() {
              scrollStackConfig[prop] = this.checked;
              updatePreview();
            });
          }
        });

        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;
            }
          }
        });
        
        setTimeout(() => {
          showNotification('ScrollStack configurator loaded!');
        }, 500);
      }
      
      initializeUI();
    });
  </script>
</body>
</html>
Scroll Stack - Bricksfusion
MEDIUM

Scroll Stack

Creates a sticky card stack effect where cards scale down and stack on top of each other as you scroll. Perfect for testimonials, portfolios, and feature sections.

Card 1

Scroll down to see the stacking effect

Card 2

Watch how it shrinks and stacks

Card 3

Each card gets smaller

Card 4

Creating depth and dimension

Card 5

Keep scrolling for the full effect

Position

Offset 0-100px

How far from the top of the screen the first card sticks. Lower values stick closer to the top, higher values give more breathing room.

Default: 16px

Padding 0-200px

Space before the stacking effect starts. Gives visitors room to see the content before cards start stacking.

Default: 50px

Stagger Offset 0-50px

Spacing between stacked cards. Higher values create more visible gaps between cards in the stack.

Default: 20px

Visual Effects

Scale 0.7-1.0

How much cards shrink when they stack. 1.0 means no shrinking, 0.7 means they get quite a bit smaller. Try 0.92 for a subtle effect.

Default: 0.92

Scale Increment 0.01-0.1

How much smaller each card gets compared to the one above it. Smaller values = subtle difference, larger = more dramatic stacking.

Default: 0.04

Shadow on/off

Adds a shadow that grows as cards stack up. Gives depth and makes the stacking effect more realistic.

Default: On

Darken on/off

Slightly darkens cards as they go further back in the stack. Creates a nice depth effect.

Default: On

Rotate on/off

Adds a subtle 3D tilt to cards as they stack. Makes the effect more dynamic and polished.

Default: On

Perspective 500-2000

Controls the intensity of the 3D rotation effect. Lower values = more dramatic angle, higher = more subtle tilt.

Default: 1000

Animation

Animation Duration 0.2-2.0 seconds

How fast cards transform when stacking. Lower = snappy and quick, higher = smooth and flowing. Keep it under 1 second for best feel.

Default: 0.8s

Mobile

Mobile Breakpoint 320-1024px

Screen width where mobile adjustments kick in. Anything smaller than this value uses mobile settings.

Default: 768px

Mobile Scale 0.5-1.0

Reduces the intensity of effects on mobile devices. Lower values make mobile effects more subtle since phone screens are smaller.

Default: 0.6

Disable on Mobile on/off

Completely turns off the stacking effect on mobile. Cards will scroll normally without any stacking animation.

Default: Off

Advanced

Stack on Top on/off

Reverses the stacking order. When on, newer cards stack on top. When off, older cards stay on top (standard behavior).

Default: Off

Performance

This element uses scroll-based animations, so it's slightly more demanding than static elements. The effect only runs when cards are visible on screen and pauses when scrolled out of view to save resources.