Compare commits
	
		
			5 Commits
		
	
	
		
			8a04fe5aa6
			...
			04c1214cd1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					04c1214cd1 | ||
| 
						 | 
					b03779b487 | ||
| 
						 | 
					b5f8ceeb31 | ||
| 
						 | 
					1591886505 | ||
| 
						 | 
					a6b63c2d2b | 
@@ -40,34 +40,32 @@
 | 
				
			|||||||
      <p> Follow the steps to generate your static gallery</p>
 | 
					      <p> Follow the steps to generate your static gallery</p>
 | 
				
			||||||
      <ul id="stepper">
 | 
					      <ul id="stepper">
 | 
				
			||||||
        <li><a class="step-active" href="/gallery-editor">Upload your photos</a></li>
 | 
					        <li><a class="step-active" href="/gallery-editor">Upload your photos</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><a href="/site-info">Configure site info</a></li>
 | 
					        <li><a href="/site-info">Configure site info</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><a href="/theme-editor">Customize your theme</a></li>
 | 
					        <li><a href="/theme-editor">Customize your theme</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><button id="stepper-build">Generate your static site!</button></li>
 | 
					        <li><button id="stepper-build">Generate your static site!</button></li>
 | 
				
			||||||
      </ul>
 | 
					      </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					    <!-- Delete confirmation modal -->
 | 
				
			||||||
  <script src="{{ url_for('static', filename='js/main.js') }}" defer></script>
 | 
					    <div id="delete-modal" class="modal" style="display:none;">
 | 
				
			||||||
  <script src="{{ url_for('static', filename='js/upload.js') }}" defer></script>
 | 
					      <div class="modal-content">
 | 
				
			||||||
  <script src="{{ url_for('static', filename='js/build.js') }}" defer></script>
 | 
					        <span id="delete-modal-close" class="modal-close">×</span>
 | 
				
			||||||
  <!-- Delete confirmation modal -->
 | 
					        <h3>Confirm Deletion</h3>
 | 
				
			||||||
  <div id="delete-modal" class="modal" style="display:none;">
 | 
					        <p id="delete-modal-text">Are you sure you want to delete this image?</p>
 | 
				
			||||||
    <div class="modal-content">
 | 
					        <div class="modal-actions">
 | 
				
			||||||
      <span id="delete-modal-close" class="modal-close">×</span>
 | 
					          <button id="delete-modal-confirm" class="modal-btn danger">Delete</button>
 | 
				
			||||||
      <h3>Confirm Deletion</h3>
 | 
					          <button id="delete-modal-cancel" class="modal-btn">Cancel</button>
 | 
				
			||||||
      <p id="delete-modal-text">Are you sure you want to delete this image?</p>
 | 
					        </div>
 | 
				
			||||||
      <div class="modal-actions">
 | 
					 | 
				
			||||||
        <button id="delete-modal-confirm" class="modal-btn danger">Delete</button>
 | 
					 | 
				
			||||||
        <button id="delete-modal-cancel" class="modal-btn">Cancel</button>
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block scripts %}
 | 
					{% block scripts %}
 | 
				
			||||||
  <script src="{{ url_for('static', filename='js/main.js') }}"></script>
 | 
					  <script src="{{ url_for('static', filename='js/gallery-editor.js') }}"></script>
 | 
				
			||||||
  <script src="{{ url_for('static', filename='js/upload.js') }}"></script>
 | 
					  <script src="{{ url_for('static', filename='js/upload.js') }}"></script>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
