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>Text Reveal 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: hidden;
  border-radius: var(--card-radius);
  background-color: #252525;
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.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);
  width: 100%;
  line-height: 1.4;
}

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

.color-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.color-row {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 1rem 1.25rem;
  background-color: rgba(30, 30, 30, 0.7);
  border: 1px solid var(--border);
  border-radius: var(--input-radius);
  transition: var(--transition);
}

.color-row:hover {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.1);
}

.color-picker-container {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  overflow: hidden;
  border: 2px solid var(--border);
  cursor: pointer;
  transition: var(--transition);
  flex-shrink: 0;
  background: var(--card-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  --selected-color: #ffffff;
}

.color-picker-container:hover {
  border-color: var(--accent);
  transform: scale(1.05);
  box-shadow: 0 0 12px rgba(239, 96, 19, 0.3);
}

.color-picker-container::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--selected-color, #ffffff);
  border-radius: 6px;
  transition: var(--transition);
}

input[type="color"] {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
  cursor: pointer;
  background: transparent;
  opacity: 0;
  z-index: 2;
}

.color-input-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.color-label {
  font-size: 10px;
  font-weight: 500;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-left: 0.25rem;
}

.color-input {
  padding: 0.5rem 0.75rem;
  background-color: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-primary);
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: 12px;
  transition: var(--transition);
  min-width: 0;
}

.color-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.2);
  outline: none;
}

.color-input.invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.2);
}

.hex-input,
.hsl-input {
  width: 100%;
}

.color-input-group:nth-child(2) {
  flex: 0.3;
}

.color-input-group:nth-child(3) {
  flex: 0.7;
}

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

select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}

.radio-group {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  margin-bottom: 1rem;
}

.radio-option {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.radio-option input[type="radio"] {
  margin-right: 0.8rem;
  cursor: pointer;
  accent-color: var(--accent);
}

.radio-option label {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  cursor: pointer;
}

.radio-option input[type="radio"]:checked + label {
  color: var(--text-primary);
  font-weight: 500;
}

.style-description {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  margin-top: 0.5rem;
  padding-left: 1.8rem;
  margin-bottom: 0.8rem;
  line-height: 1.5;
}

.text-reveal-word {
  position: relative;
  display: inline-block;
  margin-right: 0.25em;
}

.text-reveal-dim {
  position: absolute;
  top: 0;
  left: 0;
  color: rgba(0,0,0,0.2);
  opacity: 1;
}

.text-reveal-visible {
  position: relative;
  opacity: 0;
  transition: opacity 0.05s ease;
}

.text-sample {
  font-size: 24px;
  width: 100%;
  text-align: center;
  margin-bottom: 1rem;
  line-height: 1.4;
}

#text-reveal-preview p {
  margin-bottom: 1rem;
  width: 100%;
  text-align: center;
}

