# Goondex HTML/CSS Templates Guide

**For Frontend Developers - HTML/CSS Focus**

This guide provides complete, ready-to-use HTML templates with Bootstrap styling. All JavaScript is included as simple copy-paste snippets with detailed comments.

## Getting Started

All you need to know:
1. Copy the HTML template you need
2. The JavaScript is already included in `<script>` tags - no modifications needed
3. Customize the CSS/Bootstrap classes for styling
4. Use Bootstrap 5 components for quick UI elements

---

## Table of Contents

1. [Complete Search Page](#complete-search-page)
2. [Performer Import Form](#performer-import-form)
3. [Bulk Import with Progress](#bulk-import-with-progress)
4. [Simple Data Display](#simple-data-display)
5. [Bootstrap Component Reference](#bootstrap-component-reference)

---

## Complete Search Page

**File: `search.html`**

Copy this entire file - it's ready to use!

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Goondex - Search</title>

  <!-- Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">

  <!-- Custom CSS -->
  <style>
    body {
      background-color: #f8f9fa;
    }

    .search-container {
      max-width: 1200px;
      margin: 0 auto;
      padding: 2rem;
    }

    .performer-card {
      transition: transform 0.2s;
      height: 100%;
    }

    .performer-card:hover {
      transform: translateY(-5px);
      box-shadow: 0 4px 8px rgba(0,0,0,0.2);
    }

    .performer-image {
      height: 300px;
      object-fit: cover;
    }

    .no-results {
      text-align: center;
      padding: 3rem;
      color: #6c757d;
    }
  </style>
</head>
<body>

  <!-- Navigation -->
  <nav class="navbar navbar-dark bg-dark">
    <div class="container-fluid">
      <a class="navbar-brand" href="/">Goondex</a>
    </div>
  </nav>

  <!-- Main Content -->
  <div class="search-container">
    <h1 class="mb-4">Search</h1>

    <!-- Search Form -->
    <form id="search-form" class="mb-5">
      <div class="input-group input-group-lg">
        <input
          type="text"
          id="search-input"
          class="form-control"
          placeholder="Search performers, studios, scenes..."
          required
        >
        <button type="submit" class="btn btn-primary px-5">
          Search
        </button>
      </div>
    </form>

    <!-- Loading Spinner (hidden by default) -->
    <div id="loading" class="text-center" style="display: none;">
      <div class="spinner-border text-primary" role="status">
        <span class="visually-hidden">Loading...</span>
      </div>
      <p class="mt-2">Searching...</p>
    </div>

    <!-- Results Container -->
    <div id="results"></div>
  </div>

  <!-- JavaScript -->
  <script>
    // Configuration - change this if your server runs on a different port
    const API_BASE = 'http://localhost:8080';

    // Get elements
    const form = document.getElementById('search-form');
    const input = document.getElementById('search-input');
    const loading = document.getElementById('loading');
    const results = document.getElementById('results');

    // When form is submitted
    form.addEventListener('submit', async function(event) {
      event.preventDefault(); // Don't reload the page

      const searchTerm = input.value.trim();
      if (!searchTerm) return;

      // Show loading, hide results
      loading.style.display = 'block';
      results.innerHTML = '';

      try {
        // Make API request
        const response = await fetch(
          `${API_BASE}/api/search?q=${encodeURIComponent(searchTerm)}`
        );
        const data = await response.json();

        // Hide loading
        loading.style.display = 'none';

        // Check if successful
        if (!data.success) {
          results.innerHTML = `
            <div class="alert alert-danger">
              ${data.message}
            </div>
          `;
          return;
        }

        // Display results
        displayResults(data.data);

      } catch (error) {
        loading.style.display = 'none';
        results.innerHTML = `
          <div class="alert alert-danger">
            Error: ${error.message}
          </div>
        `;
      }
    });

    // Function to display search results
    function displayResults(data) {
      const totalResults = data.total;

      if (totalResults === 0) {
        results.innerHTML = `
          <div class="no-results">
            <h3>No results found</h3>
            <p>Try a different search term</p>
          </div>
        `;
        return;
      }

      let html = `<h2 class="mb-4">${totalResults} Results Found</h2>`;

      // Performers Section
      if (data.performers && data.performers.length > 0) {
        html += `
          <h3 class="mt-4 mb-3">Performers (${data.performers.length})</h3>
          <div class="row row-cols-1 row-cols-md-3 row-cols-lg-4 g-4 mb-5">
        `;

        data.performers.forEach(performer => {
          const imageUrl = performer.image_url || '/static/placeholder.jpg';
          const nationality = performer.nationality ? getFlagEmoji(performer.nationality) : '';

          html += `
            <div class="col">
              <div class="card performer-card">
                <img
                  src="${imageUrl}"
                  class="card-img-top performer-image"
                  alt="${performer.name}"
                  onerror="this.src='/static/placeholder.jpg'"
                >
                <div class="card-body">
                  <h5 class="card-title">${performer.name}</h5>
                  <p class="card-text">
                    ${nationality} ${performer.gender || ''}
                  </p>
                  <a href="/performers/${performer.id}" class="btn btn-sm btn-primary">
                    View Profile
                  </a>
                </div>
              </div>
            </div>
          `;
        });

        html += `</div>`;
      }

      // Studios Section
      if (data.studios && data.studios.length > 0) {
        html += `
          <h3 class="mt-4 mb-3">Studios (${data.studios.length})</h3>
          <div class="list-group mb-5">
        `;

        data.studios.forEach(studio => {
          html += `
            <a href="/studios/${studio.id}" class="list-group-item list-group-item-action">
              <div class="d-flex w-100 justify-content-between">
                <h5 class="mb-1">${studio.name}</h5>
              </div>
              ${studio.description ? `<p class="mb-1">${studio.description}</p>` : ''}
            </a>
          `;
        });

        html += `</div>`;
      }

      // Scenes Section
      if (data.scenes && data.scenes.length > 0) {
        html += `
          <h3 class="mt-4 mb-3">Scenes (${data.scenes.length})</h3>
          <div class="list-group mb-5">
        `;

        data.scenes.forEach(scene => {
          html += `
            <a href="/scenes/${scene.id}" class="list-group-item list-group-item-action">
              <div class="d-flex w-100 justify-content-between">
                <h5 class="mb-1">${scene.title}</h5>
                ${scene.date ? `<small>${scene.date}</small>` : ''}
              </div>
              ${scene.description ? `<p class="mb-1">${scene.description.substring(0, 200)}...</p>` : ''}
            </a>
          `;
        });

        html += `</div>`;
      }

      results.innerHTML = html;
    }

    // Helper function: Convert country code to flag emoji
    function getFlagEmoji(countryCode) {
      const codePoints = countryCode
        .toUpperCase()
        .split('')
        .map(char => 127397 + char.charCodeAt());
      return String.fromCodePoint(...codePoints);
    }
  </script>

</body>
</html>
```

---

## Performer Import Form

**File: `import-performer.html`**

Simple form to search and import performers from TPDB.

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Import Performer - Goondex</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">

  <style>
    .import-container {
      max-width: 600px;
      margin: 3rem auto;
      padding: 2rem;
    }

    .success-message {
      animation: slideDown 0.3s ease-out;
    }

    @keyframes slideDown {
      from {
        opacity: 0;
        transform: translateY(-10px);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
  </style>
</head>
<body>

  <nav class="navbar navbar-dark bg-dark">
    <div class="container-fluid">
      <a class="navbar-brand" href="/">Goondex</a>
    </div>
  </nav>

  <div class="import-container">
    <h1 class="mb-4">Import Performer</h1>

    <div class="card">
      <div class="card-body">
        <p class="card-text text-muted">
          Search for a performer by name. All matching results from TPDB will be imported.
        </p>

        <form id="import-form">
          <div class="mb-3">
            <label for="performer-name" class="form-label">Performer Name</label>
            <input
              type="text"
              class="form-control form-control-lg"
              id="performer-name"
              placeholder="Enter performer name..."
              required
            >
          </div>

          <button type="submit" class="btn btn-primary btn-lg w-100" id="submit-btn">
            <span id="button-text">Import Performer</span>
            <span id="button-spinner" class="spinner-border spinner-border-sm" style="display: none;"></span>
          </button>
        </form>

        <!-- Result message (hidden by default) -->
        <div id="result-message" class="mt-3" style="display: none;"></div>
      </div>
    </div>
  </div>

  <script>
    const API_BASE = 'http://localhost:8080';

    const form = document.getElementById('import-form');
    const input = document.getElementById('performer-name');
    const submitBtn = document.getElementById('submit-btn');
    const buttonText = document.getElementById('button-text');
    const buttonSpinner = document.getElementById('button-spinner');
    const resultMessage = document.getElementById('result-message');

    form.addEventListener('submit', async function(event) {
      event.preventDefault();

      const performerName = input.value.trim();
      if (!performerName) return;

      // Show loading state
      submitBtn.disabled = true;
      buttonText.textContent = 'Importing...';
      buttonSpinner.style.display = 'inline-block';
      resultMessage.style.display = 'none';

      try {
        // Make API request
        const response = await fetch(`${API_BASE}/api/import/performer`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            query: performerName
          })
        });

        const data = await response.json();

        // Reset button
        submitBtn.disabled = false;
        buttonText.textContent = 'Import Performer';
        buttonSpinner.style.display = 'none';

        // Show result
        if (data.success) {
          resultMessage.className = 'alert alert-success success-message mt-3';
          resultMessage.innerHTML = `
            <strong>Success!</strong> ${data.message}<br>
            <small>Imported ${data.data.imported} out of ${data.data.found} found</small>
          `;
          resultMessage.style.display = 'block';

          // Clear form
          input.value = '';

        } else {
          resultMessage.className = 'alert alert-danger mt-3';
          resultMessage.innerHTML = `<strong>Error:</strong> ${data.message}`;
          resultMessage.style.display = 'block';
        }

      } catch (error) {
        submitBtn.disabled = false;
        buttonText.textContent = 'Import Performer';
        buttonSpinner.style.display = 'none';

        resultMessage.className = 'alert alert-danger mt-3';
        resultMessage.innerHTML = `<strong>Error:</strong> ${error.message}`;
        resultMessage.style.display = 'block';
      }
    });
  </script>

</body>
</html>
```

---

## Bulk Import with Progress

**File: `bulk-import.html`**

Import all performers with a real-time progress bar.

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Bulk Import - Goondex</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">

  <style>
    .import-container {
      max-width: 800px;
      margin: 3rem auto;
      padding: 2rem;
    }

    .progress {
      height: 30px;
    }

    .progress-bar {
      font-size: 14px;
      line-height: 30px;
    }

    .import-stats {
      display: none;
      margin-top: 1rem;
    }

    .stat-box {
      text-align: center;
      padding: 1rem;
      background: #f8f9fa;
      border-radius: 8px;
    }

    .stat-number {
      font-size: 2rem;
      font-weight: bold;
      color: #0d6efd;
    }

    .stat-label {
      color: #6c757d;
      font-size: 0.875rem;
    }
  </style>
</head>
<body>

  <nav class="navbar navbar-dark bg-dark">
    <div class="container-fluid">
      <a class="navbar-brand" href="/">Goondex</a>
    </div>
  </nav>

  <div class="import-container">
    <h1 class="mb-4">Bulk Import Performers</h1>

    <div class="card">
      <div class="card-body">
        <p class="text-muted">
          Import all performers from your database. This will fetch full metadata from TPDB.
        </p>

        <!-- Start Button -->
        <button id="start-btn" class="btn btn-primary btn-lg w-100 mb-4">
          Start Import
        </button>

        <!-- Progress Bar (hidden initially) -->
        <div id="progress-container" style="display: none;">
          <div class="progress mb-2">
            <div
              id="progress-bar"
              class="progress-bar progress-bar-striped progress-bar-animated"
              role="progressbar"
              style="width: 0%"
            >
              0%
            </div>
          </div>

          <div id="status-text" class="alert alert-info">
            Preparing to import...
          </div>

          <!-- Statistics (shown on completion) -->
          <div id="import-stats" class="import-stats">
            <div class="row g-3">
              <div class="col-md-3">
                <div class="stat-box">
                  <div id="stat-total" class="stat-number">0</div>
                  <div class="stat-label">Total</div>
                </div>
              </div>
              <div class="col-md-3">
                <div class="stat-box">
                  <div id="stat-imported" class="stat-number">0</div>
                  <div class="stat-label">Imported</div>
                </div>
              </div>
              <div class="col-md-3">
                <div class="stat-box">
                  <div id="stat-skipped" class="stat-number">0</div>
                  <div class="stat-label">Skipped</div>
                </div>
              </div>
              <div class="col-md-3">
                <div class="stat-box">
                  <div id="stat-errors" class="stat-number">0</div>
                  <div class="stat-label">Errors</div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    const API_BASE = 'http://localhost:8080';

    const startBtn = document.getElementById('start-btn');
    const progressContainer = document.getElementById('progress-container');
    const progressBar = document.getElementById('progress-bar');
    const statusText = document.getElementById('status-text');
    const importStats = document.getElementById('import-stats');

    startBtn.addEventListener('click', function() {
      // Hide button, show progress
      startBtn.style.display = 'none';
      progressContainer.style.display = 'block';
      importStats.style.display = 'none';

      // Create EventSource to listen for progress updates
      const eventSource = new EventSource(
        `${API_BASE}/api/import/all-performers/progress`
      );

      // When we receive a message
      eventSource.onmessage = function(event) {
        const update = JSON.parse(event.data);

        // Check for error
        if (update.error) {
          statusText.className = 'alert alert-danger';
          statusText.textContent = `Error: ${update.error}`;
          progressBar.className = 'progress-bar bg-danger';
          progressBar.style.width = '100%';
          progressBar.textContent = 'Error';
          startBtn.style.display = 'block';
          eventSource.close();
          return;
        }

        // Check for completion
        if (update.complete) {
          const result = update.result;

          statusText.className = 'alert alert-success';
          statusText.innerHTML = `
            <strong>Import Complete!</strong><br>
            Successfully imported ${result.imported} out of ${result.total} performers
          `;

          progressBar.className = 'progress-bar bg-success';
          progressBar.style.width = '100%';
          progressBar.textContent = '100%';

          // Show statistics
          document.getElementById('stat-total').textContent = result.total;
          document.getElementById('stat-imported').textContent = result.imported;
          document.getElementById('stat-skipped').textContent = result.skipped;
          document.getElementById('stat-errors').textContent = result.errors;
          importStats.style.display = 'block';

          startBtn.textContent = 'Import Again';
          startBtn.style.display = 'block';

          eventSource.close();
          return;
        }

        // Progress update
        const percent = Math.round((update.current / update.total) * 100);

        progressBar.style.width = `${percent}%`;
        progressBar.textContent = `${percent}%`;

        statusText.className = 'alert alert-info';
        statusText.innerHTML = `
          <strong>Importing:</strong> ${update.name}<br>
          <small>Progress: ${update.current} / ${update.total}</small>
        `;
      };

      // Handle connection errors
      eventSource.onerror = function() {
        statusText.className = 'alert alert-danger';
        statusText.textContent = 'Connection error. Please try again.';
        progressBar.className = 'progress-bar bg-danger';
        startBtn.style.display = 'block';
        eventSource.close();
      };
    });
  </script>

</body>
</html>
```

---

## Simple Data Display

**File: `performers-list.html`**

Display a list of performers from the database.

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Performers - Goondex</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">

  <style>
    .performer-grid {
      max-width: 1400px;
      margin: 2rem auto;
      padding: 0 2rem;
    }

    .performer-card {
      height: 100%;
      transition: transform 0.2s;
    }

    .performer-card:hover {
      transform: translateY(-5px);
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    }

    .performer-image {
      width: 100%;
      height: 350px;
      object-fit: cover;
    }

    .loading {
      text-align: center;
      padding: 3rem;
    }
  </style>
</head>
<body>

  <nav class="navbar navbar-dark bg-dark">
    <div class="container-fluid">
      <a class="navbar-brand" href="/">Goondex</a>
    </div>
  </nav>

  <div class="performer-grid">
    <h1 class="mb-4">All Performers</h1>

    <!-- Loading indicator -->
    <div id="loading" class="loading">
      <div class="spinner-border text-primary" role="status">
        <span class="visually-hidden">Loading...</span>
      </div>
      <p class="mt-2">Loading performers...</p>
    </div>

    <!-- Performers grid -->
    <div id="performers-grid" class="row row-cols-1 row-cols-md-3 row-cols-lg-5 g-4"></div>
  </div>

  <script>
    const API_BASE = 'http://localhost:8080';

    // Load performers when page loads
    window.addEventListener('load', loadPerformers);

    async function loadPerformers() {
      try {
        // Fetch from search API with empty query (returns all)
        const response = await fetch(`${API_BASE}/api/search?q=`);
        const data = await response.json();

        // Hide loading
        document.getElementById('loading').style.display = 'none';

        if (!data.success) {
          alert('Error: ' + data.message);
          return;
        }

        // Display performers
        displayPerformers(data.data.performers || []);

      } catch (error) {
        document.getElementById('loading').style.display = 'none';
        alert('Error loading performers: ' + error.message);
      }
    }

    function displayPerformers(performers) {
      const grid = document.getElementById('performers-grid');

      if (performers.length === 0) {
        grid.innerHTML = `
          <div class="col-12 text-center text-muted py-5">
            <h3>No performers found</h3>
            <p>Import some performers to get started</p>
          </div>
        `;
        return;
      }

      let html = '';

      performers.forEach(performer => {
        const imageUrl = performer.image_url || '/static/placeholder.jpg';
        const flag = performer.nationality ? getFlagEmoji(performer.nationality) : '';
        const age = calculateAge(performer.birthday);

        html += `
          <div class="col">
            <div class="card performer-card">
              <img
                src="${imageUrl}"
                class="performer-image"
                alt="${performer.name}"
                onerror="this.src='/static/placeholder.jpg'"
              >
              <div class="card-body">
                <h5 class="card-title">${performer.name}</h5>
                <p class="card-text">
                  ${flag} ${performer.gender || ''}<br>
                  ${age ? `Age: ${age}` : ''}
                </p>
                <a href="/performers/${performer.id}" class="btn btn-primary btn-sm w-100">
                  View Profile
                </a>
              </div>
            </div>
          </div>
        `;
      });

      grid.innerHTML = html;
    }

    // Helper: Convert country code to flag emoji
    function getFlagEmoji(countryCode) {
      const codePoints = countryCode
        .toUpperCase()
        .split('')
        .map(char => 127397 + char.charCodeAt());
      return String.fromCodePoint(...codePoints);
    }

    // Helper: Calculate age from birthday
    function calculateAge(birthday) {
      if (!birthday) return null;

      const birthDate = new Date(birthday);
      const today = new Date();
      let age = today.getFullYear() - birthDate.getFullYear();
      const monthDiff = today.getMonth() - birthDate.getMonth();

      if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
        age--;
      }

      return age;
    }
  </script>

</body>
</html>
```

---

## Bootstrap Component Reference

Quick reference for Bootstrap 5 components to use in your pages.

### Buttons

```html
<!-- Primary button -->
<button class="btn btn-primary">Primary</button>

<!-- Success button -->
<button class="btn btn-success">Success</button>

<!-- Danger button -->
<button class="btn btn-danger">Danger</button>

<!-- Large button -->
<button class="btn btn-primary btn-lg">Large Button</button>

<!-- Small button -->
<button class="btn btn-primary btn-sm">Small Button</button>

<!-- Full width button -->
<button class="btn btn-primary w-100">Full Width</button>

<!-- Disabled button -->
<button class="btn btn-primary" disabled>Disabled</button>
```

### Alerts

```html
<!-- Success alert -->
<div class="alert alert-success">
  <strong>Success!</strong> Your action was successful.
</div>

<!-- Error alert -->
<div class="alert alert-danger">
  <strong>Error!</strong> Something went wrong.
</div>

<!-- Warning alert -->
<div class="alert alert-warning">
  <strong>Warning!</strong> Please be careful.
</div>

<!-- Info alert -->
<div class="alert alert-info">
  <strong>Info:</strong> Here's some information.
</div>

<!-- Dismissible alert -->
<div class="alert alert-success alert-dismissible fade show">
  Success message
  <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
```

### Cards

```html
<!-- Basic card -->
<div class="card">
  <img src="image.jpg" class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">Card Title</h5>
    <p class="card-text">Card description goes here.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>

<!-- Card with header -->
<div class="card">
  <div class="card-header">
    Featured
  </div>
  <div class="card-body">
    <h5 class="card-title">Special title</h5>
    <p class="card-text">Card content</p>
  </div>
</div>
```

### Forms

```html
<!-- Basic form -->
<form>
  <div class="mb-3">
    <label for="input1" class="form-label">Email address</label>
    <input type="email" class="form-control" id="input1">
  </div>

  <div class="mb-3">
    <label for="input2" class="form-label">Password</label>
    <input type="password" class="form-control" id="input2">
  </div>

  <button type="submit" class="btn btn-primary">Submit</button>
</form>

<!-- Large input -->
<input type="text" class="form-control form-control-lg" placeholder="Large input">

<!-- Small input -->
<input type="text" class="form-control form-control-sm" placeholder="Small input">

<!-- Select dropdown -->
<select class="form-select">
  <option selected>Choose...</option>
  <option value="1">Option 1</option>
  <option value="2">Option 2</option>
</select>
```

### Progress Bars

```html
<!-- Basic progress bar -->
<div class="progress">
  <div class="progress-bar" style="width: 50%">50%</div>
</div>

<!-- Colored progress bars -->
<div class="progress">
  <div class="progress-bar bg-success" style="width: 75%"></div>
</div>

<div class="progress">
  <div class="progress-bar bg-danger" style="width: 100%"></div>
</div>

<!-- Striped and animated -->
<div class="progress">
  <div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 75%"></div>
</div>
```

### Spinners

```html
<!-- Basic spinner -->
<div class="spinner-border" role="status">
  <span class="visually-hidden">Loading...</span>
</div>

<!-- Colored spinners -->
<div class="spinner-border text-primary"></div>
<div class="spinner-border text-success"></div>
<div class="spinner-border text-danger"></div>

<!-- Small spinner -->
<div class="spinner-border spinner-border-sm"></div>

<!-- Growing spinner -->
<div class="spinner-grow" role="status">
  <span class="visually-hidden">Loading...</span>
</div>
```

### Lists

```html
<!-- Basic list group -->
<ul class="list-group">
  <li class="list-group-item">Item 1</li>
  <li class="list-group-item">Item 2</li>
  <li class="list-group-item">Item 3</li>
</ul>

<!-- Clickable list -->
<div class="list-group">
  <a href="#" class="list-group-item list-group-item-action">
    Clickable item
  </a>
  <a href="#" class="list-group-item list-group-item-action active">
    Active item
  </a>
</div>
```

### Grid System

```html
<!-- Responsive grid (1 column on mobile, 3 on tablet, 4 on desktop) -->
<div class="row row-cols-1 row-cols-md-3 row-cols-lg-4 g-4">
  <div class="col">Column 1</div>
  <div class="col">Column 2</div>
  <div class="col">Column 3</div>
  <div class="col">Column 4</div>
</div>

<!-- Two column layout -->
<div class="row">
  <div class="col-md-6">Left column</div>
  <div class="col-md-6">Right column</div>
</div>

<!-- Sidebar layout -->
<div class="row">
  <div class="col-md-3">Sidebar</div>
  <div class="col-md-9">Main content</div>
</div>
```

---

## CSS Tips

### Spacing Utilities

```html
<!-- Margin -->
<div class="m-3">Margin on all sides</div>
<div class="mt-3">Margin top</div>
<div class="mb-3">Margin bottom</div>
<div class="mx-3">Margin left and right</div>
<div class="my-3">Margin top and bottom</div>

<!-- Padding -->
<div class="p-3">Padding on all sides</div>
<div class="pt-3">Padding top</div>
<div class="pb-3">Padding bottom</div>
<div class="px-3">Padding left and right</div>
<div class="py-3">Padding top and bottom</div>

<!-- Sizes: 0, 1, 2, 3, 4, 5 (0 = 0, 5 = 3rem) -->
```

### Text Utilities

```html
<!-- Alignment -->
<div class="text-start">Left aligned</div>
<div class="text-center">Center aligned</div>
<div class="text-end">Right aligned</div>

<!-- Colors -->
<p class="text-primary">Primary text</p>
<p class="text-success">Success text</p>
<p class="text-danger">Danger text</p>
<p class="text-muted">Muted text</p>

<!-- Font weight -->
<p class="fw-bold">Bold text</p>
<p class="fw-normal">Normal text</p>
<p class="fw-light">Light text</p>

<!-- Font size -->
<p class="fs-1">Very large</p>
<p class="fs-5">Medium</p>
```

### Display Utilities

```html
<!-- Show/Hide -->
<div class="d-none">Hidden</div>
<div class="d-block">Shown as block</div>
<div class="d-inline-block">Inline block</div>
<div class="d-flex">Flexbox container</div>

<!-- Responsive display -->
<div class="d-none d-md-block">Hidden on mobile, shown on tablet+</div>
```

---

## Quick JavaScript Snippets

### Show/Hide Elements

```javascript
// Hide element
document.getElementById('myElement').style.display = 'none';

// Show element
document.getElementById('myElement').style.display = 'block';

// Toggle visibility
const el = document.getElementById('myElement');
el.style.display = el.style.display === 'none' ? 'block' : 'none';
```

### Change Text Content

```javascript
document.getElementById('myElement').textContent = 'New text';
document.getElementById('myElement').innerHTML = '<strong>HTML</strong> text';
```

### Add/Remove CSS Classes

```javascript
// Add class
document.getElementById('myElement').classList.add('active');

// Remove class
document.getElementById('myElement').classList.remove('active');

// Toggle class
document.getElementById('myElement').classList.toggle('active');
```

### Get Form Values

```javascript
const inputValue = document.getElementById('myInput').value;
const selectValue = document.getElementById('mySelect').value;
```

---

## Need Help?

All the JavaScript in these templates is ready to use - just copy and paste! If you need to customize something:

1. Look for comments in the JavaScript (`// like this`)
2. The `API_BASE` constant at the top controls the server URL
3. Use AI assistance to modify the JavaScript parts
4. Focus on the HTML/CSS which you're already great at!

The templates are fully functional and follow Bootstrap best practices.
