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>Avatar Orbit Configurator - BricksFusion</title>
  <style>
    :root {
  --background: #000;
  --card-bg: #1e1e1e;
  --card-bg-hover: #252525;
  --text-primary: #f2f2f7;
  --text-secondary: #8e8e93;
  --accent: #ef6013;
  --accent-hover: #c64c0c;
  --border: #2c2c2e;
  --shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  --track: #2c2c2e;
  --thumb: #ef6013;
  --card-radius: 16px;
  --input-radius: 8px;
  --button-radius: 12px;
  --transition: all 0.25s ease;
  --font: 'Inter', BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --action-bar-height: 70px;
  --success: #28a745;
  --warning: #ffc107;
  --danger: #dc3545;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.preview-container {
  height: 500px;
  width: 100%;
  position: relative;
  overflow: hidden;
  border-radius: var(--card-radius);
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  display: flex;
  align-items: center;
  justify-content: 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;
  padding: 2rem;
  background: rgba(0, 0, 0, 0.3);
  border-radius: var(--card-radius);
  backdrop-filter: blur(10px);
}

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

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

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

.preview-btn svg {
  width: 18px;
  height: 18px;
  stroke: currentColor;
}

.background-selector-wrapper {
  position: relative;
  display: inline-block;
}

.background-selector-btn {
  position: relative;
}

.background-selector-btn:hover {
  background-color: rgba(239, 96, 19, 0.2);
  border-color: var(--accent);
  box-shadow: 0 0 8px rgba(239, 96, 19, 0.3);
}

.hidden-color-input {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}

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

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

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

#avatar-orbit-container {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  border-radius: var(--card-radius);
}

.avatar-orbit-preview {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}

.orbit-avatar {
  position: absolute;
  border-radius: 50%;
  border: 3px solid #ffffff;
  background-size: cover;
  background-position: center;
  transition: all 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  cursor: pointer;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  will-change: transform;
  z-index: 5;
}

.image-url-group {
  margin-bottom: 1rem;
}

.image-url-input {
  display: flex;
  gap: 0.5rem;
}

.url-input {
  flex-grow: 1;
  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);
  outline: none;
  transition: var(--transition);
}

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

.remove-btn {
  background-color: rgba(255, 59, 48, 0.2);
  color: #ff3b30;
  border: none;
  border-radius: var(--input-radius);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 16px;
  transition: var(--transition);
}

.remove-btn:hover {
  background-color: rgba(255, 59, 48, 0.3);
}

.add-image-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.6rem 1rem;
  background-color: rgba(50, 50, 50, 0.5);
  color: var(--text-primary);
  border: 1px dashed var(--border);
  border-radius: var(--input-radius);
  font-size: var(--text-xs);
  cursor: pointer;
  transition: var(--transition);
  width: 100%;
  margin-top: 0.5rem;
}

.add-image-btn:hover {
  background-color: rgba(80, 80, 80, 0.5);
  border-color: var(--text-secondary);
}

.updating {
  position: relative;
}

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

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

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

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

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

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

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

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

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

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

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

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

@keyframes spin {
  to { transform: rotate(360deg); }
}
  </style>
