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>SVG 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: 2rem;
            text-align: center;
        }

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

        #svg-reveal-preview {
            width: 90%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
            position: relative;
        }

        .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: #ef6013;
        }

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

        .svg-preview-content {
            width: 200px;
            height: 200px;
        }

        .svg-preview-content svg {
            width: 100%;
            height: 100%;
        }

        .svg-preview-content path,
        .svg-preview-content circle,
        .svg-preview-content rect,
        .svg-preview-content line,
        .svg-preview-content polygon,
        .svg-preview-content polyline {
            fill: none;
            stroke: #ef6013;
            stroke-width: 2;
        }

        .svg-preview-content circle {
            fill: #ef6013;
        }

        .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/visual-effects/" class="breadcrumb-item">Visual effects</a>
            <span class="breadcrumb-separator">›</span>
            <span class="breadcrumb-item active">SVG Reveal</span>
        </nav>
        
        <div class="action-buttons">
            <div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
                data-svg-reveal
            </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">SVG Reveal</h1>
            <p class="page-subtitle">Interactive SVG 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 SVG reveal effect using the controls below</li>
                                <li>Click <strong>Copy JS</strong> to copy the JavaScript code to clipboard</li>
                                <li>In Bricks Builder, add a <strong>Code</strong> element</li>
                                <li>Paste the JavaScript code</li>
                                <li>To add the effect to any SVG element: go to <strong>Section → Style → Attributes</strong>, add <code>data-svg-reveal</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">
                    <div id="svg-reveal-preview" data-svg-reveal="true">
                        <div class="svg-preview-content">
                            <svg viewBox="0 0 200 60" xmlns="http://www.w3.org/2000/svg">
                                <g fill="#ef6013">
                                    <path d="M20,30 C20,23.4 25.4,18 32,18 C38.6,18 44,23.4 44,30 C44,36.6 38.6,42 32,42 C25.4,42 20,36.6 20,30 Z M24,30 C24,34.4 27.6,38 32,38 C36.4,38 40,34.4 40,30 C40,25.6 36.4,22 32,22 C27.6,22 24,25.6 24,30 Z"/>
                                    <path d="M50,20 L54,20 L54,40 L50,40 Z"/>
                                    <path d="M60,20 L64,20 L64,37 L74,37 L74,40 L60,40 Z"/>
                                    <path d="M80,20 L96,20 L96,23 L84,23 L84,28 L94,28 L94,31 L84,31 L84,37 L96,37 L96,40 L80,40 Z"/>
                                    <path d="M102,20 L106,20 L106,37 L116,37 L116,40 L102,40 Z"/>
                                    <path d="M122,20 L126,20 L126,40 L122,40 Z"/>
                                    <path d="M132,20 L136,20 L148,33 L148,20 L152,20 L152,40 L148,40 L136,27 L136,40 L132,40 Z"/>
                                    <path d="M158,20 L174,20 L174,23 L162,23 L162,28 L172,28 L172,31 L162,31 L162,37 L174,37 L174,40 L158,40 Z"/>
                                </g>
                            </svg>
                        </div>
                    </div>
                </div>
            </section>

            <section class="controls-section">
                <div class="card">
                    <div class="card-heading">
                        Colors
                        <div class="card-actions">
                            <button class="card-action-btn" id="reset-colors" title="Reset Colors">↺</button>
                        </div>
                    </div>
                    <div class="card-content">
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">Stroke Color</span>
                            </div>
                            <div class="color-list">
                                <div class="color-row">
                                    <div class="color-picker-container" style="--selected-color: #ef6013;">
                                        <input type="color" id="stroke-color" value="#ef6013">
                                    </div>
                                    <div class="color-input-group">
                                        <span class="color-label">HEX</span>
                                        <input type="text" class="color-input hex-input" id="stroke-color-hex" value="#ef6013" placeholder="#FFFFFF">
                                    </div>
                                    <div class="color-input-group">
                                        <span class="color-label">HSL</span>
                                        <input type="text" class="color-input hsl-input" id="stroke-color-hsl" placeholder="hsl(0, 100%, 50%)">
                                    </div>
                                </div>
                            </div>
                        </div>
                        
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">Fill Color</span>
                            </div>
                            <div class="color-list">
                                <div class="color-row">
                                    <div class="color-picker-container" style="--selected-color: #ffffff;">
                                        <input type="color" id="fill-color" value="#ffffff">
                                    </div>
                                    <div class="color-input-group">
                                        <span class="color-label">HEX</span>
                                        <input type="text" class="color-input hex-input" id="fill-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="fill-color-hsl" placeholder="hsl(0, 100%, 50%)">
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="card">
                    <div class="card-heading">
                        Animation Settings
                        <div class="card-actions">
                            <button class="card-action-btn" id="reset-animation" title="Reset Animation Settings">↺</button>
                        </div>
                    </div>
                    <div class="card-content">
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">
                                    Duration
                                    <span class="help-tooltip" title="Total duration of the animation">ℹ</span>
                                </span>
                                <div class="value-display">
                                    <span class="value-text"><span id="duration-value">1500</span>ms</span>
                                    <button class="reset-btn" onclick="resetParameter('duration', 1500)">↺</button>
                                </div>
                            </div>
                            <input type="range" id="duration" min="500" max="3000" step="100" value="1500">
                        </div>
                        
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">
                                    Base Delay
                                    <span class="help-tooltip" title="Initial delay before animation starts">ℹ</span>
                                </span>
                                <div class="value-display">
                                    <span class="value-text"><span id="base-delay-value">0</span>ms</span>
                                    <button class="reset-btn" onclick="resetParameter('base-delay', 0)">↺</button>
                                </div>
                            </div>
                            <input type="range" id="base-delay" min="0" max="1000" step="50" value="0">
                        </div>
                        
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">
                                    Delay Increment
                                    <span class="help-tooltip" title="Delay between each element in sequential mode">ℹ</span>
                                </span>
                                <div class="value-display">
                                    <span class="value-text"><span id="delay-increment-value">100</span>ms</span>
                                    <button class="reset-btn" onclick="resetParameter('delay-increment', 100)">↺</button>
                                </div>
                            </div>
                            <input type="range" id="delay-increment" min="0" max="300" step="10" value="100">
                        </div>
                        
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">
                                    Stroke Width
                                    <span class="help-tooltip" title="Thickness of the animated stroke">ℹ</span>
                                </span>
                                <div class="value-display">
                                    <span class="value-text"><span id="stroke-width-value">1</span>px</span>
                                    <button class="reset-btn" onclick="resetParameter('stroke-width', 1)">↺</button>
                                </div>
                            </div>
                            <input type="range" id="stroke-width" min="0.5" max="5" step="0.5" value="1">
                        </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">Animation Order</span>
                            </div>
                            <div class="radio-group">
                                <div class="radio-option">
                                    <input type="radio" id="order-sequential" name="animation-order" value="sequential" checked>
                                    <label for="order-sequential">Sequential</label>
                                </div>
                                <div class="style-description">Each element animates one after another</div>
                                
                                <div class="radio-option">
                                    <input type="radio" id="order-simultaneous" name="animation-order" value="simultaneous">
                                    <label for="order-simultaneous">Simultaneous</label>
                                </div>
                                <div class="style-description">All elements animate at the same time</div>
                            </div>
                        </div>
                        
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">Easing Function</span>
                            </div>
                            <select id="easing">
                                <option value="ease">Ease</option>
                                <option value="ease-in">Ease In</option>
                                <option value="ease-out">Ease Out</option>
                                <option value="ease-in-out">Ease In Out</option>
                                <option value="linear">Linear</option>
                                <option value="cubic-bezier(0.65, 0, 0.35, 1)" selected>Custom Bounce</option>
                            </select>
                        </div>
                        
                        <div class="control-group">
                            <div class="control-label">
                                <span class="label-text">
                                    Intersection Threshold
                                    <span class="help-tooltip" title="How much of the SVG must be visible to trigger animation">ℹ</span>
                                </span>
                                <div class="value-display">
                                    <span class="value-text"><span id="threshold-value">0.5</span></span>
                                    <button class="reset-btn" onclick="resetParameter('threshold', 0.5)">↺</button>
                                </div>
                            </div>
                            <input type="range" id="threshold" min="0.1" max="1" step="0.1" value="0.5">
                        </div>
                    </div>
                </div>
            </section>
        </div>
    </div>

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

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            let svgRevealConfig = {
                duration: 1500,
                baseDelay: 0,
                delayIncrement: 100,
                animationOrder: 'sequential',
                easing: 'cubic-bezier(0.65, 0, 0.35, 1)',
                strokeColor: '#ef6013',
                fillColor: '#ffffff',
                strokeWidth: 1,
                threshold: 0.5
            };

            const defaultConfig = { ...svgRevealConfig };

            const previewContainer = document.getElementById('svg-reveal-preview');

            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 copyToClipboard(text) {
                navigator.clipboard.writeText(text)
                    .then(() => {
                        showNotification('Copied to clipboard!');
                    })
                    .catch(err => {
                        showNotification('Failed to copy to clipboard', 'error');
                    });
            }

            function hexToHsl(hex) {
                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) {
                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) {
                return /^#[0-9A-F]{6}$/i.test(hex);
            }

            function isValidHsl(hsl) {
                return /^hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)$/i.test(hsl);
            }

            function formatHex(value) {
                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) {
                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 updatePreview() {
                resetPreview();
                
                previewContainer.setAttribute('data-svg-reveal', 'true');
                previewContainer.setAttribute('data-duration', svgRevealConfig.duration);
                previewContainer.setAttribute('data-base-delay', svgRevealConfig.baseDelay);
                previewContainer.setAttribute('data-delay-increment', svgRevealConfig.delayIncrement);
                previewContainer.setAttribute('data-animation-order', svgRevealConfig.animationOrder);
                previewContainer.setAttribute('data-easing', svgRevealConfig.easing);
                
                const svgElements = previewContainer.querySelectorAll('path, line, circle, rect, polygon, polyline');
                svgElements.forEach(element => {
                    element.style.stroke = svgRevealConfig.strokeColor;
                    element.style.strokeWidth = svgRevealConfig.strokeWidth;
                    
                    if (element.getAttribute('fill') !== 'none') {
                        element.style.fill = svgRevealConfig.fillColor;
                    }
                });
                
                initPreviewAnimation();
            }

            function resetPreview() {
                if (window.svgRevealTimeout) {
                    clearTimeout(window.svgRevealTimeout);
                    window.svgRevealTimeout = null;
                }
                
                Array.from(previewContainer.querySelectorAll('[data-cleanup]')).forEach(element => {
                    const timeoutId = parseInt(element.getAttribute('data-cleanup'));
                    if (!isNaN(timeoutId)) {
                        clearTimeout(timeoutId);
                    }
                    element.removeAttribute('data-cleanup');
                });
                
                previewContainer.removeAttribute('data-svg-reveal');
                
                Array.from(previewContainer.querySelectorAll('[data-animated-clone]')).forEach(clone => {
                    clone.remove();
                });
                
                Array.from(previewContainer.querySelectorAll('svg *')).forEach(element => {
                    element.style.opacity = '';
                    element.style.fill = '';
                    element.style.stroke = '';
                    element.style.strokeWidth = '';
                });
            }

            function initPreviewAnimation() {
                const svg = previewContainer.querySelector('svg');
                if (!svg) return;
                
                const elements = Array.from(svg.querySelectorAll('path, line, circle, rect, polygon, polyline, g')).filter(el => {
                    return el.tagName.toLowerCase() !== 'g' || Array.from(el.children).length === 0;
                });
                
                if (!elements.length) return;
                
                const fragment = document.createDocumentFragment();
                const cloneData = new Map();
                
                elements.forEach(element => {
                    const clone = element.cloneNode(true);
                    const originalFill = element.getAttribute('fill');
                    
                    element.style.opacity = '0';
                    
                    clone.removeAttribute('id');
                    
                    clone.style.fill = 'none';
                    clone.style.stroke = svgRevealConfig.strokeColor;
                    clone.style.strokeWidth = svgRevealConfig.strokeWidth;
                    
                    let length;
                    const tagName = element.tagName.toLowerCase();
                    
                    try {
                        switch (tagName) {
                            case 'circle':
                                const radius = parseFloat(element.getAttribute('r'));
                                length = 2 * Math.PI * radius;
                                break;
                            case 'rect':
                                const width = parseFloat(element.getAttribute('width'));
                                const height = parseFloat(element.getAttribute('height'));
                                length = 2 * (width + height);
                                break;
                            case 'line':
                                const [x1, y1, x2, y2] = ['x1', 'y1', 'x2', 'y2'].map(attr => 
                                    parseFloat(element.getAttribute(attr)) || 0
                                );
                                length = Math.hypot(x2 - x1, y2 - y1);
                                break;
                            case 'polygon':
                            case 'polyline':
                                const points = element.getAttribute('points')?.trim().split(/[\s,]+/).map(Number);
                                if (points && points.length >= 4) {
                                    length = points.reduce((acc, _, i, arr) => {
                                        if (i % 2 === 0 && i < arr.length - 2) {
                                            return acc + Math.hypot(
                                                arr[i + 2] - arr[i],
                                                arr[i + 3] - arr[i + 1]
                                            );
                                        }
                                        return acc;
                                    }, 0);
                                } else {
                                    length = 100;
                                }
                                break;
                            default:
                                length = element.getTotalLength?.() || 100;
                        }
                    } catch (error) {
                        length = 100;
                    }
                    
                    clone.style.strokeDasharray = length;
                    clone.style.strokeDashoffset = length;
                    
                    clone.setAttribute('data-animated-clone', '');
                    clone.setAttribute('data-original-fill', originalFill || 'none');
                    
                    fragment.appendChild(clone);
                    cloneData.set(clone, { element, length });
                });
                
                svg.appendChild(fragment);
                
                const clones = svg.querySelectorAll('[data-animated-clone]');
                
                requestAnimationFrame(() => {
                    clones.forEach((clone, index) => {
                        const delay = svgRevealConfig.animationOrder === 'sequential' 
                            ? svgRevealConfig.baseDelay + (index * svgRevealConfig.delayIncrement) 
                            : svgRevealConfig.baseDelay;
                        const { element, length } = cloneData.get(clone);
                        const originalFill = clone.getAttribute('data-original-fill');
                        
                        clone.style.transition = `stroke-dashoffset ${svgRevealConfig.duration}ms ${svgRevealConfig.easing} ${delay}ms`;
                        
                        requestAnimationFrame(() => {
                            requestAnimationFrame(() => {
                                clone.style.strokeDashoffset = '0';
                            });
                        });
                        
                        const cleanupTimeout = setTimeout(() => {
                            element.style.transition = 'opacity 300ms ease';
                            element.style.opacity = '1';
                            
                            element.style.stroke = svgRevealConfig.strokeColor;
                            element.style.strokeWidth = svgRevealConfig.strokeWidth;
                            
                            if (originalFill !== 'none') {
                                element.style.fill = svgRevealConfig.fillColor;
                            }
                            
                            clone.style.opacity = '0';
                            
                            setTimeout(() => {
                                if (clone.parentNode) {
                                    clone.remove();
                                }
                                cloneData.delete(clone);
                            }, 300);
                        }, delay + svgRevealConfig.duration);
                        
                        element.setAttribute('data-cleanup', cleanupTimeout);
                    });
                });
                
                const totalDuration = svgRevealConfig.animationOrder === 'sequential'
                    ? svgRevealConfig.baseDelay + (elements.length * svgRevealConfig.delayIncrement) + svgRevealConfig.duration
                    : svgRevealConfig.baseDelay + svgRevealConfig.duration;
                    
                window.svgRevealTimeout = setTimeout(() => {
                    window.svgRevealTimeout = null;
                    updatePreview();
                }, totalDuration + 1000);
            }

            function updateColorInputs(colorType) {
                const colorInput = document.getElementById(`${colorType}-color`);
                const hexInput = document.getElementById(`${colorType}-color-hex`);
                const hslInput = document.getElementById(`${colorType}-color-hsl`);
                
                if (colorInput && hexInput && hslInput) {
                    const color = colorType === 'stroke' ? svgRevealConfig.strokeColor : svgRevealConfig.fillColor;
                    
                    colorInput.value = color;
                    hexInput.value = color;
                    hslInput.value = `hsl(${hexToHsl(color).h}, ${hexToHsl(color).s}%, ${hexToHsl(color).l}%)`;
                    
                    hexInput.classList.remove('invalid');
                    hslInput.classList.remove('invalid');
                    
                    const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
                    if (colorPickerContainer) {
                        colorPickerContainer.style.setProperty('--selected-color', color);
                    }
                }
            }

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

            function generateFullSectionJSON() {
  // Generar IDs únicos para todos los elementos
  const sectionId = generateUniqueId();
  const containerId = generateUniqueId();
  const divId = generateUniqueId();
  const svgId = generateUniqueId();
  const codeId = generateUniqueId();
  const attributeId = generateUniqueId();

  // Obtener el JavaScript actual con la configuración del usuario
  const jsCode = generateJavaScriptCode();

  // Crear el objeto JSON completo de Bricks Builder
  const bricksJSON = {
    "content": [
      {
        "id": sectionId,
        "name": "section",
        "parent": 0,
        "children": [containerId, codeId],
        "settings": {
          "_height": "500",
          "_justifyContent": "center"
        }
      },
      {
        "id": containerId,
        "name": "container",
        "parent": sectionId,
        "children": [divId],
        "settings": {
          "_alignItems": "center",
          "_overflow": "hidden"
        }
      },
      {
        "id": divId,
        "name": "div",
        "parent": containerId,
        "children": [svgId],
        "settings": {}
      },
      {
        "id": svgId,
        "name": "svg",
        "parent": divId,
        "children": [],
        "settings": {
          "file": {
            "id": 768,
            "filename": "logoipsum-297.svg",
            "url": "https://test.bricksfusion.com/wp-content/uploads/2024/07/logoipsum-297.svg"
          },
          "_attributes": [
            {
              "id": attributeId,
              "name": "data-svg-reveal"
            }
          ]
        },
        "label": "SVG Reveal"
      },
      {
        "id": codeId,
        "name": "code",
        "parent": sectionId,
        "children": [],
        "settings": {
          "javascriptCode": jsCode,
          "executeCode": true,
          "_display": "none"
        },
        "label": "SVG Reveal JS"
      }
    ],
    "source": "bricksCopiedElements",
    "sourceUrl": "https://test.bricksfusion.com",
    "version": "2.0.1",
    "globalClasses": [],
    "globalElements": []
  };

  return JSON.stringify(bricksJSON, null, 2);
}

            function generateJavaScriptCode() {
                return `(function() {
    if (window.svgRevealInitialized) return;
    window.svgRevealInitialized = true;
   
    const initSVGReveal = () => {
        const observerOptions = {
            root: null,
            rootMargin: '0px',
            threshold: ${svgRevealConfig.threshold}
        };
    
        const handleSVGAnimation = (svg) => {
            try {
                Array.from(svg.querySelectorAll('[data-animated-clone]')).forEach(clone => {
                    clone.remove();
                });
                
                Array.from(svg.querySelectorAll('[data-cleanup]')).forEach(element => {
                    const timeoutId = parseInt(element.getAttribute('data-cleanup'));
                    if (!isNaN(timeoutId)) {
                        clearTimeout(timeoutId);
                    }
                    element.removeAttribute('data-cleanup');
                });
                
                Array.from(svg.querySelectorAll('[data-clone]')).forEach(element => {
                    element.removeAttribute('data-clone');
                    element.style.opacity = '';
                });
                
                const baseDelay = Math.max(0, parseInt(svg.getAttribute('data-base-delay')) || ${svgRevealConfig.baseDelay});
                const delayIncrement = Math.max(0, parseInt(svg.getAttribute('data-delay-increment')) || ${svgRevealConfig.delayIncrement});
                const animationOrder = svg.getAttribute('data-animation-order') || '${svgRevealConfig.animationOrder}';
                const duration = Math.max(100, parseInt(svg.getAttribute('data-duration')) || ${svgRevealConfig.duration});
                const easing = svg.getAttribute('data-easing') || '${svgRevealConfig.easing}';
                
                const elements = Array.from(svg.querySelectorAll('path, line, circle, rect, polygon, polyline, g')).filter(el => {
                    return el.tagName.toLowerCase() !== 'g' || Array.from(el.children).length === 0;
                });
        
                if (!elements.length) return;
        
                const fragment = document.createDocumentFragment();
                const cloneData = new Map();
        
                elements.forEach(element => {
                    const clone = element.cloneNode(true);
                    const originalFill = element.getAttribute('fill');
                    
                    element.style.opacity = '0';
                    
                    clone.removeAttribute('id');
                    
                    clone.style.fill = 'none';
                    clone.style.stroke = '${svgRevealConfig.strokeColor}';
                    clone.style.strokeWidth = '${svgRevealConfig.strokeWidth}';
                    
                    let length;
                    const tagName = element.tagName.toLowerCase();
                    
                    try {
                        switch (tagName) {
                            case 'circle':
                                const radius = parseFloat(element.getAttribute('r'));
                                length = 2 * Math.PI * radius;
                                break;
                            case 'rect':
                                const width = parseFloat(element.getAttribute('width'));
                                const height = parseFloat(element.getAttribute('height'));
                                length = 2 * (width + height);
                                break;
                            case 'line':
                                const [x1, y1, x2, y2] = ['x1', 'y1', 'x2', 'y2'].map(attr => 
                                    parseFloat(element.getAttribute(attr)) || 0
                                );
                                length = Math.hypot(x2 - x1, y2 - y1);
                                break;
                            case 'polygon':
                            case 'polyline':
                                const points = element.getAttribute('points')?.trim().split(/[\\s,]+/).map(Number);
                                if (points && points.length >= 4) {
                                    length = points.reduce((acc, _, i, arr) => {
                                        if (i % 2 === 0 && i < arr.length - 2) {
                                            return acc + Math.hypot(
                                                arr[i + 2] - arr[i],
                                                arr[i + 3] - arr[i + 1]
                                            );
                                        }
                                        return acc;
                                    }, 0);
                                } else {
                                    length = 100;
                                }
                                break;
                            default:
                                length = element.getTotalLength?.() || 100;
                        }
                    } catch (error) {
                        length = 100; 
                    }
        
                    clone.style.strokeDasharray = length;
                    clone.style.strokeDashoffset = length;
                    
                    element.setAttribute('data-clone', '');
                    clone.setAttribute('data-animated-clone', '');
                    clone.setAttribute('data-original-fill', originalFill || 'none');
                    
                    fragment.appendChild(clone);
                    cloneData.set(clone, { element, length });
                });
        
                elements[0].parentNode.insertBefore(fragment, elements[0]);
        
                const clones = svg.querySelectorAll('[data-animated-clone]');
                
                requestAnimationFrame(() => {
                    clones.forEach((clone, index) => {
                        const delay = animationOrder === 'sequential' ? baseDelay + (index * delayIncrement) : baseDelay;
                        const { element, length } = cloneData.get(clone);
                        const originalFill = clone.getAttribute('data-original-fill');
        
                        clone.style.transition = \`stroke-dashoffset \${duration}ms \${easing} \${delay}ms\`;
                        
                        requestAnimationFrame(() => {
                            requestAnimationFrame(() => {
                                clone.style.strokeDashoffset = '0';
                            });
                        });
        
                        const cleanupTimeout = setTimeout(() => {
                            element.style.transition = 'opacity 300ms ease';
                            element.style.opacity = '1';
                            
                            element.style.stroke = '${svgRevealConfig.strokeColor}';
                            element.style.strokeWidth = '${svgRevealConfig.strokeWidth}';
                            
                            if (originalFill !== 'none') {
                                element.style.fill = '${svgRevealConfig.fillColor}';
                            }
                            
                            clone.style.opacity = '0';
                            
                            setTimeout(() => {
                                if (clone.parentNode) {
                                    clone.remove();
                                }
                                cloneData.delete(clone);
                            }, 300);
                        }, delay + duration);
        
                        element.setAttribute('data-cleanup', cleanupTimeout);
                    });
                });
        
            } catch (error) {}
        };
    
        const observerCallback = (entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting && entry.intersectionRatio >= ${svgRevealConfig.threshold}) {
                    handleSVGAnimation(entry.target);
                    observer.unobserve(entry.target);
                }
            });
        };
    
        const observer = new IntersectionObserver(observerCallback, observerOptions);
    
        document.querySelectorAll('[data-svg-reveal]').forEach(svg => {
            observer.observe(svg);
        });
    };
    
    document.addEventListener('DOMContentLoaded', initSVGReveal);
})();`;
            }

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

            function copyFullSectionToClipboard() {
                const sectionJSON = generateFullSectionJSON();
                
                if (!sectionJSON) {
                    showNotification('Full section feature is not yet configured', 'warning');
                    return;
                }
                
                navigator.clipboard.writeText(sectionJSON)
                    .then(() => {
                        showNotification('Full section JSON copied to clipboard!');
                    })
                    .catch(err => {
                        showNotification('Failed to copy full section', '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 'duration':
                            svgRevealConfig.duration = defaultValue;
                            break;
                        case 'base-delay':
                            svgRevealConfig.baseDelay = defaultValue;
                            break;
                        case 'delay-increment':
                            svgRevealConfig.delayIncrement = defaultValue;
                            break;
                        case 'stroke-width':
                            svgRevealConfig.strokeWidth = defaultValue;
                            break;
                        case 'threshold':
                            svgRevealConfig.threshold = defaultValue;
                            break;
                    }
                    
                    updatePreview();
                    showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
                }
            };

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

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

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

                document.getElementById('reset-colors').addEventListener('click', () => {
                    svgRevealConfig.strokeColor = defaultConfig.strokeColor;
                    svgRevealConfig.fillColor = defaultConfig.fillColor;
                    
                    updateColorInputs('stroke');
                    updateColorInputs('fill');
                    updatePreview();
                    showNotification('Colors reset to default');
                });

                document.getElementById('reset-animation').addEventListener('click', () => {
                    svgRevealConfig.duration = defaultConfig.duration;
                    svgRevealConfig.baseDelay = defaultConfig.baseDelay;
                    svgRevealConfig.delayIncrement = defaultConfig.delayIncrement;
                    svgRevealConfig.strokeWidth = defaultConfig.strokeWidth;
                    
                    document.getElementById('duration').value = defaultConfig.duration;
                    document.getElementById('base-delay').value = defaultConfig.baseDelay;
                    document.getElementById('delay-increment').value = defaultConfig.delayIncrement;
                    document.getElementById('stroke-width').value = defaultConfig.strokeWidth;
                    
                    document.getElementById('duration-value').textContent = defaultConfig.duration;
                    document.getElementById('base-delay-value').textContent = defaultConfig.baseDelay;
                    document.getElementById('delay-increment-value').textContent = defaultConfig.delayIncrement;
                    document.getElementById('stroke-width-value').textContent = defaultConfig.strokeWidth;
                    
                    updatePreview();
                    showNotification('Animation settings reset');
                });

                document.getElementById('reset-advanced').addEventListener('click', () => {
                    svgRevealConfig.animationOrder = defaultConfig.animationOrder;
                    svgRevealConfig.easing = defaultConfig.easing;
                    svgRevealConfig.threshold = defaultConfig.threshold;
                    
                    document.getElementById('order-sequential').checked = true;
                    document.getElementById('order-simultaneous').checked = false;
                    document.getElementById('easing').value = defaultConfig.easing;
                    document.getElementById('threshold').value = defaultConfig.threshold;
                    document.getElementById('threshold-value').textContent = defaultConfig.threshold;
                    
                    updatePreview();
                    showNotification('Advanced settings reset');
                });

                function setupColorInputs(colorType) {
                    const colorInput = document.getElementById(`${colorType}-color`);
                    const hexInput = document.getElementById(`${colorType}-color-hex`);
                    const hslInput = document.getElementById(`${colorType}-color-hsl`);
                    
                    const color = colorType === 'stroke' ? svgRevealConfig.strokeColor : svgRevealConfig.fillColor;
                    hslInput.value = `hsl(${hexToHsl(color).h}, ${hexToHsl(color).s}%, ${hexToHsl(color).l}%)`;
                    
                    colorInput.addEventListener('input', () => {
                        const selectedColor = colorInput.value;
                        hexInput.value = selectedColor;
                        hslInput.value = `hsl(${hexToHsl(selectedColor).h}, ${hexToHsl(selectedColor).s}%, ${hexToHsl(selectedColor).l}%)`;
                        hexInput.classList.remove('invalid');
                        hslInput.classList.remove('invalid');
                        
                        if (colorType === 'stroke') {
                            svgRevealConfig.strokeColor = selectedColor;
                        } else {
                            svgRevealConfig.fillColor = selectedColor;
                        }
                        
                        const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
                        colorPickerContainer.style.setProperty('--selected-color', selectedColor);
                        
                        updatePreview();
                    });
                    
                    hexInput.addEventListener('input', (e) => {
                        let hex = e.target.value;
                        
                        hex = formatHex(hex);
                        e.target.value = hex;
                        
                        if (isValidHex(hex)) {
                            colorInput.value = hex;
                            hslInput.value = `hsl(${hexToHsl(hex).h}, ${hexToHsl(hex).s}%, ${hexToHsl(hex).l}%)`;
                            
                            if (colorType === 'stroke') {
                                svgRevealConfig.strokeColor = hex;
                            } else {
                                svgRevealConfig.fillColor = hex;
                            }
                            
                            e.target.classList.remove('invalid');
                            hslInput.classList.remove('invalid');
                            
                            const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
                            colorPickerContainer.style.setProperty('--selected-color', hex);
                            
                            updatePreview();
                        } else {
                            e.target.classList.add('invalid');
                        }
                    });
                    
                    hexInput.addEventListener('blur', (e) => {
                        if (!isValidHex(e.target.value)) {
                            e.target.value = colorInput.value;
                            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;
                                
                                if (colorType === 'stroke') {
                                    svgRevealConfig.strokeColor = hex;
                                } else {
                                    svgRevealConfig.fillColor = hex;
                                }
                                
                                e.target.classList.remove('invalid');
                                hexInput.classList.remove('invalid');
                                
                                const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
                                colorPickerContainer.style.setProperty('--selected-color', hex);
                                
                                updatePreview();
                                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;
                                    
                                    if (colorType === 'stroke') {
                                        svgRevealConfig.strokeColor = hex;
                                    } else {
                                        svgRevealConfig.fillColor = hex;
                                    }
                                    
                                    e.target.classList.remove('invalid');
                                    hexInput.classList.remove('invalid');
                                    updatePreview();
                                    return;
                                }
                            }
                        }
                        
                        if (!isValidHsl(e.target.value)) {
                            const currentColor = colorType === 'stroke' ? svgRevealConfig.strokeColor : svgRevealConfig.fillColor;
                            e.target.value = `hsl(${hexToHsl(currentColor).h}, ${hexToHsl(currentColor).s}%, ${hexToHsl(currentColor).l}%)`;
                            e.target.classList.remove('invalid');
                        }
                    });
                }

                setupColorInputs('stroke');
                setupColorInputs('fill');

                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 'duration':
                                svgRevealConfig.duration = parseInt(input.value);
                                break;
                            case 'base-delay':
                                svgRevealConfig.baseDelay = parseInt(input.value);
                                break;
                            case 'delay-increment':
                                svgRevealConfig.delayIncrement = parseInt(input.value);
                                break;
                            case 'stroke-width':
                                svgRevealConfig.strokeWidth = parseFloat(input.value);
                                break;
                            case 'threshold':
                                svgRevealConfig.threshold = parseFloat(input.value);
                                break;
                        }
                        
                        updatePreview();
                    });
                });

                document.querySelectorAll('input[name="animation-order"]').forEach(radio => {
                    radio.addEventListener('change', function() {
                        if (this.checked) {
                            svgRevealConfig.animationOrder = this.value;
                            updatePreview();
                        }
                    });
                });

                document.getElementById('easing').addEventListener('change', function() {
                    svgRevealConfig.easing = 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;
                        }
                    }
                });

                updateColorInputs('stroke');
                updateColorInputs('fill');
                
                setTimeout(() => {
                    showNotification('BricksFusion SVG Reveal Configurator loaded!');
                }, 500);

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

                function loadConfiguration() {
                    try {
                        const saved = localStorage.getItem('bricksfusion-svg-reveal-config');
                        if (saved) {
                            const savedConfig = JSON.parse(saved);
                            Object.assign(svgRevealConfig, savedConfig);
                            
                            document.getElementById('duration').value = savedConfig.duration;
                            document.getElementById('base-delay').value = savedConfig.baseDelay;
                            document.getElementById('delay-increment').value = savedConfig.delayIncrement;
                            document.getElementById('stroke-width').value = savedConfig.strokeWidth;
                            document.getElementById('threshold').value = savedConfig.threshold;
                            document.getElementById('easing').value = savedConfig.easing;
                            
                            document.getElementById('duration-value').textContent = savedConfig.duration;
                            document.getElementById('base-delay-value').textContent = savedConfig.baseDelay;
                            document.getElementById('delay-increment-value').textContent = savedConfig.delayIncrement;
                            document.getElementById('stroke-width-value').textContent = savedConfig.strokeWidth;
                            document.getElementById('threshold-value').textContent = savedConfig.threshold;
                            
                            if (savedConfig.animationOrder === 'simultaneous') {
                                document.getElementById('order-simultaneous').checked = true;
                                document.getElementById('order-sequential').checked = false;
                            }
                            
                            updateColorInputs('stroke');
                            updateColorInputs('fill');
                            updatePreview();
                        }
                    } catch (e) {}
                }

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

                loadConfiguration();
            }
            
            initializeUI();
        });
    </script>