.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;
  }
  
  .color-row {
    flex-direction: column;
    align-items: stretch;
    gap: 1rem;
    padding: 1rem;
  }
  
  .color-picker-container {
    align-self: center;
    margin-bottom: 0.5rem;
  }
  
  .color-input-group {
    align-items: stretch;
  }
  
  .hex-input,
  .hsl-input {
    width: 100%;
  }
  
  .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/animated-text/" class="breadcrumb-item">Animated text</a>
      <span class="breadcrumb-separator">›</span>
      <span class="breadcrumb-item active">Text Reveal</span>
    </nav>
    
    <div class="action-buttons">
      <div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
        data-text-reveal-section
      </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">Text Reveal</h1>
      <p class="page-subtitle">Interactive text animations 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 text reveal animation 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-text-reveal-section</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="text-reveal-preview" data-text-reveal-section="true">
          <div class="preview-content">
            <p class="text-sample">Discover the power of smooth text reveal animations.<br>
            Create engaging and interactive reading experiences.<br>
            Capture your audience's attention with style.</p>
          </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 Speed
                  <span class="help-tooltip" title="Controls animation sensitivity to scroll">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="animation-duration-value">0.3</span>x</span>
                  <button class="reset-btn" onclick="resetParameter('animation-duration', 0.3)">↺</button>
                </div>
              </div>
              <input type="range" id="animation-duration" min="0.1" max="5" step="0.1" value="0.3">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Trigger Position
                  <span class="help-tooltip" title="Position in viewport where animation triggers">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="trigger-position-value">50</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('trigger-position', 50)">↺</button>
                </div>
              </div>
              <input type="range" id="trigger-position" min="0" max="100" step="5" value="50">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Animation Length
                  <span class="help-tooltip" title="Distance over which animation occurs">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="animation-length-value">100</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('animation-length', 100)">↺</button>
                </div>
              </div>
              <input type="range" id="animation-length" min="20" max="200" step="10" value="100">
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Appearance
            <div class="card-actions">
              <button class="card-action-btn" id="reset-colors" title="Reset Colors">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div class="color-list">
              <div class="color-row">
                <div class="color-picker-container">
                  <input type="color" id="start-color" value="#FFFFFF">
                </div>
                <div class="color-input-group">
                  <span class="color-label">HEX</span>
                  <input type="text" class="color-input hex-input" id="start-color-hex" value="#FFFFFF" placeholder="#FFFFFF">
                </div>
                <div class="color-input-group">
                  <span class="color-label">HSL</span>
                  <input type="text" class="color-input hsl-input" id="start-color-hsl" placeholder="hsl(0, 100%, 50%)">
                </div>
              </div>
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Reveal Color</span>
              </div>
            </div>

            <div class="color-list">
              <div class="color-row">
                <div class="color-picker-container">
                  <input type="color" id="end-color" value="#333333">
                </div>
                <div class="color-input-group">
                  <span class="color-label">HEX</span>
                  <input type="text" class="color-input hex-input" id="end-color-hex" value="#333333" placeholder="#333333">
                </div>
                <div class="color-input-group">
                  <span class="color-label">HSL</span>
                  <input type="text" class="color-input hsl-input" id="end-color-hsl" placeholder="hsl(0, 0%, 20%)">
                </div>
              </div>
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Initial Color</span>
              </div>
            </div>

            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Text Opacity
                  <span class="help-tooltip" title="Opacity of animation transition">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="text-opacity-value">20</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('text-opacity', 20)">↺</button>
                </div>
              </div>
              <input type="range" id="text-opacity" min="5" max="50" step="5" value="20">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Reveal Animation Type</span>
              </div>
              <div class="radio-group">
                <div class="radio-option">
                  <input type="radio" id="reveal-type-line" name="reveal-type" value="line">
                  <label for="reveal-type-line">By Line</label>
                </div>
                <div class="style-description">Each line of text appears as a single unit</div>
                
                <div class="radio-option">
                  <input type="radio" id="reveal-type-word" name="reveal-type" value="word" checked>
                  <label for="reveal-type-word">By Word</label>
                </div>
                <div class="style-description">Each word animates individually in sequence</div>
                
                <div class="radio-option">
                  <input type="radio" id="reveal-type-letter" name="reveal-type" value="letter">
                  <label for="reveal-type-letter">By Letter</label>
                </div>
                <div class="style-description">Each letter animates individually for a typewriter effect</div>
              </div>
            </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">
                  Scroll Smoothness
                  <span class="help-tooltip" title="How smooth the animation responds to scrolling">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="smoothness-value">0.5</span>s</span>
                  <button class="reset-btn" onclick="resetParameter('smoothness', 0.5)">↺</button>
                </div>
              </div>
              <input type="range" id="smoothness" min="0.1" max="1" step="0.1" value="0.5">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">Element Selection</span>
              </div>
              <select id="element-selection">
                <option value="all" selected>All Text Elements</option>
                <option value="headings">Headings Only</option>
                <option value="paragraphs">Paragraphs Only</option>
              </select>
            </div>
          </div>
        </div>
      </section>
    </div>
  </div>

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

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      let textRevealConfig = {
        triggerPosition: 50,
        animationLength: 100,
        startColor: '#FFFFFF',
        endColor: '#333333',
        textOpacity: 20,
        revealType: 'word',
        scrollSmoothness: 0.5,
        elementSelection: 'all',
        animationDuration: 0.3
      };

      const defaultConfig = { ...textRevealConfig };

      let rafId = null;
      let animationTimeouts = new Set();
      
      const previewContainer = document.getElementById('text-reveal-preview');
      
      function initUI() {
        updateColorInputs();
        updatePreview();
      }
      
      function clearAllAnimations() {
        if (rafId) {
          cancelAnimationFrame(rafId);
          rafId = null;
        }
        
        animationTimeouts.forEach(timeoutId => clearTimeout(timeoutId));
        animationTimeouts.clear();
      }
      
      function resetPreview() {
        clearAllAnimations();
        
        const textElements = previewContainer.querySelectorAll('[data-processed="true"]');
        
        textElements.forEach(element => {
          if (element.querySelector('.text-reveal-container')) {
            const originalWords = [];
            const containers = element.querySelectorAll('.text-reveal-word, .text-reveal-word-container, .text-reveal-letter, .text-reveal-line');
            
            const lineContainer = element.querySelector('.text-reveal-line');
            if (lineContainer) {
              const revealedLine = lineContainer.querySelector('.text-reveal-revealed');
              if (revealedLine) {
                element.innerHTML = revealedLine.textContent;
              } else {
                element.innerHTML = lineContainer.textContent;
              }
            } else if (element.querySelector('.text-reveal-word')) {
              element.querySelectorAll('.text-reveal-word').forEach(word => {
                const revealedWord = word.querySelector('.text-reveal-revealed');
                if (revealedWord) {
                  originalWords.push(revealedWord.textContent);
                }
              });
              element.innerHTML = originalWords.join(' ');
            } else if (element.querySelector('.text-reveal-word-container')) {
              let text = '';
              element.querySelectorAll('.text-reveal-word-container').forEach((wordContainer, index) => {
                if (index > 0) {
                  text += ' ';
                }
                wordContainer.querySelectorAll('.text-reveal-letter').forEach(letter => {
                  const revealedLetter = letter.querySelector('.text-reveal-revealed');
                  if (revealedLetter) {
                    text += revealedLetter.textContent;
                  }
                });
              });
              element.innerHTML = text;
            } else {
              const container = element.querySelector('.text-reveal-container');
              if (container) {
                element.innerHTML = container.textContent.trim();
              }
            }
            
            element.removeAttribute('data-processed');
          }
        });
      }
      
      function updateConfig() {
        updateColorsOnly();
      }
      
      function updateColorsOnly() {
        const textElements = previewContainer.querySelectorAll('[data-processed="true"]');
        
        textElements.forEach(element => {
          const revealContainers = element.querySelectorAll('.text-reveal-word, .text-reveal-letter, .text-reveal-line');
          
          revealContainers.forEach(container => {
            const revealedItem = container.querySelector('.text-reveal-revealed');
            const initialItem = container.querySelector('.text-reveal-initial');
            
            if (revealedItem) {
              revealedItem.style.color = textRevealConfig.startColor;
              revealedItem.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
            }
            if (initialItem) {
              initialItem.style.color = textRevealConfig.endColor;
              initialItem.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
            }
          });
        });
      }
      
      function updatePreview() {
        clearAllAnimations();
        resetPreview();
        
        previewContainer.setAttribute('data-text-reveal-section', 'true');
        
        const textElements = previewContainer.querySelectorAll('p');
        
        textElements.forEach(element => {
          processTextElement(element);
        });
        
        const timeoutId = setTimeout(() => {
          simulateRevealAnimation();
        }, 100);
        animationTimeouts.add(timeoutId);
      }
      
      function processTextElement(element) {
        if (element.dataset.processed === 'true') {
          return;
        }
        
        const originalText = element.textContent.trim();
        
        if (!originalText) {
          return;
        }
        
        const computedStyle = window.getComputedStyle(element);
        const originalTextAlign = computedStyle.textAlign;
        const originalFontSize = computedStyle.fontSize;
        const originalFontWeight = computedStyle.fontWeight;
        const originalLineHeight = computedStyle.lineHeight;
        const originalFontFamily = computedStyle.fontFamily;
        
        element.innerHTML = '';
        
        const textContainer = document.createElement('div');
        textContainer.className = 'text-reveal-container';
        textContainer.style.cssText = `
          display: block;
          width: 100%;
          font-size: ${originalFontSize};
          font-weight: ${originalFontWeight};
          line-height: ${originalLineHeight};
          font-family: ${originalFontFamily};
          text-align: ${originalTextAlign};
        `;
        
        const transitionDuration = textRevealConfig.animationDuration;
        
        if (textRevealConfig.revealType === 'letter') {
          const wordFlowContainer = document.createElement('span');
          wordFlowContainer.className = 'text-reveal-word-flow';
          wordFlowContainer.style.cssText = `
            display: inline;
            width: auto;
          `;
          textContainer.appendChild(wordFlowContainer);
          
          const words = originalText.split(/\s+/);
          let letterIndex = 0;
          const lettersTotal = originalText.replace(/\s+/g, '').length;
          
          words.forEach((word, wordIndex) => {
            const wordContainer = document.createElement('span');
            wordContainer.className = 'text-reveal-word-container';
            wordContainer.style.cssText = `
              display: inline-block;
              margin-right: 0.25em;
              white-space: nowrap;
            `;
            
            for (let i = 0; i < word.length; i++) {
              const letter = word[i];
              
              const letterContainer = document.createElement('span');
              letterContainer.className = 'text-reveal-letter';
              letterContainer.style.cssText = `
                position: relative;
                display: inline-block;
              `;
              
              const initialLetterSpan = document.createElement('span');
              initialLetterSpan.className = 'text-reveal-initial';
              initialLetterSpan.textContent = letter;
              initialLetterSpan.style.cssText = `
                position: absolute;
                top: 0;
                left: 0;
                color: ${textRevealConfig.endColor};
                opacity: 1;
                transition: opacity ${transitionDuration}s ease;
              `;
              
              const revealedLetterSpan = document.createElement('span');
              revealedLetterSpan.className = 'text-reveal-revealed';
              revealedLetterSpan.textContent = letter;
              revealedLetterSpan.style.cssText = `
                position: relative;
                opacity: 0;
                transition: opacity ${transitionDuration}s ease;
                color: ${textRevealConfig.startColor};
              `;
              
              letterContainer.dataset.index = letterIndex;
              letterContainer.dataset.total = lettersTotal;
              letterContainer.dataset.revealType = textRevealConfig.revealType;
              
              letterIndex++;
              
              letterContainer.appendChild(initialLetterSpan);
              letterContainer.appendChild(revealedLetterSpan);
              wordContainer.appendChild(letterContainer);
            }
            
            wordFlowContainer.appendChild(wordContainer);
            
            if (wordIndex < words.length - 1) {
              const spaceElement = document.createElement('span');
              spaceElement.className = 'text-reveal-space';
              spaceElement.style.cssText = `
                display: inline-block;
                width: 0.25em;
              `;
              wordFlowContainer.appendChild(spaceElement);
            }
          });
        } else if (textRevealConfig.revealType === 'line') {
          const lineContainer = document.createElement('span');
          lineContainer.className = 'text-reveal-line';
          lineContainer.style.cssText = `
            position: relative;
            display: inline-block;
            width: 100%;
          `;
          
          const initialLineSpan = document.createElement('span');
          initialLineSpan.className = 'text-reveal-initial';
          initialLineSpan.textContent = originalText;
          initialLineSpan.style.cssText = `
            position: absolute;
            top: 0;
            left: 0;
            color: ${textRevealConfig.endColor};
            opacity: 1;
            transition: opacity ${transitionDuration}s ease;
            width: 100%;
          `;
          
          const revealedLineSpan = document.createElement('span');
          revealedLineSpan.className = 'text-reveal-revealed';
          revealedLineSpan.textContent = originalText;
          revealedLineSpan.style.cssText = `
            position: relative;
            opacity: 0;
            transition: opacity ${transitionDuration}s ease;
            color: ${textRevealConfig.startColor};
            width: 100%;
          `;
          
          lineContainer.dataset.index = 0;
          lineContainer.dataset.total = 1;
          lineContainer.dataset.revealType = textRevealConfig.revealType;
          
          lineContainer.appendChild(initialLineSpan);
          lineContainer.appendChild(revealedLineSpan);
          textContainer.appendChild(lineContainer);
        } else {
          const wordFlowContainer = document.createElement('span');
          wordFlowContainer.className = 'text-reveal-word-flow';
          wordFlowContainer.style.cssText = `
            display: inline;
            width: auto;
          `;
          textContainer.appendChild(wordFlowContainer);
          
          const words = originalText.split(/\s+/);
          
          words.forEach((word, index) => {
            const wordContainer = document.createElement('span');
            wordContainer.className = 'text-reveal-word';
            wordContainer.style.cssText = `
              position: relative;
              margin: 0 0.25em 0 0;
              display: inline-block;
            `;
            
            const initialWordSpan = document.createElement('span');
            initialWordSpan.className = 'text-reveal-initial';
            initialWordSpan.textContent = word;
            initialWordSpan.style.cssText = `
              position: absolute;
              top: 0;
              left: 0;
              color: ${textRevealConfig.endColor};
              opacity: 1;
              transition: opacity ${transitionDuration}s ease;
            `;
            
            const revealedWordSpan = document.createElement('span');
            revealedWordSpan.className = 'text-reveal-revealed';
            revealedWordSpan.textContent = word;
            revealedWordSpan.style.cssText = `
              position: relative;
              opacity: 0;
              transition: opacity ${transitionDuration}s ease;
              color: ${textRevealConfig.startColor};
            `;
            
            wordContainer.dataset.index = index;
            wordContainer.dataset.total = words.length;
            wordContainer.dataset.revealType = textRevealConfig.revealType;
            
            wordContainer.appendChild(initialWordSpan);
            wordContainer.appendChild(revealedWordSpan);
            wordFlowContainer.appendChild(wordContainer);
            
            if (index < words.length - 1) {
              const spaceElement = document.createElement('span');
              spaceElement.className = 'text-reveal-space';
              spaceElement.style.cssText = `
                display: inline-block;
                width: 0.25em;
              `;
              wordFlowContainer.appendChild(spaceElement);
            }
          });
        }
        
        element.appendChild(textContainer);
        element.dataset.processed = 'true';
      }
      
      function simulateRevealAnimation() {
        clearAllAnimations();
        
        const textElements = previewContainer.querySelectorAll('[data-processed="true"]');
        
        textElements.forEach(element => {
          const revealContainers = element.querySelectorAll('.text-reveal-word, .text-reveal-letter, .text-reveal-line');
          revealContainers.forEach(container => {
            const revealedItem = container.querySelector('.text-reveal-revealed');
            const initialItem = container.querySelector('.text-reveal-initial');
            
            if (revealedItem) revealedItem.style.opacity = '0';
            if (initialItem) initialItem.style.opacity = '1';
          });
        });
        
        const timeoutId = setTimeout(() => {
          textElements.forEach((element, elementIndex) => {
            const revealType = textRevealConfig.revealType;
            
            if (revealType === 'line') {
              const lines = element.querySelectorAll('.text-reveal-line');
              const delay = elementIndex * 400;
              
              const timeoutId = setTimeout(() => {
                lines.forEach(line => {
                  const revealedLine = line.querySelector('.text-reveal-revealed');
                  const initialLine = line.querySelector('.text-reveal-initial');
                  
                  if (revealedLine) {
                    revealedLine.style.opacity = '1';
                    revealedLine.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
                  }
                  if (initialLine) {
                    initialLine.style.opacity = '0';
                    initialLine.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
                  }
                });
              }, delay);
              animationTimeouts.add(timeoutId);
              
            } else if (revealType === 'word') {
              const words = element.querySelectorAll('.text-reveal-word');
              const baseDelay = elementIndex * 400;
              const sequentialDelay = 100;
              
              const allWords = Array.from(words);
              
              allWords.sort((a, b) => {
                return parseInt(a.dataset.index || 0) - parseInt(b.dataset.index || 0);
              });
              
              allWords.forEach((word, wordIndex) => {
                const delay = baseDelay + (wordIndex * sequentialDelay);
                
                const timeoutId = setTimeout(() => {
                  const revealedWord = word.querySelector('.text-reveal-revealed');
                  const initialWord = word.querySelector('.text-reveal-initial');
                  
                  if (revealedWord) {
                    revealedWord.style.opacity = '1';
                    revealedWord.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
                  }
                  if (initialWord) {
                    initialWord.style.opacity = '0';
                    initialWord.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
                  }
                }, delay);
                animationTimeouts.add(timeoutId);
              });
              
            } else if (revealType === 'letter') {
              const letters = element.querySelectorAll('.text-reveal-letter');
              const baseDelay = elementIndex * 400;
              const sequentialDelay = 30;
              
              const allLetters = Array.from(letters);
              
              allLetters.sort((a, b) => {
                return parseInt(a.dataset.index || 0) - parseInt(b.dataset.index || 0);
              });
              
              allLetters.forEach((letter, letterIndex) => {
                const delay = baseDelay + (letterIndex * sequentialDelay);
                
                const timeoutId = setTimeout(() => {
                  const revealedLetter = letter.querySelector('.text-reveal-revealed');
                  const initialLetter = letter.querySelector('.text-reveal-initial');
                  
                  if (revealedLetter) {
                    revealedLetter.style.opacity = '1';
                    revealedLetter.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
                  }
                  if (initialLetter) {
                    initialLetter.style.opacity = '0';
                    initialLetter.style.transition = `opacity ${textRevealConfig.animationDuration}s ease`;
                  }
                }, delay);
                animationTimeouts.add(timeoutId);
              });
            }
          });
        }, 500);
        animationTimeouts.add(timeoutId);
        
        const animationDuration = Math.max(textRevealConfig.animationDuration * 1000, 500);
        const lettersCount = previewContainer.querySelectorAll('.text-reveal-letter').length;
        const wordsCount = previewContainer.querySelectorAll('.text-reveal-word').length;
        
        let totalDuration = 0;
        if (textRevealConfig.revealType === 'line') {
          totalDuration = 1000 + (animationDuration * 2);
        } else if (textRevealConfig.revealType === 'word') {
          totalDuration = 1000 + (100 * wordsCount) + (animationDuration * 2);
        } else if (textRevealConfig.revealType === 'letter') {
          totalDuration = 1000 + (30 * lettersCount) + (animationDuration * 2);
        }
        
        totalDuration = Math.max(totalDuration, 3000);
        
        const timeoutId2 = setTimeout(() => {
          simulateRevealAnimation();
        }, totalDuration);
        animationTimeouts.add(timeoutId2);
      }
      
      function hexToHsl(hex) {
        if (!hex || typeof hex !== 'string') return { h: 0, s: 0, l: 50 };
        
        const r = parseInt(hex.slice(1, 3), 16) / 255;
        const g = parseInt(hex.slice(3, 5), 16) / 255;
        const b = parseInt(hex.slice(5, 7), 16) / 255;
        
        const max = Math.max(r, g, b);
        const min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;
        
        if (max === min) {
          h = s = 0;
        } else {
          const d = max - min;
          s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
          switch (max) {
            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
            case g: h = (b - r) / d + 2; break;
            case b: h = (r - g) / d + 4; break;
          }
          h /= 6;
        }
        
        return {
          h: Math.round(h * 360),
          s: Math.round(s * 100),
          l: Math.round(l * 100)
        };
      }

      function hslToHex(hsl) {
        if (!hsl || typeof hsl !== 'string') return '#ffffff';
        
        const match = hsl.match(/hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/);
        if (!match) return null;
        
        let h = parseInt(match[1]) / 360;
        let s = parseInt(match[2]) / 100;
        let l = parseInt(match[3]) / 100;
        
        const hue2rgb = (p, q, t) => {
          if (t < 0) t += 1;
          if (t > 1) t -= 1;
          if (t < 1/6) return p + (q - p) * 6 * t;
          if (t < 1/2) return q;
          if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
          return p;
        };
        
        let r, g, b;
        if (s === 0) {
          r = g = b = l;
        } else {
          const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
          const p = 2 * l - q;
          r = hue2rgb(p, q, h + 1/3);
          g = hue2rgb(p, q, h);
          b = hue2rgb(p, q, h - 1/3);
        }
        
        const toHex = (c) => {
          const hex = Math.round(c * 255).toString(16);
          return hex.length === 1 ? '0' + hex : hex;
        };
        
        return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
      }

      function isValidHex(hex) {
        if (!hex || typeof hex !== 'string') return false;
        return /^#[0-9A-F]{6}$/i.test(hex);
      }

      function isValidHsl(hsl) {
        if (!hsl || typeof hsl !== 'string') return false;
        return /^hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)$/i.test(hsl);
      }

      function formatHex(value) {
        if (!value || typeof value !== 'string') return '#ffffff';
        
        let hex = value.replace(/[^0-9A-Fa-f#]/g, '');
        
        if (!hex.startsWith('#')) {
          hex = '#' + hex;
        }
        
        if (hex.length > 7) {
          hex = hex.substring(0, 7);
        }
        
        return hex.toUpperCase();
      }

      function formatHsl(value) {
        if (!value || typeof value !== 'string') return 'hsl(0, 0%, 50%)';
        
        const cleanValue = value.replace(/[^\d,\s]/g, '');
        const numbers = cleanValue.match(/\d+/g);
        
        if (!numbers || numbers.length < 3) {
          const partialMatch = value.match(/(\d+)/g);
          if (partialMatch && partialMatch.length >= 1) {
            const h = Math.min(360, Math.max(0, parseInt(partialMatch[0]) || 0));
            const s = Math.min(100, Math.max(0, parseInt(partialMatch[1]) || 50));
            const l = Math.min(100, Math.max(0, parseInt(partialMatch[2]) || 50));
            return `hsl(${h}, ${s}%, ${l}%)`;
          }
          return value;
        }
        
        let h = Math.min(360, Math.max(0, parseInt(numbers[0])));
        let s = Math.min(100, Math.max(0, parseInt(numbers[1])));
        let l = Math.min(100, Math.max(0, parseInt(numbers[2])));
        
        return `hsl(${h}, ${s}%, ${l}%)`;
      }

      function updateColorInputs() {
        updateColorInputsForField('start-color');
        updateColorInputsForField('end-color');
      }

      function updateColorInputsForField(colorFieldId) {
        const colorInput = document.getElementById(colorFieldId);
        const hexInput = document.getElementById(`${colorFieldId}-hex`);
        const hslInput = document.getElementById(`${colorFieldId}-hsl`);
        
        if (!colorInput || !hexInput || !hslInput) return;
        
        const color = textRevealConfig[colorFieldId.replace('-', '')] || '#ffffff';
        colorInput.value = color;
        hexInput.value = color;
        
        const hslValue = hexToHsl(color);
        hslInput.value = `hsl(${hslValue.h}, ${hslValue.s}%, ${hslValue.l}%)`;
        
        hexInput.classList.remove('invalid');
        hslInput.classList.remove('invalid');
        
        const colorPickerContainer = colorInput.closest('.color-row');
        if (colorPickerContainer) {
          const container = colorPickerContainer.querySelector('.color-picker-container');
          if (container) {
            container.style.setProperty('--selected-color', color);
          }
        }
      }

      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() {
        
      }

      function generateJavaScriptCode() {
        return `(function() {
  if (window.textRevealInitialized) return;
  window.textRevealInitialized = true;

  const CONFIG = {
    triggerPosition: ${textRevealConfig.triggerPosition},
    animationLength: ${textRevealConfig.animationLength},
    startColor: '${textRevealConfig.startColor}',
    endColor: '${textRevealConfig.endColor}',
    textOpacity: ${textRevealConfig.textOpacity / 100},
    revealType: '${textRevealConfig.revealType}',
    scrollSmoothness: ${textRevealConfig.scrollSmoothness},
    elementSelection: '${textRevealConfig.elementSelection}',
    animationSpeed: ${textRevealConfig.animationDuration}
  };

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

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

  if (document.readyState !== 'loading') {
    initTextReveal();
  }
})();`;
      }

      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 'trigger-position':
              textRevealConfig.triggerPosition = defaultValue;
              updatePreview();
              break;
            case 'animation-length':
              textRevealConfig.animationLength = defaultValue;
              updatePreview();
              break;
            case 'text-opacity':
              textRevealConfig.textOpacity = defaultValue;
              updateConfig();
              break;
            case 'smoothness':
              textRevealConfig.scrollSmoothness = defaultValue;
              updatePreview();
              break;
            case 'animation-duration':
              textRevealConfig.animationDuration = defaultValue;
              updateConfig();
              break;
          }
          showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
        }
      };

      function initializeUI() {        
        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-text-reveal-section');
        });

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

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

        document.getElementById('reset-colors').addEventListener('click', () => {
          textRevealConfig.startColor = defaultConfig.startColor;
          textRevealConfig.endColor = defaultConfig.endColor;
          textRevealConfig.textOpacity = defaultConfig.textOpacity;
          
          document.getElementById('start-color').value = defaultConfig.startColor;
          document.getElementById('end-color').value = defaultConfig.endColor;
          document.getElementById('text-opacity').value = defaultConfig.textOpacity;
          document.getElementById('text-opacity-value').textContent = defaultConfig.textOpacity;
          
          updateColorInputs();
          updateConfig();
          showNotification('Colors reset to default');
        });

        document.getElementById('reset-animation').addEventListener('click', () => {
          textRevealConfig.triggerPosition = defaultConfig.triggerPosition;
          textRevealConfig.animationLength = defaultConfig.animationLength;
          textRevealConfig.animationDuration = defaultConfig.animationDuration;
          
          document.getElementById('trigger-position').value = defaultConfig.triggerPosition;
          document.getElementById('animation-length').value = defaultConfig.animationLength;
          document.getElementById('animation-duration').value = defaultConfig.animationDuration;
          
          document.getElementById('trigger-position-value').textContent = defaultConfig.triggerPosition;
          document.getElementById('animation-length-value').textContent = defaultConfig.animationLength;
          document.getElementById('animation-duration-value').textContent = defaultConfig.animationDuration;
          
          updatePreview();
          showNotification('Animation settings reset');
        });

        document.getElementById('reset-advanced').addEventListener('click', () => {
          textRevealConfig.scrollSmoothness = defaultConfig.scrollSmoothness;
          textRevealConfig.elementSelection = defaultConfig.elementSelection;
          
          document.getElementById('smoothness').value = defaultConfig.scrollSmoothness;
          document.getElementById('element-selection').value = defaultConfig.elementSelection;
          document.getElementById('smoothness-value').textContent = defaultConfig.scrollSmoothness;
          
          updatePreview();
          showNotification('Advanced settings reset');
        });

        setupColorInputs('start-color', 'startColor');
        setupColorInputs('end-color', 'endColor');

        const rangeInputs = document.querySelectorAll('input[type="range"]');
        rangeInputs.forEach(input => {
          const valueElement = document.getElementById(`${input.id}-value`);
          
          if (valueElement) {
            valueElement.textContent = input.value;
          }
          
          input.addEventListener('input', () => {
            if (valueElement) {
              valueElement.textContent = input.value;
            }
            
            switch (input.id) {
              case 'trigger-position':
                textRevealConfig.triggerPosition = parseInt(input.value);
                break;
              case 'animation-length':
                textRevealConfig.animationLength = parseInt(input.value);
                break;
              case 'text-opacity':
                textRevealConfig.textOpacity = parseInt(input.value);
                updateConfig();
                return;
              case 'smoothness':
                textRevealConfig.scrollSmoothness = parseFloat(input.value);
                break;
              case 'animation-duration':
                textRevealConfig.animationDuration = parseFloat(input.value);
                updateConfig();
                return;
            }
            
            updatePreview();
          });
        });

        const radioButtons = document.querySelectorAll('input[name="reveal-type"]');
        radioButtons.forEach(button => {
          button.addEventListener('change', function() {
            if (this.checked) {
              textRevealConfig.revealType = this.value;
              clearAllAnimations();
              resetPreview();
              updatePreview();
            }
          });
        });

        document.getElementById('element-selection').addEventListener('change', function() {
          textRevealConfig.elementSelection = this.value;
          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;
            }
          }
        });

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

        function loadConfiguration() {
          try {
            const saved = localStorage.getItem('bricksfusion-text-reveal-config');
            if (saved) {
              const savedConfig = JSON.parse(saved);
              Object.assign(textRevealConfig, savedConfig);
              
              document.getElementById('trigger-position').value = savedConfig.triggerPosition;
              document.getElementById('animation-length').value = savedConfig.animationLength;
              document.getElementById('start-color').value = savedConfig.startColor;
              document.getElementById('end-color').value = savedConfig.endColor;
              document.getElementById('text-opacity').value = savedConfig.textOpacity;
              document.getElementById('smoothness').value = savedConfig.scrollSmoothness;
              document.getElementById('element-selection').value = savedConfig.elementSelection;
              document.getElementById('animation-duration').value = savedConfig.animationDuration;
              
              document.getElementById('trigger-position-value').textContent = savedConfig.triggerPosition;
              document.getElementById('animation-length-value').textContent = savedConfig.animationLength;
              document.getElementById('text-opacity-value').textContent = savedConfig.textOpacity;
              document.getElementById('smoothness-value').textContent = savedConfig.scrollSmoothness;
              document.getElementById('animation-duration-value').textContent = savedConfig.animationDuration;
              
              const revealTypeRadio = document.getElementById(`reveal-type-${savedConfig.revealType}`);
              if (revealTypeRadio) {
                revealTypeRadio.checked = true;
              }
              
              updateColorInputs();
              updatePreview();
            }
          } catch (e) {
          }
        }

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

        const originalUpdateConfig = updateConfig;
        updateConfig = function() {
          originalUpdateConfig();
          saveConfiguration();
        };

        loadConfiguration();
        
        setTimeout(() => {
          showNotification('BricksFusion Text Reveal Configurator loaded!');
        }, 500);
      }

      function setupColorInputs(colorFieldId, configKey) {
        const colorInput = document.getElementById(colorFieldId);
        const hexInput = document.getElementById(`${colorFieldId}-hex`);
        const hslInput = document.getElementById(`${colorFieldId}-hsl`);
        
        if (!colorInput || !hexInput || !hslInput) return;
        
        const currentColor = colorInput.value || '#ffffff';
        const hslValue = hexToHsl(currentColor);
        hslInput.value = `hsl(${hslValue.h}, ${hslValue.s}%, ${hslValue.l}%)`;
        
        colorInput.addEventListener('input', () => {
          const color = colorInput.value || '#ffffff';
          hexInput.value = color;
          const hslVal = hexToHsl(color);
          hslInput.value = `hsl(${hslVal.h}, ${hslVal.s}%, ${hslVal.l}%)`;
          hexInput.classList.remove('invalid');
          hslInput.classList.remove('invalid');
          textRevealConfig[configKey] = color;
          
          const colorRow = colorInput.closest('.color-row');
          if (colorRow) {
            const colorPickerContainer = colorRow.querySelector('.color-picker-container');
            if (colorPickerContainer) {
              colorPickerContainer.style.setProperty('--selected-color', color);
            }
          }
          
          updateConfig();
        });
        
        hexInput.addEventListener('input', (e) => {
          let hex = e.target.value || '';
          
          hex = formatHex(hex);
          e.target.value = hex;
          
                      if (isValidHex(hex)) {
            colorInput.value = hex;
            const hslVal = hexToHsl(hex);
            hslInput.value = `hsl(${hslVal.h}, ${hslVal.s}%, ${hslVal.l}%)`;
            textRevealConfig[configKey] = hex;
            e.target.classList.remove('invalid');
            hslInput.classList.remove('invalid');
            
            const colorRow = colorInput.closest('.color-row');
            if (colorRow) {
              const colorPickerContainer = colorRow.querySelector('.color-picker-container');
              if (colorPickerContainer) {
                colorPickerContainer.style.setProperty('--selected-color', hex);
              }
            }
            
            updateConfig();
          } else {
            e.target.classList.add('invalid');
          }
        });
        
        hexInput.addEventListener('blur', (e) => {
          if (!isValidHex(e.target.value)) {
            e.target.value = colorInput.value || '#ffffff';
            e.target.classList.remove('invalid');
          }
        });
        
        hslInput.addEventListener('input', (e) => {
          let hsl = e.target.value || '';
          
          if (isValidHsl(hsl)) {
            const hex = hslToHex(hsl);
            if (hex) {
              colorInput.value = hex;
              hexInput.value = hex;
              textRevealConfig[configKey] = hex;
              e.target.classList.remove('invalid');
              hexInput.classList.remove('invalid');
              
              const colorRow = colorInput.closest('.color-row');
              if (colorRow) {
                const colorPickerContainer = colorRow.querySelector('.color-picker-container');
                if (colorPickerContainer) {
                  colorPickerContainer.style.setProperty('--selected-color', hex);
                }
              }
              
              updateConfig();
              return;
            }
          }
          
          e.target.classList.add('invalid');
        });
        
        hslInput.addEventListener('blur', (e) => {
          let hsl = e.target.value || '';
          
          if (!isValidHsl(hsl) && hsl.trim()) {
            const formatted = formatHsl(hsl);
            if (isValidHsl(formatted)) {
              e.target.value = formatted;
              const hex = hslToHex(formatted);
              if (hex) {
                colorInput.value = hex;
                hexInput.value = hex;
                textRevealConfig[configKey] = hex;
                e.target.classList.remove('invalid');
                hexInput.classList.remove('invalid');
                updateConfig();
                return;
              }
            }
          }
          
          if (!isValidHsl(e.target.value)) {
            const currentColor = colorInput.value || '#ffffff';
            const hslVal = hexToHsl(currentColor);
            e.target.value = `hsl(${hslVal.h}, ${hslVal.s}%, ${hslVal.l}%)`;
            e.target.classList.remove('invalid');
          }
        });
      }
      
      initUI();
      initializeUI();
    });
  </script>