</head>
<body>
  <div class="action-bar">
    <nav class="breadcrumb">
      <a href="https://bricksfusion.com" class="breadcrumb-item">Home</a>
      <span class="breadcrumb-separator">›</span>
      <a href="https://bricksfusion.com/showcase/" class="breadcrumb-item">Showcase</a>
      <span class="breadcrumb-separator">›</span>
      <span class="breadcrumb-item active">Avatar Orbit</span>
    </nav>
    
    <div class="action-buttons">
      <div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
        data-avatar-orbit
      </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">Avatar Orbit</h1>
      <p class="page-subtitle">Responsive scroll-driven semicircle avatar animation 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 Avatar Orbit 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-avatar-orbit</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="orbit-preview">
          <div class="preview-content">
            <h3>Avatar Orbit Preview</h3>
            <p>Scroll this page to see avatars move around the semicircle</p>
          </div>
          <div class="preview-controls">
            <button class="preview-btn" id="randomize-orbit" title="Randomize (R)">🎲</button>
            <div class="background-selector-wrapper">
              <button class="preview-btn background-selector-btn" id="background-selector">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <polygon points="12,2 2,7 12,12 22,7"/>
                  <polyline points="2,17 12,22 22,17"/>
                  <polyline points="2,12 12,17 22,12"/>
                </svg>
              </button>
              <input type="color" id="preview-background-picker" class="hidden-color-input" value="#667eea" title="Change Preview Background (B)">
            </div>
          </div>
          <div id="avatar-orbit-container" data-avatar-orbit="true"></div>
        </div>
      </section>

      <section class="controls-section">
        <div class="card">
          <div class="card-heading">
            Orbit Settings
            <div class="card-actions">
              <button class="card-action-btn" id="reset-orbit" title="Reset Orbit Settings">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Avatar Size
                  <span class="help-tooltip" title="Size of avatar images in pixels (automatically scales down to 70% on mobile)">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="avatar-size-value">60</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('avatar-size', 60)">↺</button>
                </div>
              </div>
              <input type="range" id="avatar-size" min="40" max="120" step="10" value="60">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Orbit Radius
                  <span class="help-tooltip" title="Radius of the semicircle path (automatically scales down on smaller screens)">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="orbit-radius-value">150</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('orbit-radius', 150)">↺</button>
                </div>
              </div>
              <input type="range" id="orbit-radius" min="80" max="500" step="10" value="150">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Vertical Position (Desktop)
                  <span class="help-tooltip" title="Vertical position of the circle center on desktop (0% = top, 100% = bottom)">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="vertical-position-value">70</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('vertical-position', 70)">↺</button>
                </div>
              </div>
              <input type="range" id="vertical-position" min="20" max="90" step="5" value="70">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Vertical Position (Mobile)
                  <span class="help-tooltip" title="Vertical position of the circle center on mobile devices (0% = top, 100% = bottom)">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="mobile-vertical-position-value">60</span>%</span>
                  <button class="reset-btn" onclick="resetParameter('mobile-vertical-position', 60)">↺</button>
                </div>
              </div>
              <input type="range" id="mobile-vertical-position" min="20" max="90" step="5" value="60">
            </div>
            
            <div class="control-group" id="total-avatars-control">
              <div class="control-label">
                <span class="label-text">
                  Total Avatars
                  <span class="help-tooltip" title="Total number of avatars in the orbit (custom images will repeat to fill this amount)">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="total-avatars-value">8</span></span>
                  <button class="reset-btn" onclick="resetParameter('total-avatars', 8)">↺</button>
                </div>
              </div>
              <input type="range" id="total-avatars" min="6" max="20" step="1" value="8">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Animation Speed
                  <span class="help-tooltip" title="Speed multiplier for scroll-based animation">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="animation-speed-value">1</span>x</span>
                  <button class="reset-btn" onclick="resetParameter('animation-speed', 1)">↺</button>
                </div>
              </div>
              <input type="range" id="animation-speed" min="0.2" max="3" step="0.1" value="1">
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Smoothness
                  <span class="help-tooltip" title="Animation smoothness factor (higher = smoother but more delayed)">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="smoothness-value">0.85</span></span>
                  <button class="reset-btn" onclick="resetParameter('smoothness', 0.85)">↺</button>
                </div>
              </div>
              <input type="range" id="smoothness" min="0.5" max="0.95" step="0.05" value="0.85">
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Appearance
            <div class="card-actions">
              <button class="card-action-btn" id="reset-appearance" title="Reset Appearance Settings">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div class="color-list">
              <div class="color-row">
                <div class="color-picker-container">
                  <input type="color" id="border-color" value="#ffffff">
                </div>
                <div class="color-input-group">
                  <span class="color-label">HEX</span>
                  <input type="text" class="color-input hex-input" id="border-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="border-color-hsl" placeholder="hsl(0, 100%, 50%)">
                </div>
              </div>
            </div>
            
            <div class="control-group">
              <div class="control-label">
                <span class="label-text">
                  Border Width
                  <span class="help-tooltip" title="Width of avatar border in pixels">ℹ</span>
                </span>
                <div class="value-display">
                  <span class="value-text"><span id="border-width-value">3</span>px</span>
                  <button class="reset-btn" onclick="resetParameter('border-width', 3)">↺</button>
                </div>
              </div>
              <input type="range" id="border-width" min="0" max="8" step="1" value="3">
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-heading">
            Avatar Images
            <div class="card-actions">
              <button class="card-action-btn" id="reset-avatars" title="Reset Avatar URLs">↺</button>
            </div>
          </div>
          <div class="card-content">
            <div id="image-urls-container">
              <div class="image-url-group">
                <div class="control-label">
                  <span class="label-text">Avatar 1</span>
                </div>
                <div class="image-url-input">
                  <input type="text" placeholder="https://example.com/avatar1.jpg" class="url-input" data-index="1">
                </div>
              </div>
              
              <div class="image-url-group">
                <div class="control-label">
                  <span class="label-text">Avatar 2</span>
                </div>
                <div class="image-url-input">
                  <input type="text" placeholder="https://example.com/avatar2.jpg" class="url-input" data-index="2">
                </div>
              </div>
              
              <div class="image-url-group">
                <div class="control-label">
                  <span class="label-text">Avatar 3</span>
                </div>
                <div class="image-url-input">
                  <input type="text" placeholder="https://example.com/avatar3.jpg" class="url-input" data-index="3">
                </div>
              </div>
            </div>
            
            <button class="add-image-btn" id="add-image-btn">
              <span>+</span> Add Another Avatar
            </button>
          </div>
        </div>
      </section>
    </div>
  </div>

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

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      let orbitConfig = {
        avatarSize: 60,
        orbitRadius: 150,
        verticalPosition: 70,
        mobileVerticalPosition: 60,
        totalAvatars: 8,
        animationSpeed: 1,
        smoothness: 0.85,
        borderColor: '#ffffff',
        borderWidth: 3,
        imageUrls: [],
        defaultAvatars: [
          { src: "https://i.pravatar.cc/150?img=1", alt: "Avatar 1" },
          { src: "https://i.pravatar.cc/150?img=2", alt: "Avatar 2" },
          { src: "https://i.pravatar.cc/150?img=3", alt: "Avatar 3" },
          { src: "https://i.pravatar.cc/150?img=4", alt: "Avatar 4" },
          { src: "https://i.pravatar.cc/150?img=5", alt: "Avatar 5" },
          { src: "https://i.pravatar.cc/150?img=6", alt: "Avatar 6" }
        ]
      };

      const defaultConfig = { ...orbitConfig };
      let activeOrbit = null;
      
      // Math utilities for smooth interpolation
      const lerp = (start, end, factor) => start + (end - start) * factor;
      const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
      const easeInOutQuart = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
      
      // Responsive utility functions
      function getScaleFactor() {
        const width = window.innerWidth;
        if (width <= 480) return 0.5;      // Mobile small
        if (width <= 768) return 0.65;     // Mobile/tablet
        if (width <= 1024) return 0.8;     // Tablet landscape
        return 1.0;                        // Desktop
      }
      
      function getAvatarScaleFactor() {
        const width = window.innerWidth;
        if (width <= 768) return 0.7;      // Mobile: 70% of original size
        return 1.0;                        // Desktop: full size
      }
      
      function getVerticalPosition(desktopValue, mobileValue) {
        const width = window.innerWidth;
        if (width <= 768) return mobileValue;
        return desktopValue;
      }
      
      // Enhanced AvatarOrbit class with responsive scaling
      class AvatarOrbit {
        constructor(container, options = {}) {
          this.container = container;
          this.preview = container.querySelector('.avatar-orbit-preview');
          
          this.options = {
            avatarSize: options.avatarSize || 60,
            orbitRadius: options.orbitRadius || 150,
            verticalPosition: options.verticalPosition || 70,
            mobileVerticalPosition: options.mobileVerticalPosition || 60,
            totalAvatars: options.totalAvatars || 8,
            animationSpeed: options.animationSpeed || 1,
            smoothness: options.smoothness || 0.85,
            borderColor: options.borderColor || '#ffffff',
            borderWidth: options.borderWidth || 3,
            imageUrls: options.imageUrls || [],
            ...options
          };
          
          this.avatars = [];
          this.scrollY = 0;
          this.targetScrollY = 0;
          this.scrollVelocity = 0;
          this.lastScrollY = 0;
          this.isVisible = true;
          this.isUpdating = false;
          this.animationId = null;
          
          // Performance tracking
          this.lastTime = performance.now();
          this.fps = 60;
          
          this.init();
        }
        
        updateConfig(newOptions, updateType = 'full') {
          if (this.isUpdating) return;
          this.isUpdating = true;
          
          this.container.classList.add('updating');
          setTimeout(() => this.container.classList.remove('updating'), 600);
          
          const prevOptions = { ...this.options };
          Object.assign(this.options, newOptions);
          
          if (updateType === 'appearance' || this.optionsChanged(prevOptions, ['borderColor', 'borderWidth'])) {
            this.updateAvatarStyles();
          }
          
          if (updateType === 'size' || this.optionsChanged(prevOptions, ['avatarSize', 'orbitRadius', 'verticalPosition', 'mobileVerticalPosition'])) {
            this.updateAvatarSize();
            this.updatePositions();
          }
          
          if (updateType === 'content' || this.optionsChanged(prevOptions, ['imageUrls', 'totalAvatars'])) {
            this.updateContent();
          }
          
          this.isUpdating = false;
        }
        
        optionsChanged(prevOptions, keys) {
          return keys.some(key => 
            JSON.stringify(prevOptions[key]) !== JSON.stringify(this.options[key])
          );
        }
        
        updateAvatarStyles() {
          this.avatars.forEach(avatar => {
            avatar.style.borderColor = this.options.borderColor;
            avatar.style.borderWidth = `${this.options.borderWidth}px`;
          });
        }
        
        updateAvatarSize() {
          const avatarScaleFactor = getAvatarScaleFactor();
          const scaledSize = this.options.avatarSize * avatarScaleFactor;
          
          this.avatars.forEach(avatar => {
            avatar.style.width = `${scaledSize}px`;
            avatar.style.height = `${scaledSize}px`;
          });
        }
        
        updateContent() {
          this.preview.innerHTML = '';
          this.avatars = [];
          this.createAvatars();
          this.updatePositions();
        }
        
        init() {
          this.createAvatars();
          this.setupScrollListener();
          this.setupIntersectionObserver();
          this.setupResizeListener();
          this.startAnimationLoop();
          this.updatePositions();
        }
        
        createAvatars() {
          const hasCustomImages = this.options.imageUrls?.length > 0;
          let baseAvatars, totalAvatarsToShow;
          
          if (hasCustomImages) {
            baseAvatars = this.options.imageUrls;
            totalAvatarsToShow = this.options.totalAvatars;
          } else {
            baseAvatars = this.getDefaultAvatars();
            totalAvatarsToShow = this.options.totalAvatars;
          }
          
          const repeatedAvatars = this.repeatAvatarsToFill(baseAvatars, totalAvatarsToShow);
          
          // Calculate responsive sizes
          const avatarScaleFactor = getAvatarScaleFactor();
          const scaledSize = this.options.avatarSize * avatarScaleFactor;
          
          repeatedAvatars.forEach((avatar, index) => {
            const avatarEl = document.createElement('div');
            avatarEl.className = 'orbit-avatar';
            avatarEl.style.width = `${scaledSize}px`;
            avatarEl.style.height = `${scaledSize}px`;
            avatarEl.style.borderColor = this.options.borderColor;
            avatarEl.style.borderWidth = `${this.options.borderWidth}px`;
            avatarEl.style.backgroundImage = `url(${avatar.src})`;
            avatarEl.setAttribute('data-index', index);
            
            // Store initial transform values for smooth interpolation
            avatarEl._currentX = 0;
            avatarEl._currentY = 0;
            avatarEl._targetX = 0;
            avatarEl._targetY = 0;
            avatarEl._currentOpacity = 1;
            avatarEl._targetOpacity = 1;
            
            // Enhanced hover effects with momentum
            avatarEl.addEventListener('mouseenter', () => {
              avatarEl.style.transform = `translate3d(${avatarEl._currentX}px, ${avatarEl._currentY}px, 0) scale(1.1)`;
              avatarEl.style.boxShadow = '0 8px 25px rgba(0, 0, 0, 0.4)';
              avatarEl.style.zIndex = '100';
            });
            
            avatarEl.addEventListener('mouseleave', () => {
              avatarEl.style.transform = `translate3d(${avatarEl._currentX}px, ${avatarEl._currentY}px, 0) scale(1)`;
              avatarEl.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.3)';
              avatarEl.style.zIndex = '5';
            });
            
            this.preview.appendChild(avatarEl);
            this.avatars.push(avatarEl);
          });
        }
        
        repeatAvatarsToFill(baseAvatars, totalNeeded) {
          const result = [];
          for (let i = 0; i < totalNeeded; i++) {
            const sourceIndex = i % baseAvatars.length;
            result.push(baseAvatars[sourceIndex]);
          }
          return result;
        }
        
        getDefaultAvatars() {
          return [
            { src: "https://i.pravatar.cc/150?img=1", alt: "Avatar 1" },
            { src: "https://i.pravatar.cc/150?img=2", alt: "Avatar 2" },
            { src: "https://i.pravatar.cc/150?img=3", alt: "Avatar 3" },
            { src: "https://i.pravatar.cc/150?img=4", alt: "Avatar 4" },
            { src: "https://i.pravatar.cc/150?img=5", alt: "Avatar 5" },
            { src: "https://i.pravatar.cc/150?img=6", alt: "Avatar 6" }
          ];
        }
        
        setupScrollListener() {
          const updateScroll = () => {
            const currentY = window.pageYOffset;
            
            // Calculate velocity for momentum effect
            this.scrollVelocity = (currentY - this.lastScrollY) * 0.5;
            this.lastScrollY = currentY;
            this.targetScrollY = currentY;
          };
          
          // Use passive listener for better performance
          window.addEventListener('scroll', updateScroll, { passive: true });
          this.scrollHandler = updateScroll;
        }
        
        setupIntersectionObserver() {
          const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
              this.isVisible = entry.isIntersecting;
            });
          }, {
            threshold: 0.1,
            rootMargin: '50px'
          });
          
          observer.observe(this.container);
          this.intersectionObserver = observer;
        }
        
        setupResizeListener() {
          let resizeTimer = null;
          
          const handleResize = () => {
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(() => {
              this.updateAvatarSize();
              this.updatePositions();
            }, 100);
          };
          
          window.addEventListener('resize', handleResize, { passive: true });
          this.resizeHandler = handleResize;
        }
        
        startAnimationLoop() {
          const animate = (currentTime) => {
            if (!this.isVisible) {
              this.animationId = requestAnimationFrame(animate);
              return;
            }
            
            // Calculate delta time for frame rate independent animation
            const deltaTime = currentTime - this.lastTime;
            this.lastTime = currentTime;
            
            // Smooth scroll interpolation with momentum
            const smoothness = this.options.smoothness;
            this.scrollY = lerp(this.scrollY, this.targetScrollY + this.scrollVelocity, 1 - smoothness);
            
            // Decay velocity for natural feel
            this.scrollVelocity *= 0.95;
            
            this.updatePositions();
            
            this.animationId = requestAnimationFrame(animate);
          };
          
          this.animationId = requestAnimationFrame(animate);
        }
        
        updatePositions() {
          if (!this.isVisible || this.avatars.length === 0) return;
          
          const containerRect = this.container.getBoundingClientRect();
          
          // Get responsive values
          const scaleFactor = getScaleFactor();
          const avatarScaleFactor = getAvatarScaleFactor();
          const verticalPos = getVerticalPosition(this.options.verticalPosition, this.options.mobileVerticalPosition);
          
          // Center positioning
          const centerX = containerRect.width / 2;
          const centerY = containerRect.height * (verticalPos / 100);
          
          const orbitRadius = this.options.orbitRadius * scaleFactor;
          const avatarSize = this.options.avatarSize * avatarScaleFactor;
          
          // Enhanced scroll progress with easing
          const rawScrollProgress = this.scrollY * this.options.animationSpeed * 0.0008;
          const scrollProgress = rawScrollProgress;
          
          const totalAvatars = this.avatars.length;
          let spacingRadians, startAngle;
          
          if (totalAvatars > 1) {
            spacingRadians = (2 * Math.PI) / totalAvatars;
            startAngle = Math.PI / 2;
          } else {
            spacingRadians = 0;
            startAngle = Math.PI / 2;
          }
          
          this.avatars.forEach((avatar, index) => {
            const angle = startAngle + (spacingRadians * index) + scrollProgress;
            
            // Calculate target positions
            const targetX = centerX + Math.cos(angle) * orbitRadius - (avatarSize / 2);
            const targetY = centerY - Math.sin(angle) * orbitRadius - (avatarSize / 2);
            
            // Smooth interpolation to target positions
            avatar._currentX = lerp(avatar._currentX, targetX, 0.15);
            avatar._currentY = lerp(avatar._currentY, targetY, 0.15);
            
            // Calculate visibility with smooth fading
            const isInBounds = avatar._currentY > -avatarSize && 
                             avatar._currentY < containerRect.height && 
                             avatar._currentX > -avatarSize && 
                             avatar._currentX < containerRect.width;
            
            const targetOpacity = isInBounds ? 1 : 0;
            avatar._currentOpacity = lerp(avatar._currentOpacity, targetOpacity, 0.1);
            
            // Apply transformations
            avatar.style.transform = `translate3d(${avatar._currentX}px, ${avatar._currentY}px, 0)`;
            avatar.style.opacity = avatar._currentOpacity;
            avatar.style.pointerEvents = avatar._currentOpacity > 0.1 ? 'auto' : 'none';
          });
        }
        
        destroy() {
          if (this.animationId) {
            cancelAnimationFrame(this.animationId);
          }
          
          if (this.scrollHandler) {
            window.removeEventListener('scroll', this.scrollHandler);
          }
          
          if (this.resizeHandler) {
            window.removeEventListener('resize', this.resizeHandler);
          }
          
          if (this.intersectionObserver) {
            this.intersectionObserver.disconnect();
          }
          
          this.avatars = [];
        }
      }
      
      function updateTotalAvatarsVisibility() {
        const totalAvatarsControl = document.getElementById('total-avatars-control');
        if (totalAvatarsControl) {
          totalAvatarsControl.style.display = 'block';
          
          const hasCustomImages = orbitConfig.imageUrls.length > 0;
          const tooltip = totalAvatarsControl.querySelector('.help-tooltip');
          if (tooltip && hasCustomImages) {
            tooltip.title = 'Total number of avatars in the orbit (custom images will repeat to fill this amount)';
          } else if (tooltip) {
            tooltip.title = 'Total number of avatars in the orbit';
          }
        }
      }
      
      function initAvatarOrbit() {
        const container = document.getElementById('avatar-orbit-container');
        if (!container) return;
        
        if (activeOrbit) {
          activeOrbit.destroy();
          activeOrbit = null;
        }
        
        container.innerHTML = '';
        
        const preview = document.createElement('div');
        preview.className = 'avatar-orbit-preview';
        container.appendChild(preview);
        
        setTimeout(() => {
          activeOrbit = new AvatarOrbit(container, {
            avatarSize: orbitConfig.avatarSize,
            orbitRadius: orbitConfig.orbitRadius,
            verticalPosition: orbitConfig.verticalPosition,
            mobileVerticalPosition: orbitConfig.mobileVerticalPosition,
            totalAvatars: orbitConfig.totalAvatars,
            animationSpeed: orbitConfig.animationSpeed,
            smoothness: orbitConfig.smoothness,
            borderColor: orbitConfig.borderColor,
            borderWidth: orbitConfig.borderWidth,
            imageUrls: orbitConfig.imageUrls
          });
        }, 50);
      }
      
      function updateOrbitPreview(updateType = 'full') {
        if (activeOrbit && activeOrbit.updateConfig) {
          const newOptions = {
            avatarSize: orbitConfig.avatarSize,
            orbitRadius: orbitConfig.orbitRadius,
            verticalPosition: orbitConfig.verticalPosition,
            mobileVerticalPosition: orbitConfig.mobileVerticalPosition,
            totalAvatars: orbitConfig.totalAvatars,
            animationSpeed: orbitConfig.animationSpeed,
            smoothness: orbitConfig.smoothness,
            borderColor: orbitConfig.borderColor,
            borderWidth: orbitConfig.borderWidth,
            imageUrls: orbitConfig.imageUrls
          };
          
          activeOrbit.updateConfig(newOptions, updateType);
        } else {
          initAvatarOrbit();
        }
      }

      function hexToRgb(hex) {
        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
          r: parseInt(result[1], 16),
          g: parseInt(result[2], 16),
          b: parseInt(result[3], 16)
        } : null;
      }

      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 generateRandomColor() {
        return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
      }

      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() {
  const sectionId = generateUniqueId();
  const containerId = generateUniqueId();
  const divMainId = generateUniqueId();
  const headingId = generateUniqueId();
  const divContainerId = generateUniqueId();
  const divImageContainerId = generateUniqueId();
  const imageId = generateUniqueId();
  const divTextContainerId = generateUniqueId();
  const textBasicId = generateUniqueId();
  const divStatusId = generateUniqueId();
  const textStatusId = generateUniqueId();
  const codeStatusId = generateUniqueId();
  const codeMainId = generateUniqueId();
  const attributeMainId = generateUniqueId();
  const attributeStatusId = generateUniqueId();
  
  const jsCode = generateJavaScriptCode();
  
  const bricksJSON = {
    "content": [
      {
        "id": sectionId,
        "name": "section",
        "parent": 0,
        "children": [containerId, codeMainId],
        "settings": {
          "_padding": {
            "top": "100",
            "right": "20",
            "bottom": "100",
            "left": "20"
          },
          "_padding:mobile_landscape": {
            "top": "50",
            "bottom": "50"
          },
          "_justifyContent": "center",
          "_attributes": [
            {
              "id": attributeMainId,
              "name": "data-avatar-orbit"
            }
          ],
          "_height": "450",
          "_height:mobile_landscape": "300"
        },
        "label": "Loomia 003"
      },
      {
        "id": containerId,
        "name": "container",
        "parent": sectionId,
        "children": [divMainId],
        "settings": {
          "_alignItems": "center"
        }
      },
      {
        "id": divMainId,
        "name": "div",
        "parent": containerId,
        "children": [headingId, divContainerId],
        "settings": {
          "_alignItems": "center",
          "_display": "flex",
          "_direction": "column",
          "_rowGap": "30",
          "_zIndex": "999",
          "_width": "50%",
          "_width:tablet_portrait": "70%",
          "_width:mobile_landscape": "100%"
        }
      },
      {
        "id": headingId,
        "name": "heading",
        "parent": divMainId,
        "children": [],
        "settings": {
          "text": "Design trusted by creators worldwide.",
          "tag": "h2",
          "_typography": {
            "color": {
              "hex": "#1e40af"
            },
            "font-size": "50",
            "font-weight": "600",
            "line-height": "1.2",
            "text-align": "center"
          },
          "_typography:tablet_portrait": {
            "font-size": "40"
          },
          "_typography:mobile_landscape": {
            "font-size": "30"
          }
        }
      },
      {
        "id": divContainerId,
        "name": "div",
        "parent": divMainId,
        "children": [divImageContainerId, divTextContainerId],
        "settings": {
          "_display": "flex",
          "_padding": {
            "top": "5",
            "right": "10",
            "bottom": "5",
            "left": "10"
          },
          "_border": {
            "radius": {
              "top": "20",
              "right": "20",
              "bottom": "20",
              "left": "20"
            }
          },
          "_columnGap": "10",
          "_order:mobile_portrait": "1"
        }
      },
      {
        "id": divImageContainerId,
        "name": "div",
        "parent": divContainerId,
        "children": [imageId],
        "settings": {
          "_alignSelf": "center"
        }
      },
      {
        "id": imageId,
        "name": "image",
        "parent": divImageContainerId,
        "children": [],
        "settings": {
          "image": {
            "id": 2241,
            "filename": "smiling-girl-with-cap.png",
            "size": "thumbnail",
            "full": "https://library.bricksfusion.com/wp-content/uploads/2025/09/smiling-girl-with-cap.png",
            "url": "https://library.bricksfusion.com/wp-content/uploads/2025/09/smiling-girl-with-cap-150x150.png"
          },
          "_border": {
            "radius": {
              "top": "50",
              "right": "50",
              "bottom": "50",
              "left": "50"
            }
          },
          "_objectFit": "cover",
          "_width": "40"
        }
      },
      {
        "id": divTextContainerId,
        "name": "div",
        "parent": divContainerId,
        "children": [textBasicId, divStatusId],
        "settings": {
          "_display": "flex",
          "_direction": "column"
        }
      },
      {
        "id": textBasicId,
        "name": "text-basic",
        "parent": divTextContainerId,
        "children": [],
        "settings": {
          "text": "Book a 15-min intro call",
          "tag": "span",
          "_typography": {
            "font-size": "13",
            "font-weight": "600",
            "color": {
              "hex": "#000000"
            }
          }
        }
      },
      {
        "id": divStatusId,
        "name": "div",
        "parent": divTextContainerId,
        "children": [textStatusId, codeStatusId],
        "settings": {
          "_display": "flex",
          "_direction": "row",
          "_attributes": [
            {
              "id": attributeStatusId,
              "name": "data-pulse-dot"
            }
          ]
        },
        "label": "Status Pulse"
      },
      {
        "id": textStatusId,
        "name": "text-basic",
        "parent": divStatusId,
        "children": [],
        "settings": {
          "text": "Available now",
          "tag": "span",
          "_typography": {
            "font-size": "12",
            "font-weight": "500",
            "color": {
              "hex": "#707070"
            }
          }
        }
      },
      {
        "id": codeStatusId,
        "name": "code",
        "parent": divStatusId,
        "children": [],
        "settings": {
          "executeCode": true,
          "javascriptCode": "function createPulseDot() {\n    const elements = document.querySelectorAll('[data-pulse-dot]:not([data-pulse-initialized])');\n    \n    elements.forEach(element => {\n        element.setAttribute('data-pulse-initialized', 'true');\n        \n        // Get custom attributes or use defaults\n        const size = element.getAttribute('data-pulse-size') || '7';\n        const color = element.getAttribute('data-pulse-color') || '#22c55e';\n        const speed = element.getAttribute('data-pulse-speed') || '1.5';\n        const scale = element.getAttribute('data-pulse-scale') || '2';\n        const opacityRaw = element.getAttribute('data-pulse-opacity') || '100';\n        const opacity = parseInt(opacityRaw) / 100;\n        const spacing = element.getAttribute('data-pulse-spacing') || '8';\n        const shadowEnabled = element.getAttribute('data-pulse-shadow-enabled') !== null ? \n            element.getAttribute('data-pulse-shadow-enabled') === 'true' : true;\n        const shadowBlur = element.getAttribute('data-pulse-shadow-blur') || '10';\n        const animationStyle = element.getAttribute('data-pulse-animation-style') || 'fade';\n        const timingFunction = element.getAttribute('data-pulse-timing-function') || 'cubic-bezier(0.4, 0, 0.6, 1)';\n        const pauseOnHover = element.getAttribute('data-pulse-pause-hover') !== null ? \n            element.getAttribute('data-pulse-pause-hover') === 'true' : false;\n        \n        // Create dot element\n        const dot = document.createElement('span');\n        dot.className = 'status-pulse-dot';\n        dot.style.cssText = `\n            position: relative;\n            display: inline-block;\n            width: ${size}px;\n            height: ${size}px;\n            background: ${color};\n            border-radius: 50%;\n            margin-right: ${spacing}px;\n            vertical-align: middle;\n            opacity: ${opacity};\n            ${shadowEnabled ? `box-shadow: 0 0 ${shadowBlur}px ${color};` : ''}\n        `;\n        \n        // Generate unique animation name\n        const animationId = 'pulse_' + Math.random().toString(36).substr(2, 9);\n        \n        // Create animations based on style\n        let keyframes = '';\n        if (animationStyle === 'fade') {\n            keyframes = `\n                @keyframes ${animationId} {\n                    0% { transform: scale(1); opacity: ${opacity}; }\n                    100% { transform: scale(${scale}); opacity: 0; }\n                }\n            `;\n            const pulse = document.createElement('span');\n            pulse.style.cssText = `\n                position: absolute;\n                left: 0;\n                top: 0;\n                width: 100%;\n                height: 100%;\n                background: inherit;\n                border-radius: inherit;\n                animation: ${animationId} ${speed}s ${timingFunction} infinite;\n            `;\n            dot.appendChild(pulse);\n        } else if (animationStyle === 'grow') {\n            keyframes = `\n                @keyframes ${animationId} {\n                    0%, 100% { transform: scale(1); }\n                    50% { transform: scale(${scale}); }\n                }\n            `;\n            dot.style.animation = `${animationId} ${speed}s ${timingFunction} infinite`;\n        } else if (animationStyle === 'both') {\n            keyframes = `\n                @keyframes ${animationId} {\n                    0% { transform: scale(1); opacity: ${opacity}; }\n                    50% { transform: scale(${scale}); opacity: ${opacity / 2}; }\n                    100% { transform: scale(1); opacity: ${opacity}; }\n                }\n            `;\n            const pulse = document.createElement('span');\n            pulse.style.cssText = `\n                position: absolute;\n                left: 0;\n                top: 0;\n                width: 100%;\n                height: 100%;\n                background: inherit;\n                border-radius: inherit;\n                animation: ${animationId} ${speed}s ${timingFunction} infinite;\n            `;\n            dot.appendChild(pulse);\n        } else if (animationStyle === 'double') {\n            keyframes = `\n                @keyframes ${animationId} {\n                    0% { transform: scale(1); opacity: ${opacity}; }\n                    100% { transform: scale(${scale}); opacity: 0; }\n                }\n            `;\n            for (let i = 0; i < 2; i++) {\n                const pulse = document.createElement('span');\n                pulse.style.cssText = `\n                    position: absolute;\n                    left: 0;\n                    top: 0;\n                    width: 100%;\n                    height: 100%;\n                    background: inherit;\n                    border-radius: inherit;\n                    animation: ${animationId} ${speed}s ${timingFunction} infinite;\n                    ${i === 1 ? `animation-delay: ${parseFloat(speed) / 2}s;` : ''}\n                `;\n                dot.appendChild(pulse);\n            }\n        }\n        \n        // Inject keyframes\n        if (keyframes) {\n            const style = document.createElement('style');\n            style.textContent = keyframes;\n            document.head.appendChild(style);\n        }\n        \n        // Add pause on hover functionality\n        if (pauseOnHover) {\n            element.addEventListener('mouseenter', () => {\n                dot.style.animationPlayState = 'paused';\n                dot.querySelectorAll('span').forEach(span => {\n                    span.style.animationPlayState = 'paused';\n                });\n            });\n            \n            element.addEventListener('mouseleave', () => {\n                dot.style.animationPlayState = 'running';\n                dot.querySelectorAll('span').forEach(span => {\n                    span.style.animationPlayState = 'running';\n                });\n            });\n        }\n        \n        // Insert dot and setup element display\n        element.insertBefore(dot, element.firstChild);\n        element.style.display = 'inline-flex';\n        element.style.alignItems = 'center';\n    });\n}\n\n// Initialize on DOM ready or immediately if already loaded\nif (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', createPulseDot);\n} else {\n    createPulseDot();\n}\n\n// Also run on dynamic content changes (optional)\nconst observer = new MutationObserver(() => {\n    createPulseDot();\n});\n\nobserver.observe(document.body, {\n    childList: true,\n    subtree: true\n});",
          "_display": "none"
        },
        "label": "Status pulse JS",
        "themeStyles": []
      },
      {
        "id": codeMainId,
        "name": "code",
        "parent": sectionId,
        "children": [],
        "settings": {
          "_display": "none",
          "executeCode": true,
          "javascriptCode": jsCode
        },
        "label": "Avatar Orbit JS",
        "themeStyles": []
      }
    ],
    "source": "bricksCopiedElements",
    "sourceUrl": "https://library.bricksfusion.com",
    "version": "2.0.1",
    "globalClasses": [],
    "globalElements": []
  };
  
  return JSON.stringify(bricksJSON, null, 2);
}

      function generateJavaScriptCode() {
        const validUrls = orbitConfig.imageUrls.filter(avatar => avatar.src.trim() !== '');
        
        const avatarUrlsCode = validUrls.map((avatar, index) => 
          `              c.setAttribute('data-avatar-${index + 1}', '${avatar.src}');`
        ).join('\n');

        return `(function(){
  const style = document.createElement('style');
  style.textContent = \`
    .avatar-orbit-overlay {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
      z-index: 10;
      overflow: hidden;
    }
    
    .avatar-orbit-preview {
      width: 100%;
      height: 100%;
      position: relative;
      overflow: hidden;
    }
    
    .orbit-avatar {
      position: absolute;
      border-radius: 50%;
      border: ${orbitConfig.borderWidth}px solid ${orbitConfig.borderColor};
      background-size: cover;
      background-position: center;
      transition: all 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94);
      cursor: pointer;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
      pointer-events: auto;
      z-index: 5;
    }
    
    [data-avatar-orbit] {
      position: relative;
    }
  \`;
  document.head.appendChild(style);
  
  // Math utilities for smooth interpolation
  const lerp = (start, end, factor) => start + (end - start) * factor;
  
  // Responsive utility functions
  function getScaleFactor() {
    const width = window.innerWidth;
    if (width <= 480) return 0.5;      // Mobile small
    if (width <= 768) return 0.65;     // Mobile/tablet
    if (width <= 1024) return 0.8;     // Tablet landscape
    return 1.0;                        // Desktop
  }
  
  function getAvatarScaleFactor() {
    const width = window.innerWidth;
    if (width <= 768) return 0.7;      // Mobile: 70% of original size
    return 1.0;                        // Desktop: full size
  }
  
  function getVerticalPosition(desktopValue, mobileValue) {
    const width = window.innerWidth;
    if (width <= 768) return mobileValue;
    return desktopValue;
  }
  
  class AvatarOrbit {
    constructor(container, options = {}) {
      this.container = container;
      
      this.options = {
        avatarSize: options.avatarSize || ${orbitConfig.avatarSize},
        orbitRadius: options.orbitRadius || ${orbitConfig.orbitRadius},
        verticalPosition: options.verticalPosition || ${orbitConfig.verticalPosition},
        mobileVerticalPosition: options.mobileVerticalPosition || ${orbitConfig.mobileVerticalPosition},
        totalAvatars: options.totalAvatars || ${orbitConfig.totalAvatars},
        animationSpeed: options.animationSpeed || ${orbitConfig.animationSpeed},
        smoothness: options.smoothness || ${orbitConfig.smoothness},
        borderColor: options.borderColor || '${orbitConfig.borderColor}',
        borderWidth: options.borderWidth || ${orbitConfig.borderWidth},
        ...options
      };
      
      this.avatars = [];
      this.scrollY = 0;
      this.targetScrollY = 0;
      this.scrollVelocity = 0;
      this.lastScrollY = 0;
      this.isVisible = true;
      this.animationId = null;
      this.lastTime = performance.now();
      
      this.init();
    }
    
    init() {
      this.container.style.position = 'relative';
      
      const existingOverlay = this.container.querySelector('.avatar-orbit-overlay');
      if (existingOverlay) {
        existingOverlay.remove();
      }
      
      this.overlay = document.createElement('div');
      this.overlay.className = 'avatar-orbit-overlay';
      
      this.preview = document.createElement('div');
      this.preview.className = 'avatar-orbit-preview';
      this.overlay.appendChild(this.preview);
      
      this.container.appendChild(this.overlay);
      
      this.createAvatars();
      this.setupScrollListener();
      this.setupIntersectionObserver();
      this.setupResizeListener();
      this.startAnimationLoop();
      this.updatePositions();
    }
    
    createAvatars() {
      let avatarUrls = [];
      for(let i = 1; i <= 10; i++) {
        const src = this.container.getAttribute(\`data-avatar-\${i}\`);
        if(src) {
          avatarUrls.push({ src: src, alt: \`Avatar \${i}\` });
        }
      }
      
      const hasCustomImages = avatarUrls.length > 0;
      let baseAvatars, totalAvatarsToShow;
      
      if (hasCustomImages) {
        baseAvatars = avatarUrls;
        totalAvatarsToShow = this.options.totalAvatars;
      } else {
        baseAvatars = [
          { src: "https://i.pravatar.cc/150?img=1", alt: "Avatar 1" },
          { src: "https://i.pravatar.cc/150?img=2", alt: "Avatar 2" },
          { src: "https://i.pravatar.cc/150?img=3", alt: "Avatar 3" },
          { src: "https://i.pravatar.cc/150?img=4", alt: "Avatar 4" },
          { src: "https://i.pravatar.cc/150?img=5", alt: "Avatar 5" },
          { src: "https://i.pravatar.cc/150?img=6", alt: "Avatar 6" }
        ];
        totalAvatarsToShow = this.options.totalAvatars;
      }
      
      const repeatedAvatars = [];
      for (let i = 0; i < totalAvatarsToShow; i++) {
        const sourceIndex = i % baseAvatars.length;
        repeatedAvatars.push(baseAvatars[sourceIndex]);
      }
      
      // Calculate responsive sizes
      const avatarScaleFactor = getAvatarScaleFactor();
      const scaledSize = this.options.avatarSize * avatarScaleFactor;
      
      repeatedAvatars.forEach((avatar, index) => {
        const avatarEl = document.createElement('div');
        avatarEl.className = 'orbit-avatar';
        avatarEl.style.width = \`\${scaledSize}px\`;
        avatarEl.style.height = \`\${scaledSize}px\`;
        avatarEl.style.backgroundImage = \`url(\${avatar.src})\`;
        avatarEl.setAttribute('data-index', index);
        
        // Store current positions for smooth interpolation
        avatarEl._currentX = 0;
        avatarEl._currentY = 0;
        avatarEl._currentOpacity = 1;
        
        avatarEl.addEventListener('mouseenter', () => {
          avatarEl.style.transform = \`translate3d(\${avatarEl._currentX}px, \${avatarEl._currentY}px, 0) scale(1.1)\`;
          avatarEl.style.boxShadow = '0 8px 25px rgba(0, 0, 0, 0.4)';
          avatarEl.style.zIndex = '100';
        });
        
        avatarEl.addEventListener('mouseleave', () => {
          avatarEl.style.transform = \`translate3d(\${avatarEl._currentX}px, \${avatarEl._currentY}px, 0) scale(1)\`;
          avatarEl.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.3)';
          avatarEl.style.zIndex = '5';
        });
        
        this.preview.appendChild(avatarEl);
        this.avatars.push(avatarEl);
      });
    }
    
    setupScrollListener() {
      const updateScroll = () => {
        const currentY = window.pageYOffset;
        this.scrollVelocity = (currentY - this.lastScrollY) * 0.5;
        this.lastScrollY = currentY;
        this.targetScrollY = currentY;
      };
      
      window.addEventListener('scroll', updateScroll, { passive: true });
      this.scrollHandler = updateScroll;
    }
    
    setupIntersectionObserver() {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          this.isVisible = entry.isIntersecting;
        });
      }, {
        threshold: 0.1,
        rootMargin: '50px'
      });
      
      observer.observe(this.container);
      this.intersectionObserver = observer;
    }
    
    setupResizeListener() {
      let resizeTimer = null;
      
      const handleResize = () => {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(() => {
          this.updateAvatarSize();
          this.updatePositions();
        }, 100);
      };
      
      window.addEventListener('resize', handleResize, { passive: true });
      this.resizeHandler = handleResize;
    }
    
    updateAvatarSize() {
      const avatarScaleFactor = getAvatarScaleFactor();
      const scaledSize = this.options.avatarSize * avatarScaleFactor;
      
      this.avatars.forEach(avatar => {
        avatar.style.width = \`\${scaledSize}px\`;
        avatar.style.height = \`\${scaledSize}px\`;
      });
    }
    
    startAnimationLoop() {
      const animate = (currentTime) => {
        if (!this.isVisible) {
          this.animationId = requestAnimationFrame(animate);
          return;
        }
        
        const deltaTime = currentTime - this.lastTime;
        this.lastTime = currentTime;
        
        // Smooth scroll interpolation
        const smoothness = this.options.smoothness;
        this.scrollY = lerp(this.scrollY, this.targetScrollY + this.scrollVelocity, 1 - smoothness);
        this.scrollVelocity *= 0.95;
        
        this.updatePositions();
        this.animationId = requestAnimationFrame(animate);
      };
      
      this.animationId = requestAnimationFrame(animate);
    }
    
    updatePositions() {
      if (!this.isVisible || this.avatars.length === 0) return;
      
      const containerRect = this.container.getBoundingClientRect();
      
      // Get responsive values
      const scaleFactor = getScaleFactor();
      const avatarScaleFactor = getAvatarScaleFactor();
      const verticalPos = getVerticalPosition(this.options.verticalPosition, this.options.mobileVerticalPosition);
      
      const centerX = containerRect.width / 2;
      const centerY = containerRect.height * (verticalPos / 100);
      
      const orbitRadius = this.options.orbitRadius * scaleFactor;
      const avatarSize = this.options.avatarSize * avatarScaleFactor;
      
      const scrollProgress = this.scrollY * this.options.animationSpeed * 0.0008;
      
      const totalAvatars = this.avatars.length;
      let spacingRadians, startAngle;
      
      if (totalAvatars > 1) {
        spacingRadians = (2 * Math.PI) / totalAvatars;
        startAngle = Math.PI / 2;
      } else {
        spacingRadians = 0;
        startAngle = Math.PI / 2;
      }
      
      this.avatars.forEach((avatar, index) => {
        const angle = startAngle + (spacingRadians * index) + scrollProgress;
        
        const targetX = centerX + Math.cos(angle) * orbitRadius - (avatarSize / 2);
        const targetY = centerY - Math.sin(angle) * orbitRadius - (avatarSize / 2);
        
        // Smooth interpolation
        avatar._currentX = lerp(avatar._currentX, targetX, 0.15);
        avatar._currentY = lerp(avatar._currentY, targetY, 0.15);
        
        const isInBounds = avatar._currentY > -avatarSize && 
                         avatar._currentY < containerRect.height && 
                         avatar._currentX > -avatarSize && 
                         avatar._currentX < containerRect.width;
        
        const targetOpacity = isInBounds ? 1 : 0;
        avatar._currentOpacity = lerp(avatar._currentOpacity, targetOpacity, 0.1);
        
        avatar.style.transform = \`translate3d(\${avatar._currentX}px, \${avatar._currentY}px, 0)\`;
        avatar.style.opacity = avatar._currentOpacity;
        avatar.style.pointerEvents = avatar._currentOpacity > 0.1 ? 'auto' : 'none';
      });
    }
    
    destroy() {
      if (this.animationId) {
        cancelAnimationFrame(this.animationId);
      }
      
      if (this.scrollHandler) {
        window.removeEventListener('scroll', this.scrollHandler);
      }
      
      if (this.resizeHandler) {
        window.removeEventListener('resize', this.resizeHandler);
      }
      
      if (this.intersectionObserver) {
        this.intersectionObserver.disconnect();
      }
      
      if (this.overlay && this.overlay.parentNode) {
        this.overlay.parentNode.removeChild(this.overlay);
      }
      
      this.avatars = [];
    }
  }
  
  function init() {
    const c = document.querySelector('[data-avatar-orbit]');
    if(!c) return;
    
    if(c._orbitInstance) {
      c._orbitInstance.destroy();
    }
    
${avatarUrlsCode}
    
    setTimeout(() => {
      c._orbitInstance = new AvatarOrbit(c, {
        avatarSize: ${orbitConfig.avatarSize},
        orbitRadius: ${orbitConfig.orbitRadius},
        verticalPosition: ${orbitConfig.verticalPosition},
        mobileVerticalPosition: ${orbitConfig.mobileVerticalPosition},
        totalAvatars: ${orbitConfig.totalAvatars},
        animationSpeed: ${orbitConfig.animationSpeed},
        smoothness: ${orbitConfig.smoothness},
        borderColor: '${orbitConfig.borderColor}',
        borderWidth: ${orbitConfig.borderWidth}
      });
    }, 50);
  }
  
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
  
  document.addEventListener('bricks/content_loaded', init);
  
  setTimeout(init, 100);
})();`;
      }

      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 'avatar-size':
              orbitConfig.avatarSize = defaultValue;
              updateOrbitPreview('size');
              break;
            case 'orbit-radius':
              orbitConfig.orbitRadius = defaultValue;
              updateOrbitPreview('size');
              break;
            case 'vertical-position':
              orbitConfig.verticalPosition = defaultValue;
              updateOrbitPreview('size');
              break;
            case 'mobile-vertical-position':
              orbitConfig.mobileVerticalPosition = defaultValue;
              updateOrbitPreview('size');
              break;
            case 'total-avatars':
              orbitConfig.totalAvatars = defaultValue;
              updateOrbitPreview('content');
              break;
            case 'animation-speed':
              orbitConfig.animationSpeed = defaultValue;
              updateOrbitPreview('speed');
              break;
            case 'smoothness':
              orbitConfig.smoothness = defaultValue;
              updateOrbitPreview('speed');
              break;
            case 'border-width':
              orbitConfig.borderWidth = defaultValue;
              updateOrbitPreview('appearance');
              break;
          }
          
          showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
        }
      };

      function generateRandomOrbit() {
        orbitConfig.avatarSize = Math.floor(Math.random() * 80) + 40;
        orbitConfig.orbitRadius = Math.floor(Math.random() * 420) + 80;
        orbitConfig.verticalPosition = Math.floor(Math.random() * 70) + 20;
        orbitConfig.mobileVerticalPosition = Math.floor(Math.random() * 70) + 20;
        orbitConfig.totalAvatars = Math.floor(Math.random() * 15) + 6;
        orbitConfig.animationSpeed = Math.random() * 2.8 + 0.2;
        orbitConfig.smoothness = Math.random() * 0.45 + 0.5;
        orbitConfig.borderColor = generateRandomColor();
        orbitConfig.borderWidth = Math.floor(Math.random() * 8) + 1;
        
        document.getElementById('avatar-size').value = orbitConfig.avatarSize;
        document.getElementById('orbit-radius').value = orbitConfig.orbitRadius;
        document.getElementById('vertical-position').value = orbitConfig.verticalPosition;
        document.getElementById('mobile-vertical-position').value = orbitConfig.mobileVerticalPosition;
        document.getElementById('total-avatars').value = orbitConfig.totalAvatars;
        document.getElementById('animation-speed').value = orbitConfig.animationSpeed;
        document.getElementById('smoothness').value = orbitConfig.smoothness;
        document.getElementById('border-color').value = orbitConfig.borderColor;
        document.getElementById('border-width').value = orbitConfig.borderWidth;
        
        document.getElementById('avatar-size-value').textContent = orbitConfig.avatarSize;
        document.getElementById('orbit-radius-value').textContent = orbitConfig.orbitRadius;
        document.getElementById('vertical-position-value').textContent = orbitConfig.verticalPosition;
        document.getElementById('mobile-vertical-position-value').textContent = orbitConfig.mobileVerticalPosition;
        document.getElementById('total-avatars-value').textContent = orbitConfig.totalAvatars;
        document.getElementById('animation-speed-value').textContent = orbitConfig.animationSpeed;
        document.getElementById('smoothness-value').textContent = orbitConfig.smoothness;
        document.getElementById('border-width-value').textContent = orbitConfig.borderWidth;
        
        updateColorInputs();
        updateOrbitPreview('full');
        showNotification('Random avatar orbit generated!');
      }

      function updateColorInputs() {
        const colorInput = document.getElementById('border-color');
        const hexInput = document.getElementById('border-color-hex');
        const hslInput = document.getElementById('border-color-hsl');
        
        if (colorInput && hexInput && hslInput) {
          colorInput.value = orbitConfig.borderColor;
          hexInput.value = orbitConfig.borderColor;
          const hsl = hexToRgb(orbitConfig.borderColor);
          if (hsl) {
            const hslColor = rgbToHsl(hsl.r, hsl.g, hsl.b);
            hslInput.value = `hsl(${hslColor.h}, ${hslColor.s}%, ${hslColor.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', orbitConfig.borderColor);
          }
        }
      }

      function rgbToHsl(r, g, b) {
        r /= 255;
        g /= 255;
        b /= 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 initializeUI() {
        setTimeout(() => {
          initAvatarOrbit();
        }, 100);
        
        const instructionsToggle = document.getElementById('instructions-toggle');
        const instructionsContent = document.getElementById('instructions-content');
        const instructionsCard = document.getElementById('instructions-card');
        const toggleIcon = instructionsToggle?.querySelector('.toggle-icon');
        
        if (instructionsToggle && instructionsContent && instructionsCard && toggleIcon) {
          instructionsToggle.addEventListener('click', () => {
            const isVisible = instructionsContent.classList.contains('show');
            
            if (isVisible) {
              instructionsContent.classList.remove('show');
              instructionsCard.classList.remove('expanded');
              toggleIcon.classList.remove('expanded');
            } else {
              instructionsContent.classList.add('show');
              instructionsCard.classList.add('expanded');
              toggleIcon.classList.add('expanded');
            }
          });
        }

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

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

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

        const randomizeOrbit = document.getElementById('randomize-orbit');
        if (randomizeOrbit) {
          randomizeOrbit.addEventListener('click', () => {
            generateRandomOrbit();
          });
        }

        const backgroundPicker = document.getElementById('preview-background-picker');
        const previewContainer = document.getElementById('orbit-preview');

        if (backgroundPicker && previewContainer) {
          backgroundPicker.addEventListener('input', (e) => {
            const selectedColor = e.target.value;
            previewContainer.style.background = `linear-gradient(135deg, ${selectedColor} 0%, #764ba2 100%)`;
            showNotification(`Preview background changed to ${selectedColor}`);
          });
        }

        const resetOrbit = document.getElementById('reset-orbit');
        if (resetOrbit) {
          resetOrbit.addEventListener('click', () => {
            orbitConfig.avatarSize = defaultConfig.avatarSize;
            orbitConfig.orbitRadius = defaultConfig.orbitRadius;
            orbitConfig.verticalPosition = defaultConfig.verticalPosition;
            orbitConfig.mobileVerticalPosition = defaultConfig.mobileVerticalPosition;
            orbitConfig.totalAvatars = defaultConfig.totalAvatars;
            orbitConfig.animationSpeed = defaultConfig.animationSpeed;
            orbitConfig.smoothness = defaultConfig.smoothness;
            
            const avatarSize = document.getElementById('avatar-size');
            const orbitRadius = document.getElementById('orbit-radius');
            const verticalPosition = document.getElementById('vertical-position');
            const mobileVerticalPosition = document.getElementById('mobile-vertical-position');
            const totalAvatars = document.getElementById('total-avatars');
            const animationSpeed = document.getElementById('animation-speed');
            const smoothness = document.getElementById('smoothness');
            const avatarSizeValue = document.getElementById('avatar-size-value');
            const orbitRadiusValue = document.getElementById('orbit-radius-value');
            const verticalPositionValue = document.getElementById('vertical-position-value');
            const mobileVerticalPositionValue = document.getElementById('mobile-vertical-position-value');
            const totalAvatarsValue = document.getElementById('total-avatars-value');
            const animationSpeedValue = document.getElementById('animation-speed-value');
            const smoothnessValue = document.getElementById('smoothness-value');
            
            if (avatarSize) avatarSize.value = defaultConfig.avatarSize;
            if (orbitRadius) orbitRadius.value = defaultConfig.orbitRadius;
            if (verticalPosition) verticalPosition.value = defaultConfig.verticalPosition;
            if (mobileVerticalPosition) mobileVerticalPosition.value = defaultConfig.mobileVerticalPosition;
            if (totalAvatars) totalAvatars.value = defaultConfig.totalAvatars;
            if (animationSpeed) animationSpeed.value = defaultConfig.animationSpeed;
            if (smoothness) smoothness.value = defaultConfig.smoothness;
            if (avatarSizeValue) avatarSizeValue.textContent = defaultConfig.avatarSize;
            if (orbitRadiusValue) orbitRadiusValue.textContent = defaultConfig.orbitRadius;
            if (verticalPositionValue) verticalPositionValue.textContent = defaultConfig.verticalPosition;
            if (mobileVerticalPositionValue) mobileVerticalPositionValue.textContent = defaultConfig.mobileVerticalPosition;
            if (totalAvatarsValue) totalAvatarsValue.textContent = defaultConfig.totalAvatars;
            if (animationSpeedValue) animationSpeedValue.textContent = defaultConfig.animationSpeed;
            if (smoothnessValue) smoothnessValue.textContent = defaultConfig.smoothness;
            
            updateOrbitPreview('full');
            showNotification('Orbit settings reset');
          });
        }

        const resetAppearance = document.getElementById('reset-appearance');
        if (resetAppearance) {
          resetAppearance.addEventListener('click', () => {
            orbitConfig.borderColor = defaultConfig.borderColor;
            orbitConfig.borderWidth = defaultConfig.borderWidth;
            
            const borderColor = document.getElementById('border-color');
            const borderWidth = document.getElementById('border-width');
            const borderWidthValue = document.getElementById('border-width-value');
            
            if (borderColor) borderColor.value = defaultConfig.borderColor;
            if (borderWidth) borderWidth.value = defaultConfig.borderWidth;
            if (borderWidthValue) borderWidthValue.textContent = defaultConfig.borderWidth;
            
            updateColorInputs();
            updateOrbitPreview('appearance');
            showNotification('Appearance settings reset');
          });
        }

        const resetAvatars = document.getElementById('reset-avatars');
        if (resetAvatars) {
          resetAvatars.addEventListener('click', () => {
            orbitConfig.imageUrls = [];
            
            document.querySelectorAll('.url-input').forEach(input => {
              input.value = '';
            });
            
            updateTotalAvatarsVisibility();
            updateOrbitPreview('content');
            showNotification('Avatar URLs cleared');
          });
        }

        const colorInput = document.getElementById('border-color');
        const hexInput = document.getElementById('border-color-hex');
        const hslInput = document.getElementById('border-color-hsl');
        
        if (colorInput && hexInput && hslInput) {
          const hsl = hexToRgb(colorInput.value);
          if (hsl) {
            const hslColor = rgbToHsl(hsl.r, hsl.g, hsl.b);
            hslInput.value = `hsl(${hslColor.h}, ${hslColor.s}%, ${hslColor.l}%)`;
          }
          
          colorInput.addEventListener('input', () => {
            const color = colorInput.value;
            hexInput.value = color;
            const rgb = hexToRgb(color);
            if (rgb) {
              const hslColor = rgbToHsl(rgb.r, rgb.g, rgb.b);
              hslInput.value = `hsl(${hslColor.h}, ${hslColor.s}%, ${hslColor.l}%)`;
            }
            hexInput.classList.remove('invalid');
            hslInput.classList.remove('invalid');
            orbitConfig.borderColor = color;
            
            const colorPickerContainer = colorInput.closest('.color-row')?.querySelector('.color-picker-container');
            if (colorPickerContainer) {
              colorPickerContainer.style.setProperty('--selected-color', color);
            }
            
            updateOrbitPreview('appearance');
          });
          
          hexInput.addEventListener('input', (e) => {
            let hex = e.target.value;
            
            hex = formatHex(hex);
            e.target.value = hex;
            
            if (isValidHex(hex)) {
              colorInput.value = hex;
              const rgb = hexToRgb(hex);
              if (rgb) {
                const hslColor = rgbToHsl(rgb.r, rgb.g, rgb.b);
                hslInput.value = `hsl(${hslColor.h}, ${hslColor.s}%, ${hslColor.l}%)`;
              }
              orbitConfig.borderColor = hex;
              e.target.classList.remove('invalid');
              hslInput.classList.remove('invalid');
              
              const colorPickerContainer = colorInput.closest('.color-row')?.querySelector('.color-picker-container');
              if (colorPickerContainer) {
                colorPickerContainer.style.setProperty('--selected-color', hex);
              }
              
              updateOrbitPreview('appearance');
            } 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;
                orbitConfig.borderColor = hex;
                e.target.classList.remove('invalid');
                hexInput.classList.remove('invalid');
                
                const colorPickerContainer = colorInput.closest('.color-row')?.querySelector('.color-picker-container');
                if (colorPickerContainer) {
                  colorPickerContainer.style.setProperty('--selected-color', hex);
                }
                
                updateOrbitPreview('appearance');
                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;
                  orbitConfig.borderColor = hex;
                  e.target.classList.remove('invalid');
                  hexInput.classList.remove('invalid');
                  updateOrbitPreview('appearance');
                  return;
                }
              }
            }
            
            if (!isValidHsl(e.target.value)) {
              const rgb = hexToRgb(colorInput.value);
              if (rgb) {
                const hslColor = rgbToHsl(rgb.r, rgb.g, rgb.b);
                e.target.value = `hsl(${hslColor.h}, ${hslColor.s}%, ${hslColor.l}%)`;
              }
              e.target.classList.remove('invalid');
            }
          });
        }

        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 'avatar-size':
                orbitConfig.avatarSize = parseInt(input.value);
                updateOrbitPreview('size');
                break;
              case 'orbit-radius':
                orbitConfig.orbitRadius = parseInt(input.value);
                updateOrbitPreview('size');
                break;
              case 'vertical-position':
                orbitConfig.verticalPosition = parseInt(input.value);
                updateOrbitPreview('size');
                break;
              case 'mobile-vertical-position':
                orbitConfig.mobileVerticalPosition = parseInt(input.value);
                updateOrbitPreview('size');
                break;
              case 'total-avatars':
                orbitConfig.totalAvatars = parseInt(input.value);
                updateOrbitPreview('content');
                break;
              case 'animation-speed':
                orbitConfig.animationSpeed = parseFloat(input.value);
                updateOrbitPreview('speed');
                break;
              case 'smoothness':
                orbitConfig.smoothness = parseFloat(input.value);
                updateOrbitPreview('speed');
                break;
              case 'border-width':
                orbitConfig.borderWidth = parseInt(input.value);
                updateOrbitPreview('appearance');
                break;
            }
          });
        });

        document.querySelectorAll('.url-input').forEach(input => {
          input.addEventListener('input', () => {
            const newImageUrls = [];
            document.querySelectorAll('.url-input').forEach(urlInput => {
              const url = urlInput.value.trim();
              if (url) {
                newImageUrls.push({
                  src: url,
                  alt: `Avatar ${urlInput.getAttribute('data-index')}`
                });
              }
            });
            orbitConfig.imageUrls = newImageUrls;
            updateTotalAvatarsVisibility();
            updateOrbitPreview('content');
          });
        });

        const addImageBtn = document.getElementById('add-image-btn');
        if (addImageBtn) {
          addImageBtn.addEventListener('click', addImageInput);
        }

        function addImageInput() {
          const container = document.getElementById('image-urls-container');
          const count = container.querySelectorAll('.image-url-group').length + 1;
          
          if (count > 10) {
            showNotification('Maximum 10 avatars allowed', 'warning');
            return;
          }
          
          const groupDiv = document.createElement('div');
          groupDiv.className = 'image-url-group';
          groupDiv.style.opacity = '0';
          groupDiv.style.transform = 'translateY(10px)';
          groupDiv.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
          
          groupDiv.innerHTML = `
            <div class="control-label">
              <span class="label-text">Avatar ${count}</span>
            </div>
            <div class="image-url-input">
              <input type="text" placeholder="https://example.com/avatar${count}.jpg" class="url-input" data-index="${count}">
              <button class="remove-btn" title="Remove this avatar">×</button>
            </div>
          `;
          
          container.appendChild(groupDiv);
          
          setTimeout(() => {
            groupDiv.style.opacity = '1';
            groupDiv.style.transform = 'translateY(0)';
          }, 10);
          
          const input = groupDiv.querySelector('.url-input');
          input.addEventListener('input', () => {
            const newImageUrls = [];
            document.querySelectorAll('.url-input').forEach(urlInput => {
              const url = urlInput.value.trim();
              if (url) {
                newImageUrls.push({
                  src: url,
                  alt: `Avatar ${urlInput.getAttribute('data-index')}`
                });
              }
            });
            orbitConfig.imageUrls = newImageUrls;
            updateTotalAvatarsVisibility();
            updateOrbitPreview('content');
          });
          
          const removeBtn = groupDiv.querySelector('.remove-btn');
          removeBtn.addEventListener('click', function() {
            groupDiv.style.opacity = '0';
            groupDiv.style.transform = 'translateY(10px)';
            
            setTimeout(() => {
              container.removeChild(groupDiv);
              updateImageIndexes();
              const newImageUrls = [];
              document.querySelectorAll('.url-input').forEach(urlInput => {
                const url = urlInput.value.trim();
                if (url) {
                  newImageUrls.push({
                    src: url,
                    alt: `Avatar ${urlInput.getAttribute('data-index')}`
                  });
                }
              });
              orbitConfig.imageUrls = newImageUrls;
              updateTotalAvatarsVisibility();
              updateOrbitPreview('content');
            }, 300);
          });
        }

        function updateImageIndexes() {
          const groups = document.querySelectorAll('.image-url-group');
          
          groups.forEach((group, index) => {
            const labelText = group.querySelector('.label-text');
            const input = group.querySelector('.url-input');
            const newIndex = index + 1;
            
            labelText.textContent = `Avatar ${newIndex}`;
            input.setAttribute('data-index', newIndex);
            input.setAttribute('placeholder', `https://example.com/avatar${newIndex}.jpg`);
          });
        }

        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;
            }
          } else {
            switch (e.key.toLowerCase()) {
              case 'r':
                generateRandomOrbit();
                break;
              case 'b':
                document.getElementById('preview-background-picker').click();
                break;
            }
          }
        });

        updateColorInputs();
        updateTotalAvatarsVisibility();
        
        setTimeout(() => {
          showNotification('Responsive Avatar Orbit configurator loaded!');
        }, 500);
      }
      
      initializeUI();
    });
  </script>