</body>
</html>
SVG Reveal - Bricksfusion
LIGHTWEIGHT

SVG Reveal

Animates SVG graphics with a smooth drawing effect that reveals them when scrolled into view. Perfect for logos, icons, illustrations, and decorative elements.

Animation

Duration 500-5000ms

How long it takes to draw each part of the SVG. Shorter is snappier, longer is more dramatic. Sweet spot is usually 1500-2500ms.

Default: 1500ms

Base Delay 0-2000ms

Initial wait time before any drawing starts. Gives visitors a moment to focus before the animation begins.

Default: 0ms (starts immediately)

Delay Increment 0-500ms

When using Sequential order, this is the delay between each path. Higher values create a more staggered, piece-by-piece reveal.

Default: 100ms

Animation Order sequential / simultaneous

Sequential draws paths one after another, creating a flowing reveal. Simultaneous draws everything at once for instant impact.

Default: Sequential

Easing timing function

Animation curve that controls the drawing speed. Ease creates natural motion, Linear is constant speed, Custom lets you fine-tune with cubic-bezier.

Default: Ease (cubic-bezier(0.65, 0, 0.35, 1))

Appearance

Stroke Color color picker

Color of the drawing line as it animates. This is what visitors see being drawn. Choose a color that stands out against your background.

Default: Orange (#ef6013)

Fill Color color picker

Final color that fills the shapes after they're drawn. Only applies to closed shapes like circles, rectangles, and filled paths.

Default: White (#ffffff)

Stroke Width 0.5-5.0

Thickness of the drawing line. Thinner lines are more delicate, thicker lines are bolder and easier to see on large screens.

Default: 1.0

Trigger

Scroll Threshold 0.0-1.0

How much of the SVG needs to be visible before animation starts. 0.5 means halfway into view, 0.2 starts sooner, 0.8 waits until almost fully visible.

Default: 0.5 (50% visible)

Performance

This element uses CSS transitions and stroke-dashoffset animation, making it very efficient. Works with any SVG containing paths, circles, rectangles, lines, polygons, and polylines. The animation only triggers once when scrolled into view, so it won't impact ongoing page performance.