</body>
</html>
Text Reveal - Bricksfusion
LIGHTWEIGHT

Text Reveal

Changes text color progressively as you scroll, creating a dynamic reading experience. Perfect for headlines, hero text, and highlighting key messages.

Scroll down to watch this text come to life letter by letter

↓ Keep scrolling to see the full effect

Appearance

Start Color color picker

Initial color before the text is revealed. Usually a muted gray or color that blends with the background.

Default: White (#FFFFFF)

End Color color picker

Final color when text is fully revealed. This should be your main text color or a vibrant accent color for emphasis.

Default: Dark Gray (#333333)

Animation

Reveal Type word / letter / line

How the text reveals. Letter does it character by character (dramatic and eye-catching, great for headlines), Word changes color word by word (smooth for longer text), Line changes the entire sentence at once (simple and clean).

Default: Word

Speed 0.1-2.0

Controls how quickly text changes color. Lower = faster reveal as you scroll, higher = slower and more gradual transition.

Default: 0.3

Trigger Position 0-100%

Where on screen the reveal starts. 50% means middle of viewport, 20% starts near top, 80% waits until bottom. Adjust based on where you want focus.

Default: 50% (center)

Animation Length 50-200%

How much scrolling is needed to complete the reveal. 100% means one viewport height, 200% means two screen heights. Longer = more gradual.

Default: 100%

Performance

This element uses optimized scroll tracking with throttling and caching. The color transitions are pure CSS opacity changes, making it very smooth. Works great even with multiple text reveal elements on the same page.