</body>
</html>
Avatar Orbit - Bricksfusion
LIGHTWEIGHT

Avatar Orbit

Creates a circular orbit of avatars that rotates smoothly as you scroll. Perfect for showcasing team members, testimonials, or social proof.

Scroll to see the orbit rotate

Avatars

Avatar Images (1-20) image upload

Upload up to 20 custom avatar images. If you don't upload images, placeholder avatars will be used automatically. The element will repeat your images if you show more avatars than you've uploaded.

Default: Random placeholder avatars

Total Avatars 1-20

How many avatars to display in the orbit. More avatars create a fuller circle.

Default: 8

Appearance

Avatar Size 30-120 pixels

Size of each avatar. Larger values make the avatars more prominent.

Default: 60

Orbit Radius 80-400 pixels

Size of the circular path the avatars follow. Larger radius creates a bigger circle.

Default: 150

Border Color color picker

Color of the border around each avatar.

Default: White

Border Width 0-10 pixels

Thickness of the border. Set to 0 for no border.

Default: 3

Position

Vertical Position 0-100%

Where the orbit sits vertically on desktop. 0 is top, 50 is center, 100 is bottom.

Default: 70

Mobile Vertical Position 0-100%

Where the orbit sits vertically on mobile devices. Often needs to be different than desktop for better mobile layout.

Default: 60

Animation

Animation Speed 0.1-3.0

How fast the orbit rotates when scrolling. Higher values make it spin faster.

Default: 1.0

Smoothness 0.5-0.95

How smooth the animation feels. Higher values create smoother, more fluid movement. Lower values make it more responsive but less smooth.

Default: 0.85

Performance

This element is lightweight and optimized. It only animates when visible on screen and automatically scales for mobile devices. Safe to use multiple times on a page.