@@ -13,11 +13,11 @@
 | 
				
			|||||||
      <p> Follow the steps to generate your static gallery</p>
 | 
					      <p> Follow the steps to generate your static gallery</p>
 | 
				
			||||||
      <ul id="stepper">
 | 
					      <ul id="stepper">
 | 
				
			||||||
        <li><a href="/gallery-editor">Upload your photos</a></li>
 | 
					        <li><a href="/gallery-editor">Upload your photos</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><a href="/site-info">Configure site info</a></li>
 | 
					        <li><a href="/site-info">Configure site info</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><a href="/theme-editor">Customize your theme</a></li>
 | 
					        <li><a href="/theme-editor">Customize your theme</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><button id="stepper-build">Generate your static site!</button></li>
 | 
					        <li><button id="stepper-build">Generate your static site!</button></li>
 | 
				
			||||||
      </ul>
 | 
					      </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,21 @@
 | 
				
			|||||||
 * @param {string} type - "success" or "error".
 | 
					 * @param {string} type - "success" or "error".
 | 
				
			||||||
 * @param {number} duration - Duration in ms.
 | 
					 * @param {number} duration - Duration in ms.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Loader helpers ---
 | 
				
			||||||
 | 
					function showLoader(text = "Uploading...") {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) {
 | 
				
			||||||
 | 
					    loader.classList.add("active");
 | 
				
			||||||
 | 
					    document.getElementById("loader-text").textContent = text;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function hideLoader() {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) loader.classList.remove("active");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Toast helpers ---
 | 
				
			||||||
function showToast(message, type = "success", duration = 3000) {
 | 
					function showToast(message, type = "success", duration = 3000) {
 | 
				
			||||||
  const container = document.getElementById("toast-container");
 | 
					  const container = document.getElementById("toast-container");
 | 
				
			||||||
  if (!container) return;
 | 
					  if (!container) return;
 | 
				
			||||||
@@ -29,9 +44,11 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Build action handler
 | 
					  // Build action handler
 | 
				
			||||||
  async function handleBuildClick() {
 | 
					  async function handleBuildClick() {
 | 
				
			||||||
 | 
					    showLoader("Building static site...");
 | 
				
			||||||
    // Trigger build on backend
 | 
					    // Trigger build on backend
 | 
				
			||||||
    const res = await fetch("/api/build", { method: "POST" });
 | 
					    const res = await fetch("/api/build", { method: "POST" });
 | 
				
			||||||
    const result = await res.json();
 | 
					    const result = await res.json();
 | 
				
			||||||
 | 
					    hideLoader();
 | 
				
			||||||
    if (result.status === "ok") {
 | 
					    if (result.status === "ok") {
 | 
				
			||||||
      // Show build success modal
 | 
					      // Show build success modal
 | 
				
			||||||
      if (buildModal) buildModal.style.display = "flex";
 | 
					      if (buildModal) buildModal.style.display = "flex";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -92,6 +92,14 @@ function renderTags(imgIndex, tags) {
 | 
				
			|||||||
  input.placeholder = 'Add tag...';
 | 
					  input.placeholder = 'Add tag...';
 | 
				
			||||||
  inputContainer.appendChild(input);
 | 
					  inputContainer.appendChild(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // --- Validate button ---
 | 
				
			||||||
 | 
					  const validateBtn = document.createElement('button');
 | 
				
			||||||
 | 
					  validateBtn.textContent = '✔️';
 | 
				
			||||||
 | 
					  validateBtn.className = 'validate-tag-btn';
 | 
				
			||||||
 | 
					  validateBtn.style.display = 'none'; // hidden by default
 | 
				
			||||||
 | 
					  validateBtn.style.marginLeft = '4px';
 | 
				
			||||||
 | 
					  inputContainer.appendChild(validateBtn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const suggestionBox = document.createElement('ul');
 | 
					  const suggestionBox = document.createElement('ul');
 | 
				
			||||||
  suggestionBox.className = 'suggestions';
 | 
					  suggestionBox.className = 'suggestions';
 | 
				
			||||||
  inputContainer.appendChild(suggestionBox);
 | 
					  inputContainer.appendChild(suggestionBox);
 | 
				
			||||||
@@ -148,8 +156,14 @@ function renderTags(imgIndex, tags) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  input.addEventListener('input', updateSuggestions);
 | 
					  input.addEventListener('input', () => {
 | 
				
			||||||
  input.addEventListener('focus', updateSuggestions);
 | 
					    updateSuggestions();
 | 
				
			||||||
 | 
					    validateBtn.style.display = input.value.trim() ? 'inline-block' : 'none';
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  input.addEventListener('focus', () => {
 | 
				
			||||||
 | 
					    updateSuggestions();
 | 
				
			||||||
 | 
					    validateBtn.style.display = input.value.trim() ? 'inline-block' : 'none';
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  input.addEventListener('keydown', (e) => {
 | 
					  input.addEventListener('keydown', (e) => {
 | 
				
			||||||
    const items = suggestionBox.querySelectorAll('li');
 | 
					    const items = suggestionBox.querySelectorAll('li');
 | 
				
			||||||
@@ -172,11 +186,13 @@ function renderTags(imgIndex, tags) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      input.value = '';
 | 
					      input.value = '';
 | 
				
			||||||
      updateSuggestions();
 | 
					      updateSuggestions();
 | 
				
			||||||
 | 
					      validateBtn.style.display = 'none';
 | 
				
			||||||
    } else if ([' ', ','].includes(e.key)) {
 | 
					    } else if ([' ', ','].includes(e.key)) {
 | 
				
			||||||
      e.preventDefault();
 | 
					      e.preventDefault();
 | 
				
			||||||
      addTag(input.value);
 | 
					      addTag(input.value);
 | 
				
			||||||
      input.value = '';
 | 
					      input.value = '';
 | 
				
			||||||
      updateSuggestions();
 | 
					      updateSuggestions();
 | 
				
			||||||
 | 
					      validateBtn.style.display = 'none';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -184,9 +200,20 @@ function renderTags(imgIndex, tags) {
 | 
				
			|||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      suggestionBox.style.display = 'none';
 | 
					      suggestionBox.style.display = 'none';
 | 
				
			||||||
      input.value = '';
 | 
					      input.value = '';
 | 
				
			||||||
 | 
					      validateBtn.style.display = 'none';
 | 
				
			||||||
    }, 150);
 | 
					    }, 150);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // --- Validate button action ---
 | 
				
			||||||
 | 
					  validateBtn.onclick = () => {
 | 
				
			||||||
 | 
					    if (input.value.trim()) {
 | 
				
			||||||
 | 
					      addTag(input.value.trim());
 | 
				
			||||||
 | 
					      input.value = '';
 | 
				
			||||||
 | 
					      updateSuggestions();
 | 
				
			||||||
 | 
					      validateBtn.style.display = 'none';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  input.focus();
 | 
					  input.focus();
 | 
				
			||||||
  updateSuggestions();
 | 
					  updateSuggestions();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -12,6 +12,19 @@ function showToast(message, type = "success", duration = 3000) {
 | 
				
			|||||||
  }, duration);
 | 
					  }, duration);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Loader helpers ---
 | 
				
			||||||
 | 
					function showLoader(text = "Uploading...") {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) {
 | 
				
			||||||
 | 
					    loader.classList.add("active");
 | 
				
			||||||
 | 
					    document.getElementById("loader-text").textContent = text;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function hideLoader() {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) loader.classList.remove("active");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
document.addEventListener("DOMContentLoaded", () => {
 | 
					document.addEventListener("DOMContentLoaded", () => {
 | 
				
			||||||
  // Form and menu logic
 | 
					  // Form and menu logic
 | 
				
			||||||
  const form = document.getElementById("site-info-form");
 | 
					  const form = document.getElementById("site-info-form");
 | 
				
			||||||
@@ -130,10 +143,12 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
    thumbnailUpload.addEventListener("change", async (e) => {
 | 
					    thumbnailUpload.addEventListener("change", async (e) => {
 | 
				
			||||||
      const file = e.target.files[0];
 | 
					      const file = e.target.files[0];
 | 
				
			||||||
      if (!file) return;
 | 
					      if (!file) return;
 | 
				
			||||||
 | 
					      showLoader("Uploading thumbnail...");
 | 
				
			||||||
      const formData = new FormData();
 | 
					      const formData = new FormData();
 | 
				
			||||||
      formData.append("file", file);
 | 
					      formData.append("file", file);
 | 
				
			||||||
      const res = await fetch("/api/thumbnail/upload", { method: "POST", body: formData });
 | 
					      const res = await fetch("/api/thumbnail/upload", { method: "POST", body: formData });
 | 
				
			||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        if (thumbnailInput) thumbnailInput.value = result.filename;
 | 
					        if (thumbnailInput) thumbnailInput.value = result.filename;
 | 
				
			||||||
        updateThumbnailPreview(`/photos/${result.filename}?t=${Date.now()}`);
 | 
					        updateThumbnailPreview(`/photos/${result.filename}?t=${Date.now()}`);
 | 
				
			||||||
@@ -183,12 +198,14 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
    themeUpload.addEventListener("change", async (e) => {
 | 
					    themeUpload.addEventListener("change", async (e) => {
 | 
				
			||||||
      const files = Array.from(e.target.files);
 | 
					      const files = Array.from(e.target.files);
 | 
				
			||||||
      if (files.length === 0) return;
 | 
					      if (files.length === 0) return;
 | 
				
			||||||
 | 
					      showLoader("Uploading theme...");
 | 
				
			||||||
      const formData = new FormData();
 | 
					      const formData = new FormData();
 | 
				
			||||||
      files.forEach(file => {
 | 
					      files.forEach(file => {
 | 
				
			||||||
        formData.append("files", file, file.webkitRelativePath || file.name);
 | 
					        formData.append("files", file, file.webkitRelativePath || file.name);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      const res = await fetch("/api/theme/upload", { method: "POST", body: formData });
 | 
					      const res = await fetch("/api/theme/upload", { method: "POST", body: formData });
 | 
				
			||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        showToast("✅ Theme uploaded!", "success");
 | 
					        showToast("✅ Theme uploaded!", "success");
 | 
				
			||||||
        // Refresh theme select after upload
 | 
					        // Refresh theme select after upload
 | 
				
			||||||
@@ -239,12 +256,14 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
    deleteThemeModalConfirm.onclick = async () => {
 | 
					    deleteThemeModalConfirm.onclick = async () => {
 | 
				
			||||||
      if (!themeToDelete) return;
 | 
					      if (!themeToDelete) return;
 | 
				
			||||||
 | 
					      showLoader("Removing theme...");
 | 
				
			||||||
      const res = await fetch("/api/theme/remove", {
 | 
					      const res = await fetch("/api/theme/remove", {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
        headers: { "Content-Type": "application/json" },
 | 
					        headers: { "Content-Type": "application/json" },
 | 
				
			||||||
        body: JSON.stringify({ theme: themeToDelete })
 | 
					        body: JSON.stringify({ theme: themeToDelete })
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        showToast("✅ Theme removed!", "success");
 | 
					        showToast("✅ Theme removed!", "success");
 | 
				
			||||||
        // Refresh theme select
 | 
					        // Refresh theme select
 | 
				
			||||||
@@ -379,7 +398,9 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Check if thumbnail is set before saving (uploaded or present in input)
 | 
					      // Check if thumbnail is set before saving (uploaded or present in input)
 | 
				
			||||||
      if (!thumbnailInput || !thumbnailInput.value) {
 | 
					      if (!thumbnailInput || !thumbnailInput.value) {
 | 
				
			||||||
 | 
					        showLoader("Saving...");
 | 
				
			||||||
        showToast("❌ Thumbnail is required.", "error");
 | 
					        showToast("❌ Thumbnail is required.", "error");
 | 
				
			||||||
 | 
					        hideLoader();
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -417,6 +438,8 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
          intellectual_property: ipParagraphs
 | 
					          intellectual_property: ipParagraphs
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					      // --- REMOVE loader for save ---
 | 
				
			||||||
 | 
					      // showLoader("Saving...");
 | 
				
			||||||
      const res = await fetch("/api/site-info", {
 | 
					      const res = await fetch("/api/site-info", {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
        headers: { "Content-Type": "application/json" },
 | 
					        headers: { "Content-Type": "application/json" },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,20 @@ function showToast(message, type = "success", duration = 3000) {
 | 
				
			|||||||
  }, duration);
 | 
					  }, duration);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Loader helpers ---
 | 
				
			||||||
 | 
					function showLoader(text = "Uploading...") {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) {
 | 
				
			||||||
 | 
					    loader.classList.add("active");
 | 
				
			||||||
 | 
					    document.getElementById("loader-text").textContent = text;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function hideLoader() {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) loader.classList.remove("active");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Color Picker
 | 
				
			||||||
function setupColorPicker(colorId, btnId, textId, initial) {
 | 
					function setupColorPicker(colorId, btnId, textId, initial) {
 | 
				
			||||||
  const colorInput = document.getElementById(colorId);
 | 
					  const colorInput = document.getElementById(colorId);
 | 
				
			||||||
  const colorBtn = document.getElementById(btnId);
 | 
					  const colorBtn = document.getElementById(btnId);
 | 
				
			||||||
@@ -40,7 +54,6 @@ function setupColorPicker(colorId, btnId, textId, initial) {
 | 
				
			|||||||
  colorBtn.style.background = initial;
 | 
					  colorBtn.style.background = initial;
 | 
				
			||||||
  textInput.value = initial.toUpperCase();
 | 
					  textInput.value = initial.toUpperCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Color input is positioned over the button and is clickable
 | 
					 | 
				
			||||||
  colorInput.addEventListener("input", () => {
 | 
					  colorInput.addEventListener("input", () => {
 | 
				
			||||||
    colorBtn.style.background = colorInput.value;
 | 
					    colorBtn.style.background = colorInput.value;
 | 
				
			||||||
    textInput.value = colorInput.value.toUpperCase();
 | 
					    textInput.value = colorInput.value.toUpperCase();
 | 
				
			||||||
@@ -178,11 +191,13 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
        showToast("Only .woff and .woff2 fonts are allowed.", "error");
 | 
					        showToast("Only .woff and .woff2 fonts are allowed.", "error");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      showLoader("Uploading font...");
 | 
				
			||||||
      const formData = new FormData();
 | 
					      const formData = new FormData();
 | 
				
			||||||
      formData.append("file", file);
 | 
					      formData.append("file", file);
 | 
				
			||||||
      formData.append("theme", themeInfo.theme_name);
 | 
					      formData.append("theme", themeInfo.theme_name);
 | 
				
			||||||
      const res = await fetch("/api/font/upload", { method: "POST", body: formData });
 | 
					      const res = await fetch("/api/font/upload", { method: "POST", body: formData });
 | 
				
			||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        showToast("✅ Font uploaded!", "success");
 | 
					        showToast("✅ Font uploaded!", "success");
 | 
				
			||||||
        localFonts = await fetchLocalFonts(themeInfo.theme_name);
 | 
					        localFonts = await fetchLocalFonts(themeInfo.theme_name);
 | 
				
			||||||
@@ -219,9 +234,11 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
    deleteFontModalConfirm.onclick = async () => {
 | 
					    deleteFontModalConfirm.onclick = async () => {
 | 
				
			||||||
      if (!fontToDelete) return;
 | 
					      if (!fontToDelete) return;
 | 
				
			||||||
 | 
					      showLoader("Removing font...");
 | 
				
			||||||
      const result = await removeFont(themeInfo.theme_name, fontToDelete);
 | 
					      const result = await removeFont(themeInfo.theme_name, fontToDelete);
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        showToast("Font removed!", "✅ success");
 | 
					        showToast("Font removed!", "success");
 | 
				
			||||||
        localFonts = await fetchLocalFonts(themeInfo.theme_name);
 | 
					        localFonts = await fetchLocalFonts(themeInfo.theme_name);
 | 
				
			||||||
        refreshLocalFonts();
 | 
					        refreshLocalFonts();
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
@@ -272,11 +289,13 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
        showToast("Invalid file type for favicon.", "error");
 | 
					        showToast("Invalid file type for favicon.", "error");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      showLoader("Uploading favicon...");
 | 
				
			||||||
      const formData = new FormData();
 | 
					      const formData = new FormData();
 | 
				
			||||||
      formData.append("file", file);
 | 
					      formData.append("file", file);
 | 
				
			||||||
      formData.append("theme", themeInfo.theme_name);
 | 
					      formData.append("theme", themeInfo.theme_name);
 | 
				
			||||||
      const res = await fetch("/api/favicon/upload", { method: "POST", body: formData });
 | 
					      const res = await fetch("/api/favicon/upload", { method: "POST", body: formData });
 | 
				
			||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        faviconInput.value = result.filename;
 | 
					        faviconInput.value = result.filename;
 | 
				
			||||||
        updateFaviconPreview(`/themes/${themeInfo.theme_name}/${result.filename}?t=${Date.now()}`);
 | 
					        updateFaviconPreview(`/themes/${themeInfo.theme_name}/${result.filename}?t=${Date.now()}`);
 | 
				
			||||||
@@ -303,12 +322,14 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    deleteFaviconModalConfirm.onclick = async () => {
 | 
					    deleteFaviconModalConfirm.onclick = async () => {
 | 
				
			||||||
 | 
					      showLoader("Removing favicon...");
 | 
				
			||||||
      const res = await fetch("/api/favicon/remove", {
 | 
					      const res = await fetch("/api/favicon/remove", {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
        headers: { "Content-Type": "application/json" },
 | 
					        headers: { "Content-Type": "application/json" },
 | 
				
			||||||
        body: JSON.stringify({ theme: themeInfo.theme_name })
 | 
					        body: JSON.stringify({ theme: themeInfo.theme_name })
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        faviconInput.value = "";
 | 
					        faviconInput.value = "";
 | 
				
			||||||
        updateFaviconPreview("");
 | 
					        updateFaviconPreview("");
 | 
				
			||||||
@@ -335,13 +356,11 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
  if (addGoogleFontBtn) {
 | 
					  if (addGoogleFontBtn) {
 | 
				
			||||||
    addGoogleFontBtn.addEventListener("click", async () => {
 | 
					    addGoogleFontBtn.addEventListener("click", async () => {
 | 
				
			||||||
      googleFonts.push({ family: "", weights: [] });
 | 
					      googleFonts.push({ family: "", weights: [] });
 | 
				
			||||||
      // Save immediately to backend
 | 
					 | 
				
			||||||
      await fetch("/api/theme-google-fonts", {
 | 
					      await fetch("/api/theme-google-fonts", {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
        headers: { "Content-Type": "application/json" },
 | 
					        headers: { "Content-Type": "application/json" },
 | 
				
			||||||
        body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
					        body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      // Fetch updated theme info and refresh dropdowns
 | 
					 | 
				
			||||||
      const updatedThemeInfo = await fetchThemeInfo();
 | 
					      const updatedThemeInfo = await fetchThemeInfo();
 | 
				
			||||||
      const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
					      const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
				
			||||||
      googleFonts.length = 0;
 | 
					      googleFonts.length = 0;
 | 
				
			||||||
@@ -353,13 +372,11 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const googleFontsFields = document.getElementById("google-fonts-fields");
 | 
					  const googleFontsFields = document.getElementById("google-fonts-fields");
 | 
				
			||||||
  if (googleFontsFields) {
 | 
					  if (googleFontsFields) {
 | 
				
			||||||
    // Save on blur for family/weights fields
 | 
					 | 
				
			||||||
    googleFontsFields.addEventListener("blur", async (e) => {
 | 
					    googleFontsFields.addEventListener("blur", async (e) => {
 | 
				
			||||||
      if (
 | 
					      if (
 | 
				
			||||||
        e.target.name &&
 | 
					        e.target.name &&
 | 
				
			||||||
        (e.target.name.endsWith("[family]") || e.target.name.endsWith("[weights]"))
 | 
					        (e.target.name.endsWith("[family]") || e.target.name.endsWith("[weights]"))
 | 
				
			||||||
      ) {
 | 
					      ) {
 | 
				
			||||||
        // Update googleFonts array from the form fields
 | 
					 | 
				
			||||||
        const fontFields = googleFontsFields.querySelectorAll(".input-field");
 | 
					        const fontFields = googleFontsFields.querySelectorAll(".input-field");
 | 
				
			||||||
        googleFonts.length = 0;
 | 
					        googleFonts.length = 0;
 | 
				
			||||||
        fontFields.forEach(field => {
 | 
					        fontFields.forEach(field => {
 | 
				
			||||||
@@ -368,13 +385,11 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
            .split(",").map(w => w.trim()).filter(Boolean);
 | 
					            .split(",").map(w => w.trim()).filter(Boolean);
 | 
				
			||||||
          googleFonts.push({ family, weights });
 | 
					          googleFonts.push({ family, weights });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        // Save immediately to backend
 | 
					 | 
				
			||||||
        await fetch("/api/theme-google-fonts", {
 | 
					        await fetch("/api/theme-google-fonts", {
 | 
				
			||||||
          method: "POST",
 | 
					          method: "POST",
 | 
				
			||||||
          headers: { "Content-Type": "application/json" },
 | 
					          headers: { "Content-Type": "application/json" },
 | 
				
			||||||
          body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
					          body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        // Fetch updated theme info and refresh dropdowns
 | 
					 | 
				
			||||||
        const updatedThemeInfo = await fetchThemeInfo();
 | 
					        const updatedThemeInfo = await fetchThemeInfo();
 | 
				
			||||||
        const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
					        const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
				
			||||||
        googleFonts.length = 0;
 | 
					        googleFonts.length = 0;
 | 
				
			||||||
@@ -382,20 +397,17 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
        renderGoogleFonts(googleFonts);
 | 
					        renderGoogleFonts(googleFonts);
 | 
				
			||||||
        refreshFontDropdowns();
 | 
					        refreshFontDropdowns();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }, true); // Use capture phase to catch blur from children
 | 
					    }, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Delegate remove button click for Google Fonts
 | 
					 | 
				
			||||||
    googleFontsFields.addEventListener("click", async (e) => {
 | 
					    googleFontsFields.addEventListener("click", async (e) => {
 | 
				
			||||||
      if (e.target.classList.contains("remove-google-font")) {
 | 
					      if (e.target.classList.contains("remove-google-font")) {
 | 
				
			||||||
        const idx = Number(e.target.dataset.idx);
 | 
					        const idx = Number(e.target.dataset.idx);
 | 
				
			||||||
        googleFonts.splice(idx, 1);
 | 
					        googleFonts.splice(idx, 1);
 | 
				
			||||||
        // Save immediately to backend
 | 
					 | 
				
			||||||
        await fetch("/api/theme-google-fonts", {
 | 
					        await fetch("/api/theme-google-fonts", {
 | 
				
			||||||
          method: "POST",
 | 
					          method: "POST",
 | 
				
			||||||
          headers: { "Content-Type": "application/json" },
 | 
					          headers: { "Content-Type": "application/json" },
 | 
				
			||||||
          body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
					          body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        // Fetch updated theme info and refresh dropdowns
 | 
					 | 
				
			||||||
        const updatedThemeInfo = await fetchThemeInfo();
 | 
					        const updatedThemeInfo = await fetchThemeInfo();
 | 
				
			||||||
        const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
					        const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
				
			||||||
        googleFonts.length = 0;
 | 
					        googleFonts.length = 0;
 | 
				
			||||||
@@ -406,9 +418,9 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Form submit
 | 
					 | 
				
			||||||
  document.getElementById("theme-editor-form").addEventListener("submit", async (e) => {
 | 
					  document.getElementById("theme-editor-form").addEventListener("submit", async (e) => {
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					    showLoader("Saving theme...");
 | 
				
			||||||
    const data = {};
 | 
					    const data = {};
 | 
				
			||||||
    data.colors = {
 | 
					    data.colors = {
 | 
				
			||||||
      primary: document.getElementById("color-primary-text").value,
 | 
					      primary: document.getElementById("color-primary-text").value,
 | 
				
			||||||
@@ -445,6 +457,7 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
				
			|||||||
      headers: { "Content-Type": "application/json" },
 | 
					      headers: { "Content-Type": "application/json" },
 | 
				
			||||||
      body: JSON.stringify({ theme_name: themeInfo.theme_name, theme_yaml: data })
 | 
					      body: JSON.stringify({ theme_name: themeInfo.theme_name, theme_yaml: data })
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    hideLoader();
 | 
				
			||||||
    if (res.ok) {
 | 
					    if (res.ok) {
 | 
				
			||||||
      showToast("✅ Theme saved!", "success");
 | 
					      showToast("✅ Theme saved!", "success");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,41 +1,64 @@
 | 
				
			|||||||
 | 
					// --- Loader helpers ---
 | 
				
			||||||
 | 
					function showLoader(text = "Uploading...") {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) {
 | 
				
			||||||
 | 
					    loader.classList.add("active");
 | 
				
			||||||
 | 
					    document.getElementById("loader-text").textContent = text;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function hideLoader() {
 | 
				
			||||||
 | 
					  const loader = document.getElementById("global-loader");
 | 
				
			||||||
 | 
					  if (loader) loader.classList.remove("active");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Upload gallery images ---
 | 
					// --- Upload gallery images ---
 | 
				
			||||||
document.getElementById('upload-gallery').addEventListener('change', async (e) => {
 | 
					const galleryInput = document.getElementById('upload-gallery');
 | 
				
			||||||
  const files = e.target.files;
 | 
					if (galleryInput) {
 | 
				
			||||||
  if (!files.length) return;
 | 
					  galleryInput.addEventListener('change', async (e) => {
 | 
				
			||||||
 | 
					    const files = e.target.files;
 | 
				
			||||||
 | 
					    if (!files.length) return;
 | 
				
			||||||
 | 
					    showLoader("Uploading photos...");
 | 
				
			||||||
 | 
					    const formData = new FormData();
 | 
				
			||||||
 | 
					    for (const file of files) formData.append('files', file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const formData = new FormData();
 | 
					    try {
 | 
				
			||||||
  for (const file of files) formData.append('files', file);
 | 
					      const res = await fetch('/api/gallery/upload', { method: 'POST', body: formData });
 | 
				
			||||||
 | 
					      const data = await res.json();
 | 
				
			||||||
  try {
 | 
					      hideLoader();
 | 
				
			||||||
    const res = await fetch('/api/gallery/upload', { method: 'POST', body: formData });
 | 
					      if (res.ok) {
 | 
				
			||||||
    const data = await res.json();
 | 
					        showToast(`✅ ${data.uploaded.length} gallery image(s) uploaded!`, "success");
 | 
				
			||||||
    if (res.ok) {
 | 
					        if (typeof refreshGallery === "function") refreshGallery();
 | 
				
			||||||
      showToast(`✅ ${data.uploaded.length} gallery image(s) uploaded!`, "success");
 | 
					      } else showToast('Error: ' + data.error, "error");
 | 
				
			||||||
      refreshGallery();
 | 
					    } catch(err) {
 | 
				
			||||||
    } else showToast('Error: ' + data.error, "error");
 | 
					      hideLoader();
 | 
				
			||||||
  } catch(err) {
 | 
					      console.error(err);
 | 
				
			||||||
    console.error(err);
 | 
					      showToast('Server error!', "error");
 | 
				
			||||||
    showToast('Server error!', "error");
 | 
					    } finally { e.target.value = ''; }
 | 
				
			||||||
  } finally { e.target.value = ''; }
 | 
					  });
 | 
				
			||||||
});
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Upload hero images ---
 | 
					// --- Upload hero images ---
 | 
				
			||||||
document.getElementById('upload-hero').addEventListener('change', async (e) => {
 | 
					const heroInput = document.getElementById('upload-hero');
 | 
				
			||||||
  const files = e.target.files;
 | 
					if (heroInput) {
 | 
				
			||||||
  if (!files.length) return;
 | 
					  heroInput.addEventListener('change', async (e) => {
 | 
				
			||||||
 | 
					    const files = e.target.files;
 | 
				
			||||||
 | 
					    if (!files.length) return;
 | 
				
			||||||
 | 
					    showLoader("Uploading hero photos...");
 | 
				
			||||||
 | 
					    const formData = new FormData();
 | 
				
			||||||
 | 
					    for (const file of files) formData.append('files', file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const formData = new FormData();
 | 
					    try {
 | 
				
			||||||
  for (const file of files) formData.append('files', file);
 | 
					      const res = await fetch('/api/hero/upload', { method: 'POST', body: formData });
 | 
				
			||||||
 | 
					      const data = await res.json();
 | 
				
			||||||
  try {
 | 
					      hideLoader();
 | 
				
			||||||
    const res = await fetch('/api/hero/upload', { method: 'POST', body: formData });
 | 
					      if (res.ok) {
 | 
				
			||||||
    const data = await res.json();
 | 
					        showToast(`✅ ${data.uploaded.length} hero image(s) uploaded!`, "success");
 | 
				
			||||||
    if (res.ok) {
 | 
					        if (typeof refreshHero === "function") refreshHero();
 | 
				
			||||||
      showToast(`✅ ${data.uploaded.length} hero image(s) uploaded!`, "success");
 | 
					      } else showToast('Error: ' + data.error, "error");
 | 
				
			||||||
      refreshHero();
 | 
					    } catch(err) {
 | 
				
			||||||
    } else showToast('Error: ' + data.error, "error");
 | 
					      hideLoader();
 | 
				
			||||||
  } catch(err) {
 | 
					      console.error(err);
 | 
				
			||||||
    console.error(err);
 | 
					      showToast('Server error!', "error");
 | 
				
			||||||
    showToast('Server error!', "error");
 | 
					    } finally { e.target.value = ''; }
 | 
				
			||||||
  } finally { e.target.value = ''; }
 | 
					  });
 | 
				
			||||||
});
 | 
					}
 | 
				
			||||||
@@ -137,36 +137,40 @@
 | 
				
			|||||||
        <p> Follow the steps to generate your static gallery</p>
 | 
					        <p> Follow the steps to generate your static gallery</p>
 | 
				
			||||||
        <ul id="stepper">
 | 
					        <ul id="stepper">
 | 
				
			||||||
          <li><a href="/gallery-editor">Upload your photos</a></li>
 | 
					          <li><a href="/gallery-editor">Upload your photos</a></li>
 | 
				
			||||||
          <div>→</div>
 | 
					          <div></div>
 | 
				
			||||||
          <li><a class="step-active" href="/site-info">Configure site info</a></li>
 | 
					          <li><a class="step-active" href="/site-info">Configure site info</a></li>
 | 
				
			||||||
          <div>→</div>
 | 
					          <div></div>
 | 
				
			||||||
          <li><a href="/theme-editor">Customize your theme</a></li>
 | 
					          <li><a href="/theme-editor">Customize your theme</a></li>
 | 
				
			||||||
          <div>→</div>
 | 
					          <div></div>
 | 
				
			||||||
          <li><button id="stepper-build">Generate your static site!</button></li>
 | 
					          <li><button id="stepper-build">Generate your static site!</button></li>
 | 
				
			||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
<!-- Delete thumbnail confirmation modal-->
 | 
					<!-- Delete thumbnail confirmation modal-->
 | 
				
			||||||
  <div id="delete-modal" class="modal" style="display:none;">
 | 
					  <div class="content-inner">
 | 
				
			||||||
    <div class="modal-content">
 | 
					    <div id="delete-modal" class="modal" style="display:none;">
 | 
				
			||||||
      <span id="delete-modal-close" class="modal-close">×</span>
 | 
					      <div class="modal-content">
 | 
				
			||||||
      <h3>Confirm Deletion</h3>
 | 
					        <span id="delete-modal-close" class="modal-close">×</span>
 | 
				
			||||||
      <p id="delete-modal-text">Are you sure you want to remove this thumbnail?</p>
 | 
					        <h3>Confirm Deletion</h3>
 | 
				
			||||||
      <div class="modal-actions">
 | 
					        <p id="delete-modal-text">Are you sure you want to remove this thumbnail?</p>
 | 
				
			||||||
        <button id="delete-modal-confirm" class="modal-btn danger">Remove</button>
 | 
					        <div class="modal-actions">
 | 
				
			||||||
        <button id="delete-modal-cancel" class="modal-btn">Cancel</button>
 | 
					          <button id="delete-modal-confirm" class="modal-btn danger">Remove</button>
 | 
				
			||||||
 | 
					          <button id="delete-modal-cancel" class="modal-btn">Cancel</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <!-- Delete theme confirmation modal -->
 | 
					  <!-- Delete theme confirmation modal -->
 | 
				
			||||||
  <div id="delete-theme-modal" class="modal" style="display:none;">
 | 
					  <div class="content-inner">
 | 
				
			||||||
    <div class="modal-content">
 | 
					    <div id="delete-theme-modal" class="modal" style="display:none;">
 | 
				
			||||||
      <span id="delete-theme-modal-close" class="modal-close">×</span>
 | 
					      <div class="modal-content">
 | 
				
			||||||
      <h3>Confirm Theme Deletion</h3>
 | 
					        <span id="delete-theme-modal-close" class="modal-close">×</span>
 | 
				
			||||||
      <p id="delete-theme-modal-text">Are you sure you want to remove this theme?</p>
 | 
					        <h3>Confirm Theme Deletion</h3>
 | 
				
			||||||
      <div class="modal-actions">
 | 
					        <p id="delete-theme-modal-text">Are you sure you want to remove this theme?</p>
 | 
				
			||||||
        <button id="delete-theme-modal-confirm" class="modal-btn danger">Remove</button>
 | 
					        <div class="modal-actions">
 | 
				
			||||||
        <button id="delete-theme-modal-cancel" class="modal-btn">Cancel</button>
 | 
					          <button id="delete-theme-modal-confirm" class="modal-btn danger">Remove</button>
 | 
				
			||||||
 | 
					          <button id="delete-theme-modal-cancel" class="modal-btn">Cancel</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,13 @@
 | 
				
			|||||||
/* --- Base Styles --- */
 | 
					/* --- Base Styles --- */
 | 
				
			||||||
body {
 | 
					body {
 | 
				
			||||||
  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
 | 
					  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
 | 
				
			||||||
  margin: 20px;
 | 
					 | 
				
			||||||
  background: #111010;
 | 
					  background: #111010;
 | 
				
			||||||
  /* background:radial-gradient(ellipse at bottom center, #002a30, #000000bd), radial-gradient(ellipse at top center, #0558a8, #000000fa); */
 | 
					 | 
				
			||||||
  color: #FBFBFB;
 | 
					  color: #FBFBFB;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
  min-height: 100vh;
 | 
					  min-height: 100vh;
 | 
				
			||||||
  margin:0px;
 | 
					  margin:0px;
 | 
				
			||||||
 | 
					  width: 100vw;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
a {
 | 
					a {
 | 
				
			||||||
@@ -16,15 +15,6 @@ a {
 | 
				
			|||||||
  color: #d3d3d3;
 | 
					  color: #d3d3d3;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.content-inner {
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  margin: 0 auto;
 | 
					 | 
				
			||||||
  max-width: 1140px;
 | 
					 | 
				
			||||||
  padding: 0 40px;
 | 
					 | 
				
			||||||
  padding-top: 70px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
h1, h2 {
 | 
					h1, h2 {
 | 
				
			||||||
  color: #FBFBFB;
 | 
					  color: #FBFBFB;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -32,26 +22,197 @@ h2 {
 | 
				
			|||||||
  color: #55c3ec;
 | 
					  color: #55c3ec;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Toolbar --- */
 | 
					.content-inner {
 | 
				
			||||||
.toolbar {
 | 
					  margin: 0 auto;
 | 
				
			||||||
  margin-bottom: 20px;
 | 
					  max-width: 1140px;
 | 
				
			||||||
 | 
					  padding: 0 40px;
 | 
				
			||||||
 | 
					  padding-top: 70px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.toolbar button {
 | 
					/* --- Navbar & Burger Menu --- */
 | 
				
			||||||
  margin-right: 10px;
 | 
					
 | 
				
			||||||
  padding: 8px 12px;
 | 
					.nav-bar {
 | 
				
			||||||
 | 
					  position: fixed;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  left: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 70px;
 | 
				
			||||||
 | 
					  background: #0c0d0c29;
 | 
				
			||||||
 | 
					  z-index: 1000;
 | 
				
			||||||
 | 
					  backdrop-filter: blur(20px);
 | 
				
			||||||
 | 
					  border-bottom: 1px solid #21212157;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  max-width: 1140px;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  padding: 0 40px;
 | 
				
			||||||
 | 
					  margin: 0 auto;
 | 
				
			||||||
 | 
					  height: 70px;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-header {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-title {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  font-size: 22px;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav img {
 | 
				
			||||||
 | 
					  height: 30px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-links {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-list {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  gap: 0;
 | 
				
			||||||
 | 
					  list-style: none;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-item {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-item a {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					  color:#fff;
 | 
				
			||||||
 | 
					  transition: all 0.2s ease;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-item a:hover {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					  color: #55c3ec;
 | 
				
			||||||
 | 
					  transition: all 0.2s ease;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-list > li + li::before {
 | 
				
			||||||
 | 
					  content: " → ";
 | 
				
			||||||
 | 
					  color: #ffc700;
 | 
				
			||||||
 | 
					  margin: 0 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.button {
 | 
				
			||||||
 | 
					  padding: 10px 25px;
 | 
				
			||||||
 | 
					  border-radius: 40px;
 | 
				
			||||||
 | 
					  margin-left: 10px;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  background: linear-gradient(135deg, #26c4ff, #016074);
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  background-color: #4CAF50;
 | 
					 | 
				
			||||||
  color: white;
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  border-radius: 4px;
 | 
					  transition: background 0.2s;
 | 
				
			||||||
  transition: background-color 0.2s;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.toolbar button:hover {
 | 
					.button:hover {
 | 
				
			||||||
  background-color: #45a049;
 | 
					  background: linear-gradient(135deg, #72d9ff, #26657e);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* --- Burger Menu --- */
 | 
				
			||||||
 | 
					.nav-burger {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  width: 40px;
 | 
				
			||||||
 | 
					  height: 40px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  margin-left: auto;
 | 
				
			||||||
 | 
					  z-index: 1100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-burger span {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  width: 28px;
 | 
				
			||||||
 | 
					  height: 4px;
 | 
				
			||||||
 | 
					  margin: 4px;
 | 
				
			||||||
 | 
					  background: #fff;
 | 
				
			||||||
 | 
					  border-radius: 2px;
 | 
				
			||||||
 | 
					  transition: 0.3s;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* --- Responsive Navbar & Burger --- */
 | 
				
			||||||
 | 
					@media (max-width: 768px) {
 | 
				
			||||||
 | 
					  .nav-burger {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .nav-list > li::before {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .nav-links {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 70px;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					    width: 100vw;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    align-items: flex-start;
 | 
				
			||||||
 | 
					    padding: 24px 0 12px 0;
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					    z-index: 1099;
 | 
				
			||||||
 | 
					    backdrop-filter: blur(20px);
 | 
				
			||||||
 | 
					    background-color: #000000d4
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nav-links .nav-list {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nav-links .nav-item {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nav-links .nav-item a,
 | 
				
			||||||
 | 
					  .nav-links .nav-item button {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    padding: 16px 24px;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    font-size: 18px;
 | 
				
			||||||
 | 
					    border: none;
 | 
				
			||||||
 | 
					    justify-content: center;
 | 
				
			||||||
 | 
					    margin: 0 20px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Show menu when burger is checked */
 | 
				
			||||||
 | 
					  .nav-toggle:checked ~ .nav-burger + .nav-links {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /* Animate burger to X */
 | 
				
			||||||
 | 
					  .nav-toggle:checked ~ .nav-burger span:nth-child(1) {
 | 
				
			||||||
 | 
					    transform: translateY(12px) rotate(45deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nav-toggle:checked ~ .nav-burger span:nth-child(2) {
 | 
				
			||||||
 | 
					    opacity: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nav-toggle:checked ~ .nav-burger span:nth-child(3) {
 | 
				
			||||||
 | 
					    transform: translateY(-12px) rotate(-45deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
/* --- Upload Section --- */
 | 
					/* --- Upload Section --- */
 | 
				
			||||||
.section {
 | 
					.section {
 | 
				
			||||||
  margin-bottom: 30px;
 | 
					  margin-bottom: 30px;
 | 
				
			||||||
@@ -113,22 +274,6 @@ h2 {
 | 
				
			|||||||
  background-color: #d32f2f;
 | 
					  background-color: #d32f2f;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Responsive Adjustments --- */
 | 
					 | 
				
			||||||
@media (max-width: 500px) {
 | 
					 | 
				
			||||||
  body {
 | 
					 | 
				
			||||||
    margin: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .toolbar button {
 | 
					 | 
				
			||||||
    margin-bottom: 8px;
 | 
					 | 
				
			||||||
    width: 100%;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .section label {
 | 
					 | 
				
			||||||
    display: block;
 | 
					 | 
				
			||||||
    margin-bottom: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Toast Notifications --- */
 | 
					/* --- Toast Notifications --- */
 | 
				
			||||||
#toast-container {
 | 
					#toast-container {
 | 
				
			||||||
@@ -165,7 +310,6 @@ h2 {
 | 
				
			|||||||
/* --- Tags --- */
 | 
					/* --- Tags --- */
 | 
				
			||||||
.tag-input {
 | 
					.tag-input {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-wrap: wrap-reverse;
 | 
					 | 
				
			||||||
  align-content: flex-start;
 | 
					  align-content: flex-start;
 | 
				
			||||||
  gap: 4px;
 | 
					  gap: 4px;
 | 
				
			||||||
  padding: 4px;
 | 
					  padding: 4px;
 | 
				
			||||||
@@ -231,6 +375,24 @@ h2 {
 | 
				
			|||||||
  margin-top: 8px;
 | 
					  margin-top: 8px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.photo button.validate-tag-btn {
 | 
				
			||||||
 | 
					    border-radius: 30px;
 | 
				
			||||||
 | 
					    border: none;
 | 
				
			||||||
 | 
					    background: #049b3d;
 | 
				
			||||||
 | 
					    color: #fff;
 | 
				
			||||||
 | 
					    font-size: 10px;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    margin-left: 4px;
 | 
				
			||||||
 | 
					    transition: all ease 0.2s;
 | 
				
			||||||
 | 
					    width: 35px;
 | 
				
			||||||
 | 
					    border: 1px solid #585858;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.photo button.validate-tag-btn:hover {
 | 
				
			||||||
 | 
					  background: #02cb4e;
 | 
				
			||||||
 | 
					} 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.suggestions li.selected {
 | 
					.suggestions li.selected {
 | 
				
			||||||
  background-color: #007782;
 | 
					  background-color: #007782;
 | 
				
			||||||
  color: white;
 | 
					  color: white;
 | 
				
			||||||
@@ -259,149 +421,7 @@ h2 {
 | 
				
			|||||||
  width: 100%
 | 
					  width: 100%
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Top Bar & Navigation --- */
 | 
					/* --- Upload Buttons --- */
 | 
				
			||||||
.nav {
 | 
					 | 
				
			||||||
  height: 100%;
 | 
					 | 
				
			||||||
  max-width: 1140px;
 | 
					 | 
				
			||||||
  padding: 0 40px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-bar {
 | 
					 | 
				
			||||||
  position: fixed;
 | 
					 | 
				
			||||||
  top: 0;
 | 
					 | 
				
			||||||
  left: 0;
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  height: 70px;
 | 
					 | 
				
			||||||
  background-color: #0c0d0c29;
 | 
					 | 
				
			||||||
  z-index: 1000;
 | 
					 | 
				
			||||||
  backdrop-filter: blur(20px);
 | 
					 | 
				
			||||||
  border-bottom: 1px solid #21212157;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav img {
 | 
					 | 
				
			||||||
  height: 30px;
 | 
					 | 
				
			||||||
  padding: 0px;
 | 
					 | 
				
			||||||
  margin-top: 10px;
 | 
					 | 
				
			||||||
} 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > .nav-header {
 | 
					 | 
				
			||||||
  display: inline;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > .nav-header > .nav-title {
 | 
					 | 
				
			||||||
  display: inline-block;
 | 
					 | 
				
			||||||
  font-size: 22px;
 | 
					 | 
				
			||||||
  color: #fff;
 | 
					 | 
				
			||||||
  padding: 0;
 | 
					 | 
				
			||||||
  margin-top: 10px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > .nav-btn {
 | 
					 | 
				
			||||||
  display: none;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-btn label {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
  justify-content: center;
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					 | 
				
			||||||
  width: 32px;
 | 
					 | 
				
			||||||
  height: 32px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > .nav-links {
 | 
					 | 
				
			||||||
  display: inline;
 | 
					 | 
				
			||||||
  float: right;
 | 
					 | 
				
			||||||
  font-size: 14px;
 | 
					 | 
				
			||||||
  height: 100%;
 | 
					 | 
				
			||||||
  line-height: 70px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-item {
 | 
					 | 
				
			||||||
  display: inline;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-list {
 | 
					 | 
				
			||||||
  list-style-type: disc;
 | 
					 | 
				
			||||||
  margin: 0px;
 | 
					 | 
				
			||||||
  padding: 0px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > .nav-links > .nav-list > .nav-item > a {
 | 
					 | 
				
			||||||
  display: inline-block;
 | 
					 | 
				
			||||||
  padding: 0px 15px 0px 15px;
 | 
					 | 
				
			||||||
  text-decoration: none;
 | 
					 | 
				
			||||||
  height: 100%;
 | 
					 | 
				
			||||||
  font-weight: 700;
 | 
					 | 
				
			||||||
  color:#fff
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > .nav-links > .nav-list > .nav-item > a:hover {
 | 
					 | 
				
			||||||
  color: #55c3ec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > .nav-links > .nav-list > .nav-item > a:active {
 | 
					 | 
				
			||||||
  color: #55c3ec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav > #nav-check {
 | 
					 | 
				
			||||||
  display: none;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-list > li + li::before{
 | 
					 | 
				
			||||||
  content: " → ";
 | 
					 | 
				
			||||||
  color: #ffc700;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-cta {
 | 
					 | 
				
			||||||
  display: inline;
 | 
					 | 
				
			||||||
  float: right;
 | 
					 | 
				
			||||||
  height: 70px;
 | 
					 | 
				
			||||||
  line-height: 70px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-cta > .arrow {
 | 
					 | 
				
			||||||
  font-size: 12px;
 | 
					 | 
				
			||||||
  display: inline;
 | 
					 | 
				
			||||||
  color: #ffc700;
 | 
					 | 
				
			||||||
  font-weight: 700;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-bar .button {
 | 
					 | 
				
			||||||
  padding: 10px 25px;
 | 
					 | 
				
			||||||
  border-radius: 40px;
 | 
					 | 
				
			||||||
  margin: 15px 20px 15px 10px;
 | 
					 | 
				
			||||||
  font-size: 14px;
 | 
					 | 
				
			||||||
  display: inline;
 | 
					 | 
				
			||||||
  background: linear-gradient(135deg, #26c4ff, #016074);
 | 
					 | 
				
			||||||
  transition: all 0.2s ease;
 | 
					 | 
				
			||||||
  text-decoration: none;
 | 
					 | 
				
			||||||
  color: #fff;
 | 
					 | 
				
			||||||
  font-weight: 700;
 | 
					 | 
				
			||||||
  border: none;
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-bar .button:hover {
 | 
					 | 
				
			||||||
  background: linear-gradient(135deg, #72d9ff, #26657e);
 | 
					 | 
				
			||||||
  transition: all 0.2s ease;
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-links > ul {
 | 
					 | 
				
			||||||
  display: inline-block;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.nav-btn span {
 | 
					 | 
				
			||||||
  display: block;
 | 
					 | 
				
			||||||
  height: 4px;
 | 
					 | 
				
			||||||
  width: 28px;
 | 
					 | 
				
			||||||
  background: #fff;
 | 
					 | 
				
			||||||
  margin: 4px 0;
 | 
					 | 
				
			||||||
  border-radius: 2px;
 | 
					 | 
				
			||||||
  transition: all 0.3s;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
/* --- Custom Upload Buttons --- */
 | 
					 | 
				
			||||||
.up-btn, .footer-links a{
 | 
					.up-btn, .footer-links a{
 | 
				
			||||||
  display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
  background: #00000000;
 | 
					  background: #00000000;
 | 
				
			||||||
@@ -413,7 +433,6 @@ h2 {
 | 
				
			|||||||
  text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
  transition: all 0.1s ease;
 | 
					  transition: all 0.1s ease;
 | 
				
			||||||
  user-select: none;
 | 
					  user-select: none;
 | 
				
			||||||
  /* box-shadow: 0 4px 10px rgba(0,0,0,0.25);*/
 | 
					 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  border: 1px solid #585858;
 | 
					  border: 1px solid #585858;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -430,18 +449,20 @@ h2 {
 | 
				
			|||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  margin: 0 30px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.modal-content {
 | 
					.modal-content {
 | 
				
			||||||
  background: #131313;
 | 
					  background: #000000a3;
 | 
				
			||||||
  color: #fff;
 | 
					  color: #fff;
 | 
				
			||||||
  padding: 2rem 2.5rem;
 | 
					  padding: 2rem 2.5rem;
 | 
				
			||||||
  border-radius: 10px;
 | 
					  border-radius: 10px;
 | 
				
			||||||
  box-shadow: 0 4px 24px rgba(0,0,0,0.25);
 | 
					  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.25);
 | 
				
			||||||
  min-width: 300px;
 | 
					  min-width: 200px;
 | 
				
			||||||
  max-width: 90vw;
 | 
					  max-width: 90vw;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  backdrop-filter: blur(20px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.modal-close {
 | 
					.modal-close {
 | 
				
			||||||
@@ -493,11 +514,12 @@ h2 {
 | 
				
			|||||||
  margin-bottom: 10px;
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Remove All Buttons */
 | 
					/* --- Remove All Buttons --- */
 | 
				
			||||||
#remove-all-hero, #remove-all-gallery {
 | 
					#remove-all-hero, #remove-all-gallery {
 | 
				
			||||||
  background: #2d2d2d;
 | 
					  background: #2d2d2d;
 | 
				
			||||||
  color: white;
 | 
					  color: white;
 | 
				
			||||||
  display: none;
 | 
					  display: none;
 | 
				
			||||||
 | 
					  margin-bottom: 6px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#remove-all-gallery:hover,
 | 
					#remove-all-gallery:hover,
 | 
				
			||||||
@@ -505,7 +527,6 @@ h2 {
 | 
				
			|||||||
  background: rgb(121, 26, 19);
 | 
					  background: rgb(121, 26, 19);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Responsive: stack buttons vertically on small screens */
 | 
					 | 
				
			||||||
@media (max-width: 500px) {
 | 
					@media (max-width: 500px) {
 | 
				
			||||||
  .upload-actions-row {
 | 
					  .upload-actions-row {
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
@@ -514,8 +535,7 @@ h2 {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Site Info --- */
 | 
					/* --- Forms --- */
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
fieldset {
 | 
					fieldset {
 | 
				
			||||||
  background-color: rgb(67 67 67 / 26%);
 | 
					  background-color: rgb(67 67 67 / 26%);
 | 
				
			||||||
@@ -543,7 +563,7 @@ legend {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.input-field {
 | 
					.input-field {
 | 
				
			||||||
  flex: 1 1 calc(33.333% - 18px);
 | 
					  flex: 1 1 calc(33.333% - 18px);
 | 
				
			||||||
  min-width: 220px;
 | 
					  min-width: 150px;
 | 
				
			||||||
  max-width: 100%;
 | 
					  max-width: 100%;
 | 
				
			||||||
  box-sizing: border-box;
 | 
					  box-sizing: border-box;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
@@ -575,6 +595,16 @@ label {
 | 
				
			|||||||
  box-shadow: 0 2px 8px rgba(0,0,0,0.07);
 | 
					  box-shadow: 0 2px 8px rgba(0,0,0,0.07);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#theme-editor-form input, #theme-editor-form textarea,#theme-editor-form select {
 | 
				
			||||||
 | 
					  margin-bottom: 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#theme-editor-form .fields {
 | 
				
			||||||
 | 
					  gap: 0 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#site-info-form input::placeholder,
 | 
					#site-info-form input::placeholder,
 | 
				
			||||||
#theme-editor-form input::placeholder,
 | 
					#theme-editor-form input::placeholder,
 | 
				
			||||||
#site-info-form textarea::placeholder,
 | 
					#site-info-form textarea::placeholder,
 | 
				
			||||||
@@ -634,7 +664,7 @@ img#thumbnail-preview {
 | 
				
			|||||||
  background: #00000000;
 | 
					  background: #00000000;
 | 
				
			||||||
  color: #fff;
 | 
					  color: #fff;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  border-radius: 18px;
 | 
					  border-radius: 30px;
 | 
				
			||||||
  padding: 7px 18px;
 | 
					  padding: 7px 18px;
 | 
				
			||||||
  font-size: 0.98em;
 | 
					  font-size: 0.98em;
 | 
				
			||||||
  margin-top: 8px;
 | 
					  margin-top: 8px;
 | 
				
			||||||
@@ -648,20 +678,6 @@ img#thumbnail-preview {
 | 
				
			|||||||
  color: #fff;
 | 
					  color: #fff;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media (max-width: 900px) {
 | 
					 | 
				
			||||||
  #site-info-form, #theme-editor-form {
 | 
					 | 
				
			||||||
    padding: 18px 8px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .fields,
 | 
					 | 
				
			||||||
  fieldset {
 | 
					 | 
				
			||||||
    flex-direction: column;
 | 
					 | 
				
			||||||
    gap: 0;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .input-field {
 | 
					 | 
				
			||||||
    min-width: 100%;
 | 
					 | 
				
			||||||
    margin-bottom: 12px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#site-info-form button.remove-menu-item, #site-info-form button.remove-ip-paragraph, #theme-editor-form button.remove-menu-item, #theme-editor-form button.remove-ip-paragraph {
 | 
					#site-info-form button.remove-menu-item, #site-info-form button.remove-ip-paragraph, #theme-editor-form button.remove-menu-item, #theme-editor-form button.remove-ip-paragraph {
 | 
				
			||||||
  margin-top: 0px;
 | 
					  margin-top: 0px;
 | 
				
			||||||
@@ -745,13 +761,13 @@ fieldset p, .section p {
 | 
				
			|||||||
  margin-top: 16px;
 | 
					  margin-top: 16px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Home --- */
 | 
					/* --- Stepper --- */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#stepper {
 | 
					#stepper {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  gap: 18px;
 | 
					  gap: 18px;
 | 
				
			||||||
  flex-wrap: nowrap;
 | 
					  flex-wrap: nowrap;
 | 
				
			||||||
  align-items: stretch; /* Ensures all children match tallest height */
 | 
					  align-items: stretch;
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  margin-left: auto;
 | 
					  margin-left: auto;
 | 
				
			||||||
  margin-right: auto;
 | 
					  margin-right: auto;
 | 
				
			||||||
@@ -764,6 +780,11 @@ fieldset p, .section p {
 | 
				
			|||||||
  
 | 
					  
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  #stepper > div::before {
 | 
				
			||||||
 | 
					    content: "→";
 | 
				
			||||||
 | 
					    color: #ffc700;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#stepper li a, #stepper li button, #stepper > div  {
 | 
					#stepper li a, #stepper li button, #stepper > div  {
 | 
				
			||||||
justify-content: center;
 | 
					justify-content: center;
 | 
				
			||||||
  min-width: 100px;
 | 
					  min-width: 100px;
 | 
				
			||||||
@@ -816,11 +837,11 @@ justify-content: center;
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#stepper li {
 | 
					#stepper li {
 | 
				
			||||||
  flex: 1 1 auto; /* li can grow/shrink, width is flexible */
 | 
					  flex: 1 1 auto; 
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#stepper > div {
 | 
					#stepper > div {
 | 
				
			||||||
  flex: 0 0 auto; /* Do not grow or shrink, width is auto */
 | 
					  flex: 0 0 auto;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
@@ -828,10 +849,12 @@ justify-content: center;
 | 
				
			|||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  background: none;
 | 
					  background: none;
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  min-width: 0; /* Prevent unwanted minimum width */
 | 
					  min-width: 0;
 | 
				
			||||||
  width: auto;   /* Let width fit content */
 | 
					  width: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* --- Footer --- */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#footer {
 | 
					#footer {
 | 
				
			||||||
  background-color: #0c0d0c29;
 | 
					  background-color: #0c0d0c29;
 | 
				
			||||||
  z-index: 1000;
 | 
					  z-index: 1000;
 | 
				
			||||||
@@ -889,3 +912,106 @@ justify-content: center;
 | 
				
			|||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* --- Global Loader & Spinner --- */
 | 
				
			||||||
 | 
					#global-loader {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					  position: fixed;
 | 
				
			||||||
 | 
					  top: 0; left: 0; width: 100vw; height: 100vh;
 | 
				
			||||||
 | 
					  z-index: 99999;
 | 
				
			||||||
 | 
					  background: rgba(0,0,0,0.4);
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#global-loader.active {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loader-inner {
 | 
				
			||||||
 | 
					  opacity: 0;
 | 
				
			||||||
 | 
					  transition: opacity 0.9s cubic-bezier(.4,0,.2,1);
 | 
				
			||||||
 | 
					  background: #0c0d0c29;
 | 
				
			||||||
 | 
					  padding: 32px 48px;
 | 
				
			||||||
 | 
					  border-radius: 16px;
 | 
				
			||||||
 | 
					  box-shadow: 0 2px 24px #000;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  backdrop-filter: blur(20px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#global-loader.active .loader-inner {
 | 
				
			||||||
 | 
					  opacity: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loader-spinner {
 | 
				
			||||||
 | 
					  width: 48px;
 | 
				
			||||||
 | 
					  height: 48px;
 | 
				
			||||||
 | 
					  border: 6px solid #55c3ec;
 | 
				
			||||||
 | 
					  border-top: 6px solid #222;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  animation: spin 1s linear infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#loader-text {
 | 
				
			||||||
 | 
					  margin-top: 18px;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  font-size: 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes spin { 100% { transform: rotate(360deg); } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* --- Responsive Adjustments --- */
 | 
				
			||||||
 | 
					@media (max-width: 768px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .content-inner {
 | 
				
			||||||
 | 
					    padding: 70px 20px
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .section label {
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  #menu-items-list > div {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  #stepper {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  #stepper li {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .footer-container, .footer-links {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  #ip-list > div {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  #stepper > div {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  #stepper > div::before {
 | 
				
			||||||
 | 
					    content: "↓";
 | 
				
			||||||
 | 
					    color: #ffc700;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  #stepper > div {
 | 
				
			||||||
 | 
					    /* Hide the default arrow */
 | 
				
			||||||
 | 
					    color: transparent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  #site-info-form, #theme-editor-form {
 | 
				
			||||||
 | 
					    padding: 18px 8px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .input-field {
 | 
				
			||||||
 | 
					    min-width: 100%;
 | 
				
			||||||
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  #color-picker .input-field{
 | 
				
			||||||
 | 
					    min-width: 140px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,45 +9,46 @@
 | 
				
			|||||||
    <body>
 | 
					    <body>
 | 
				
			||||||
    <!-- Top bar -->	
 | 
					    <!-- Top bar -->	
 | 
				
			||||||
    <div class="nav-bar">
 | 
					    <div class="nav-bar">
 | 
				
			||||||
        <div class="content-inner nav">
 | 
					    <div class="content-inner nav">
 | 
				
			||||||
        <input type="checkbox" id="nav-check">
 | 
					 | 
				
			||||||
        <div class="nav-header">
 | 
					        <div class="nav-header">
 | 
				
			||||||
            <div class="nav-title">
 | 
					            <a href="/" class="nav-title">
 | 
				
			||||||
            <a href="/"><img src="{{ url_for('static', filename='img/logo.svg') }}"></a>
 | 
					                <img src="{{ url_for('static', filename='img/logo.svg') }}">
 | 
				
			||||||
            </div>
 | 
					            </a>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="nav-btn">
 | 
					        <!-- Burger toggle input and label -->
 | 
				
			||||||
            <label for="nav-check">
 | 
					        <input type="checkbox" id="nav-toggle" class="nav-toggle" hidden>
 | 
				
			||||||
 | 
					        <label for="nav-toggle" class="nav-burger">
 | 
				
			||||||
            <span></span>
 | 
					            <span></span>
 | 
				
			||||||
            <span></span>
 | 
					            <span></span>
 | 
				
			||||||
            <span></span>
 | 
					            <span></span>
 | 
				
			||||||
            </label>
 | 
					        </label>
 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="nav-links">
 | 
					        <div class="nav-links">
 | 
				
			||||||
            <ul class="nav-list">
 | 
					            <ul class="nav-list">
 | 
				
			||||||
            <li class="nav-item"><a href="/gallery-editor">Gallery</a></li>
 | 
					                <li class="nav-item"><a href="/gallery-editor">Gallery</a></li>
 | 
				
			||||||
            <li class="nav-item"><a href="/site-info">Site info</a></li>
 | 
					                <li class="nav-item"><a href="/site-info">Site info</a></li>
 | 
				
			||||||
            <li class="nav-item"><a href="/theme-editor">Theme info</a></li>
 | 
					                <li class="nav-item"><a href="/theme-editor">Theme info</a></li>
 | 
				
			||||||
            <li class="nav-item">
 | 
					                <li class="nav-item">
 | 
				
			||||||
                <button id="build-btn" class="button">🚀 Build !</button>
 | 
					                    <button id="build-btn" class="button">🚀 Build!</button>
 | 
				
			||||||
            </li>
 | 
					                </li>
 | 
				
			||||||
            </ul>
 | 
					            </ul>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
    <!-- Toast container for notifications -->
 | 
					    <!-- Toast container for notifications -->
 | 
				
			||||||
    <div class="content-inner first-content">
 | 
					    <div class="content-inner first-content">
 | 
				
			||||||
        <div id="toast-container"></div>
 | 
					        <div id="toast-container"></div>
 | 
				
			||||||
        <!-- Page content -->
 | 
					        <!-- Page content -->
 | 
				
			||||||
        {% block content %}{% endblock %}
 | 
					        {% block content %}{% endblock %}
 | 
				
			||||||
        <!-- Build success modal -->
 | 
					        <!-- Build success modal -->
 | 
				
			||||||
        <div id="build-success-modal" class="modal" style="display:none;">
 | 
					        <div class="content-inner">
 | 
				
			||||||
            <div class="modal-content">
 | 
					            <div id="build-success-modal" class="modal" style="display:none;">
 | 
				
			||||||
                <span id="build-success-modal-close" class="modal-close">×</span>
 | 
					                <div class="modal-content">
 | 
				
			||||||
                <h3>✅ Build completed!</h3>
 | 
					                    <span id="build-success-modal-close" class="modal-close">×</span>
 | 
				
			||||||
                <p>Your files are available in the output folder.</p>
 | 
					                    <h3>✅ Build completed!</h3>
 | 
				
			||||||
                <button id="download-zip-btn" class="modal-btn">Download ZIP</button>
 | 
					                    <p>Your files are available in the output folder.</p>
 | 
				
			||||||
                <div id="zip-loader" style="display:none;">Creating ZIP...</div>
 | 
					                    <button id="download-zip-btn" class="modal-btn">Download ZIP</button>
 | 
				
			||||||
 | 
					                    <div id="zip-loader" style="display:none;">Creating ZIP...</div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <!-- Footer -->
 | 
					        <!-- Footer -->
 | 
				
			||||||
@@ -65,6 +66,13 @@
 | 
				
			|||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <!-- Loader -->
 | 
				
			||||||
 | 
					        <div id="global-loader">
 | 
				
			||||||
 | 
					            <div class="loader-inner">
 | 
				
			||||||
 | 
					                <div class="loader-spinner"></div>
 | 
				
			||||||
 | 
					                <div id="loader-text">Uploading...</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
        <!-- Scripts -->
 | 
					        <!-- Scripts -->
 | 
				
			||||||
        <script src="{{ url_for('static', filename='js/build.js') }}" defer></script>
 | 
					        <script src="{{ url_for('static', filename='js/build.js') }}" defer></script>
 | 
				
			||||||
        {% block scripts %}{% endblock %}
 | 
					        {% block scripts %}{% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <form id="theme-editor-form">
 | 
					    <form id="theme-editor-form">
 | 
				
			||||||
      <!-- Colors Section -->
 | 
					      <!-- Colors Section -->
 | 
				
			||||||
      <fieldset>
 | 
					      <fieldset id="color-picker">
 | 
				
			||||||
        <h2>Colors</h2>
 | 
					        <h2>Colors</h2>
 | 
				
			||||||
        <p>Set the color values for your theme</p>
 | 
					        <p>Set the color values for your theme</p>
 | 
				
			||||||
        <div class="fields">
 | 
					        <div class="fields">
 | 
				
			||||||
@@ -140,11 +140,11 @@
 | 
				
			|||||||
      <p> Follow the steps to generate your static gallery</p>
 | 
					      <p> Follow the steps to generate your static gallery</p>
 | 
				
			||||||
      <ul id="stepper">
 | 
					      <ul id="stepper">
 | 
				
			||||||
        <li><a href="/gallery-editor">Upload your photos</a></li>
 | 
					        <li><a href="/gallery-editor">Upload your photos</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><a href="/site-info">Configure site info</a></li>
 | 
					        <li><a href="/site-info">Configure site info</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><a class="step-active" href="/theme-editor">Customize your theme</a></li>
 | 
					        <li><a class="step-active" href="/theme-editor">Customize your theme</a></li>
 | 
				
			||||||
        <div>→</div>
 | 
					        <div></div>
 | 
				
			||||||
        <li><button id="stepper-build">Generate your static site!</button></li>
 | 
					        <li><button id="stepper-build">Generate your static site!</button></li>
 | 
				
			||||||
      </ul>
 | 
					      </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user