<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Developer Setup · ExerciseDB</title>
  <style>
    /* ── Design Tokens ──────────────────────────────── */
    :root {
      --accent:         #ff4f00;
      --accent-muted:   rgba(255,79,0,0.09);
      --bg-base:        #f4f4f5;
      --bg-surface:     #ffffff;
      --bg-elevated:    #f0f0f1;
      --border:         #e4e4e7;
      --border-hover:   #c4c4c7;
      --text-primary:   #111111;
      --text-secondary: #71717a;
      --text-tertiary:  #a1a1aa;
      --radius-sm:  6px;
      --radius-md:  10px;
      --radius-lg:  14px;
      --radius-xl:  18px;
      --nav-w: 210px;
    }

    /* ── Reset ──────────────────────────────────────── */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    html { scroll-behavior: smooth; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
      background: var(--bg-base);
      color: var(--text-primary);
      min-height: 100vh;
      font-size: 14px;
      line-height: 1.6;
    }
    a { color: inherit; text-decoration: none; }
    code {
      font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
      font-size: 12px;
      background: var(--bg-elevated);
      border: 1px solid var(--border);
      border-radius: var(--radius-sm);
      padding: 1px 5px;
    }

    /* ── Header ─────────────────────────────────────── */
    .site-header {
      background: var(--bg-surface);
      border-bottom: 1px solid var(--border);
      height: 52px;
      display: flex;
      align-items: center;
      padding: 0 24px;
      gap: 20px;
      position: sticky;
      top: 0;
      z-index: 100;
    }
    .site-logo {
      font-size: 17px;
      font-weight: 700;
      letter-spacing: -0.4px;
      color: var(--text-primary);
    }
    .site-logo span { color: var(--accent); }
    .back-link {
      display: flex;
      align-items: center;
      gap: 5px;
      font-size: 13px;
      color: var(--text-secondary);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      padding: 4px 11px;
      margin-left: auto;
      transition: color .15s, border-color .15s;
    }
    .back-link:hover { color: var(--text-primary); border-color: var(--border-hover); }
    .back-link svg { flex-shrink: 0; }
    .header-divider {
      width: 1px; height: 20px;
      background: var(--border);
    }
    .header-label {
      font-size: 13px;
      color: var(--text-secondary);
    }

    /* ── Shell ───────────────────────────────────────── */
    .page-shell {
      display: grid;
      grid-template-columns: var(--nav-w) 1fr;
      max-width: 1160px;
      margin: 0 auto;
      padding: 0 16px;
      gap: 0;
      min-height: calc(100vh - 52px);
    }

    /* ── Left Nav ────────────────────────────────────── */
    .page-nav {
      position: sticky;
      top: 52px;
      height: calc(100vh - 52px);
      overflow-y: auto;
      padding: 32px 0 32px 0;
      border-right: 1px solid var(--border);
    }
    .nav-group-label {
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .06em;
      text-transform: uppercase;
      color: var(--text-tertiary);
      padding: 0 16px 8px 16px;
    }
    .nav-link {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 13px;
      padding: 7px 16px;
      border-radius: 0 var(--radius-md) var(--radius-md) 0;
      color: var(--text-secondary);
      transition: color .15s, background .15s;
      cursor: pointer;
      border-left: 2px solid transparent;
      margin-right: 12px;
    }
    .nav-link:hover { color: var(--text-primary); background: var(--bg-elevated); }
    .nav-link.active {
      color: var(--accent);
      background: var(--accent-muted);
      border-left-color: var(--accent);
      font-weight: 500;
    }
    .nav-link svg { opacity: .5; flex-shrink: 0; }
    .nav-link.active svg { opacity: 1; }
    .nav-spacer { height: 20px; }

    /* ── Main Content ────────────────────────────────── */
    .page-content {
      padding: 48px 0 80px 48px;
      max-width: 820px;
    }

    /* ── Section ─────────────────────────────────────── */
    .section {
      margin-bottom: 72px;
    }
    .section-header {
      display: flex;
      align-items: center;
      gap: 14px;
      margin-bottom: 6px;
    }
    .section-num {
      width: 30px; height: 30px;
      background: var(--accent);
      color: #fff;
      font-size: 14px;
      font-weight: 700;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
    }
    .section-title {
      font-size: 22px;
      font-weight: 700;
      letter-spacing: -0.4px;
    }
    .section-desc {
      font-size: 14px;
      color: var(--text-secondary);
      margin-bottom: 24px;
      padding-left: 44px;
      max-width: 620px;
      line-height: 1.65;
    }
    .section-body { padding-left: 44px; }

    /* ── DB Tabs ─────────────────────────────────────── */
    .tab-bar {
      display: flex;
      gap: 2px;
      border-bottom: 1px solid var(--border);
      margin-bottom: 24px;
    }
    .tab-btn {
      font-size: 13px;
      font-weight: 500;
      padding: 8px 16px;
      background: none;
      border: none;
      border-bottom: 2px solid transparent;
      margin-bottom: -1px;
      color: var(--text-secondary);
      cursor: pointer;
      transition: color .15s, border-color .15s;
      white-space: nowrap;
    }
    .tab-btn:hover { color: var(--text-primary); }
    .tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }

    /* ── Steps ───────────────────────────────────────── */
    .step-list { display: flex; flex-direction: column; gap: 28px; }
    .step {
      display: flex;
      gap: 16px;
    }
    .step-num {
      width: 24px; height: 24px;
      background: var(--accent);
      color: #fff;
      font-size: 12px;
      font-weight: 700;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      margin-top: 1px;
    }
    .step-body { flex: 1; min-width: 0; }
    .step-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
    .step-desc { font-size: 13px; color: var(--text-secondary); margin-bottom: 10px; line-height: 1.6; }

    /* ── Code Block ──────────────────────────────────── */
    .code-block {
      position: relative;
      background: #1a1a1a;
      border-radius: var(--radius-lg);
      overflow: hidden;
    }
    .code-pre {
      font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
      font-size: 12px;
      line-height: 1.75;
      color: #e4e4e7;
      padding: 16px 20px;
      overflow-x: auto;
      white-space: pre;
      tab-size: 2;
    }
    .copy-btn {
      position: absolute;
      top: 10px;
      right: 10px;
      font-size: 11px;
      font-weight: 500;
      padding: 4px 10px;
      background: rgba(255,255,255,0.10);
      color: #a1a1aa;
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: var(--radius-sm);
      cursor: pointer;
      transition: background .15s, color .15s;
    }
    .copy-btn:hover { background: rgba(255,255,255,0.18); color: #fff; }
    .copy-btn.copied { color: #4ade80; border-color: rgba(74,222,128,0.3); }

    /* ── Generate Button ─────────────────────────────── */
    .generate-btn {
      display: inline-flex;
      align-items: center;
      gap: 7px;
      background: var(--accent);
      color: #fff;
      font-size: 13px;
      font-weight: 600;
      padding: 9px 18px;
      border: none;
      border-radius: var(--radius-md);
      cursor: pointer;
      transition: opacity .15s;
    }
    .generate-btn:hover { opacity: .88; }
    .generate-btn:disabled { opacity: .5; cursor: not-allowed; }
    .generate-status {
      font-size: 12px;
      color: var(--text-secondary);
      margin-left: 12px;
    }

    /* ── URL Input ───────────────────────────────────── */
    .url-input-row {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-bottom: 20px;
      background: var(--bg-surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      padding: 8px 14px;
    }
    .url-label {
      font-size: 12px;
      font-weight: 600;
      color: var(--text-tertiary);
      white-space: nowrap;
    }
    .url-input {
      flex: 1;
      border: none;
      outline: none;
      font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
      font-size: 13px;
      color: var(--text-primary);
      background: transparent;
    }
    .url-input::placeholder { color: var(--text-tertiary); }

    /* ── API Endpoint Cards ──────────────────────────── */
    .endpoint-list { display: flex; flex-direction: column; gap: 24px; }
    .endpoint-card {
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      overflow: hidden;
      background: var(--bg-surface);
    }
    .endpoint-header {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 12px 16px;
      border-bottom: 1px solid var(--border);
      background: var(--bg-elevated);
    }
    .method-badge {
      font-size: 11px;
      font-weight: 700;
      padding: 2px 7px;
      border-radius: var(--radius-sm);
      background: #dcfce7;
      color: #166534;
      letter-spacing: .04em;
    }
    .endpoint-path {
      font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
      font-size: 13px;
      font-weight: 500;
      color: var(--text-primary);
    }
    .endpoint-desc {
      font-size: 12px;
      color: var(--text-secondary);
      margin-left: auto;
    }
    .endpoint-body { position: relative; }
    .endpoint-body .code-block { border-radius: 0; }

    /* ── LLM Section ─────────────────────────────────── */
    .llm-selectors {
      display: flex;
      gap: 12px;
      margin-bottom: 20px;
      flex-wrap: wrap;
    }
    .selector-group { display: flex; flex-direction: column; gap: 5px; }
    .selector-label { font-size: 11px; font-weight: 600; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: .06em; }
    .selector-tabs { display: flex; gap: 2px; }
    .selector-btn {
      font-size: 12px;
      font-weight: 500;
      padding: 5px 12px;
      background: var(--bg-elevated);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      color: var(--text-secondary);
      cursor: pointer;
      transition: all .15s;
    }
    .selector-btn:hover { border-color: var(--border-hover); color: var(--text-primary); }
    .selector-btn.active { background: var(--accent-muted); border-color: var(--accent); color: var(--accent); font-weight: 600; }

    .llm-prompt-wrap {
      position: relative;
    }
    .llm-prompt-box {
      width: 100%;
      min-height: 380px;
      font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
      font-size: 12px;
      line-height: 1.75;
      background: #1a1a1a;
      color: #e4e4e7;
      border: none;
      border-radius: var(--radius-lg);
      padding: 20px;
      resize: vertical;
      outline: none;
      tab-size: 2;
    }
    .llm-actions {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-top: 12px;
    }
    .copy-prompt-btn {
      display: inline-flex;
      align-items: center;
      gap: 7px;
      background: var(--accent);
      color: #fff;
      font-size: 13px;
      font-weight: 600;
      padding: 9px 18px;
      border: none;
      border-radius: var(--radius-md);
      cursor: pointer;
      transition: opacity .15s;
    }
    .copy-prompt-btn:hover { opacity: .88; }
    .copy-prompt-btn.copied { background: #16a34a; }
    .llm-hint {
      font-size: 12px;
      color: var(--text-tertiary);
    }

/* ── Media Note ──────────────────────────────────── */
    .media-note {
      display: flex;
      align-items: flex-start;
      gap: 8px;
      margin-top: 12px;
      padding: 10px 14px;
      background: var(--bg-elevated);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      font-size: 13px;
      color: var(--text-secondary);
      line-height: 1.55;
    }
    .media-note code { font-size: 11px; }

    /* ── Misc helpers ────────────────────────────────── */
    .inline-badge {
      font-size: 11px;
      font-weight: 600;
      padding: 2px 7px;
      border-radius: var(--radius-sm);
      background: var(--accent-muted);
      color: var(--accent);
    }
    .divider { height: 1px; background: var(--border); margin: 48px 0; }

    /* ── Scrollbar ───────────────────────────────────── */
    ::-webkit-scrollbar { width: 6px; height: 6px; }
    ::-webkit-scrollbar-track { background: transparent; }
    ::-webkit-scrollbar-thumb { background: var(--border-hover); border-radius: 3px; }

    /* ── Responsive ──────────────────────────────────── */
    @media (max-width: 768px) {
      .page-shell { grid-template-columns: 1fr; }
      .page-nav { display: none; }
      .page-content { padding: 32px 0 60px 0; }
      .section-body { padding-left: 0; }
      .section-desc { padding-left: 0; }
    }
  </style>
</head>
<body>

<!-- Header -->
<header class="site-header">
  <div class="site-logo">Exercise<span>DB</span></div>
  <div class="header-divider"></div>
  <span class="header-label">Developer Setup</span>
  <a class="back-link" href="index.html">
    <svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
      <path d="M10 4L6 8l4 4"/>
    </svg>
    Back to Browse
  </a>
</header>

<!-- Shell -->
<div class="page-shell">

  <!-- Left Nav -->
  <nav class="page-nav" id="page-nav">
    <div class="nav-group-label">Contents</div>
    <a class="nav-link active" href="#db-setup" data-section="db-setup">
      <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
        <ellipse cx="8" cy="4" rx="6" ry="2.5"/>
        <path d="M2 4v3c0 1.38 2.69 2.5 6 2.5s6-1.12 6-2.5V4"/>
        <path d="M2 7v3c0 1.38 2.69 2.5 6 2.5s6-1.12 6-2.5V7"/>
        <path d="M2 10v2c0 1.38 2.69 2.5 6 2.5s6-1.12 6-2.5v-2"/>
      </svg>
      Database Setup
    </a>
    <div class="nav-spacer"></div>
    <a class="nav-link" href="#api-integration" data-section="api-integration">
      <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
        <polyline points="5 4 1 8 5 12"/>
        <polyline points="11 4 15 8 11 12"/>
        <line x1="8" y1="2" x2="8" y2="14"/>
      </svg>
      API Integration
    </a>
    <div class="nav-spacer"></div>
    <a class="nav-link" href="#ask-llm" data-section="ask-llm">
      <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
        <path d="M14 2H2a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h3l3 2 3-2h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1z"/>
        <path d="M8 6v2M8 10h.01"/>
      </svg>
      Ask Your LLM
    </a>
  </nav>

  <!-- Main Content -->
  <main class="page-content">

    <!-- ── SECTION 1: Database Setup ──────────────────── -->
    <section class="section" id="db-setup">
      <div class="section-header">
        <div class="section-num">1</div>
        <h2 class="section-title">Database Setup</h2>
      </div>
      <p class="section-desc">Import all 1,324 exercises into your database in 3 steps. Choose your database engine below.</p>

      <div class="section-body">
        <!-- DB Tabs -->
        <div class="tab-bar" id="db-tabs" role="tablist">
          <button class="tab-btn active" data-db="mssql" role="tab">SQL Server</button>
          <button class="tab-btn" data-db="postgresql" role="tab">PostgreSQL</button>
          <button class="tab-btn" data-db="mysql" role="tab">MySQL</button>
          <button class="tab-btn" data-db="sqlite" role="tab">SQLite</button>
        </div>

        <div class="step-list">
          <!-- Step 1 -->
          <div class="step">
            <div class="step-num">1</div>
            <div class="step-body">
              <h3 class="step-title">Create Table</h3>
              <p class="step-desc">Run this in your database client (SSMS, DBeaver, pgAdmin, TablePlus, etc.):</p>
              <div class="code-block">
                <button class="copy-btn" id="copy-create-btn">Copy</button>
                <pre class="code-pre" id="create-table-sql"></pre>
              </div>
            </div>
          </div>

          <!-- Step 2 -->
          <div class="step">
            <div class="step-num">2</div>
            <div class="step-body">
              <h3 class="step-title">Import Data</h3>
              <p class="step-desc">Generate a <code>.sql</code> file with all 1,324 INSERT statements. The file is built entirely in your browser — nothing is uploaded to any server.</p>
              <div style="display:flex;align-items:center;gap:0;">
                <button class="generate-btn" id="generate-sql-btn">
                  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" width="14" height="14">
                    <path d="M8 2v8M5 7l3 3 3-3"/><path d="M2 12h12"/>
                  </svg>
                  Generate INSERT SQL
                </button>
                <span class="generate-status" id="generate-status"></span>
              </div>
            </div>
          </div>

          <!-- Step 3 -->
          <div class="step">
            <div class="step-num">3</div>
            <div class="step-body">
              <h3 class="step-title">Media Files</h3>
              <p class="step-desc">Download the media archives and place them in your web server root or CDN. The <code>image</code> and <code>gif_url</code> columns store relative paths — prepend your base URL in your app.</p>
              <div class="code-block">
                <pre class="code-pre">your-server.com/
├── images/   ← 1,324 JPG thumbnails
└── videos/   ← 1,324 GIF animations</pre>
              </div>
              <div class="media-note">
                <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;margin-top:1px">
                  <circle cx="8" cy="8" r="6"/><path d="M8 7v4M8 5.5h.01"/>
                </svg>
                The <code>images/</code> and <code>videos/</code> folders are already included in the repository. After cloning, just copy them to your server or CDN — no separate download needed.
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>

    <div class="divider"></div>

    <!-- ── SECTION 2: API Integration ─────────────────── -->
    <section class="section" id="api-integration">
      <div class="section-header">
        <div class="section-num">2</div>
        <h2 class="section-title">API Integration</h2>
      </div>
      <p class="section-desc">Client-side code examples showing how to call your backend API from your app. Enter your API base URL below — all examples update live.</p>

      <div class="section-body">
        <!-- Base URL Input -->
        <div class="url-input-row">
          <span class="url-label">BASE URL</span>
          <input
            type="url"
            class="url-input"
            id="base-url-input"
            value="https://api.yourapp.com"
            placeholder="https://api.yourapp.com"
            spellcheck="false"
          />
        </div>

        <!-- Language Tabs -->
        <div class="tab-bar" id="lang-tabs" role="tablist">
          <button class="tab-btn active" data-lang="curl" role="tab">cURL</button>
          <button class="tab-btn" data-lang="js" role="tab">JavaScript</button>
          <button class="tab-btn" data-lang="python" role="tab">Python</button>
          <button class="tab-btn" data-lang="csharp" role="tab">C#</button>
          <button class="tab-btn" data-lang="java" role="tab">Java</button>
          <button class="tab-btn" data-lang="php" role="tab">PHP</button>
          <button class="tab-btn" data-lang="go" role="tab">Go</button>
        </div>

        <!-- Endpoints -->
        <div class="endpoint-list">

          <!-- GET /exercises/:id -->
          <div class="endpoint-card">
            <div class="endpoint-header">
              <span class="method-badge">GET</span>
              <span class="endpoint-path">/exercises/<span style="color:var(--accent)">:id</span></span>
              <span class="endpoint-desc">Fetch a single exercise by ID</span>
            </div>
            <div class="endpoint-body">
              <div class="code-block">
                <button class="copy-btn" onclick="copyBlock(this)">Copy</button>
                <pre class="code-pre api-code" data-endpoint="getOne" id="code-getOne"></pre>
              </div>
            </div>
          </div>

          <!-- GET /exercises?page= -->
          <div class="endpoint-card">
            <div class="endpoint-header">
              <span class="method-badge">GET</span>
              <span class="endpoint-path">/exercises<span style="color:var(--text-tertiary)">?page=1&amp;limit=20</span></span>
              <span class="endpoint-desc">Paginated list of exercises</span>
            </div>
            <div class="endpoint-body">
              <div class="code-block">
                <button class="copy-btn" onclick="copyBlock(this)">Copy</button>
                <pre class="code-pre api-code" data-endpoint="getAll" id="code-getAll"></pre>
              </div>
            </div>
          </div>

          <!-- GET /exercises?category=&body_part= -->
          <div class="endpoint-card">
            <div class="endpoint-header">
              <span class="method-badge">GET</span>
              <span class="endpoint-path">/exercises<span style="color:var(--text-tertiary)">?category=Strength&amp;body_part=Chest</span></span>
              <span class="endpoint-desc">Filter by category, body part, equipment</span>
            </div>
            <div class="endpoint-body">
              <div class="code-block">
                <button class="copy-btn" onclick="copyBlock(this)">Copy</button>
                <pre class="code-pre api-code" data-endpoint="getFiltered" id="code-getFiltered"></pre>
              </div>
            </div>
          </div>

        </div>
      </div>
    </section>

    <div class="divider"></div>

    <!-- ── SECTION 3: Ask Your LLM ─────────────────────── -->
    <section class="section" id="ask-llm">
      <div class="section-header">
        <div class="section-num">3</div>
        <h2 class="section-title">Ask Your LLM</h2>
      </div>
      <p class="section-desc">
        Don't want to write a backend from scratch? Copy this prompt into ChatGPT, Claude, Gemini, or any LLM — it contains everything needed to generate a complete, production-ready API in one shot.
      </p>

      <div class="section-body">
        <div class="llm-selectors">
          <div class="selector-group">
            <div class="selector-label">Framework</div>
            <div class="selector-tabs" id="framework-tabs">
              <button class="selector-btn active" data-fw="express">Express.js</button>
              <button class="selector-btn" data-fw="fastapi">FastAPI</button>
              <button class="selector-btn" data-fw="aspnet">ASP.NET Core</button>
              <button class="selector-btn" data-fw="spring">Spring Boot</button>
              <button class="selector-btn" data-fw="laravel">Laravel</button>
              <button class="selector-btn" data-fw="gin">Gin (Go)</button>
            </div>
          </div>
          <div class="selector-group">
            <div class="selector-label">Database</div>
            <div class="selector-tabs" id="llm-db-tabs">
              <button class="selector-btn active" data-llmdb="postgresql">PostgreSQL</button>
              <button class="selector-btn" data-llmdb="mysql">MySQL</button>
              <button class="selector-btn" data-llmdb="mssql">SQL Server</button>
              <button class="selector-btn" data-llmdb="sqlite">SQLite</button>
            </div>
          </div>
        </div>

        <div class="llm-prompt-wrap">
          <textarea class="llm-prompt-box" id="llm-prompt" readonly spellcheck="false"></textarea>
        </div>
        <div class="llm-actions">
          <button class="copy-prompt-btn" id="copy-prompt-btn">
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
              <rect x="4" y="4" width="9" height="11" rx="1.5"/>
              <path d="M11 4V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h1"/>
            </svg>
            Copy Prompt
          </button>
          <span class="llm-hint">Paste into ChatGPT, Claude, Gemini, or any LLM</span>
        </div>
      </div>
    </section>

  </main>
</div>

<script>
// ══════════════════════════════════════════════════
// ── DB SQL Templates ───────────────────────────────
// ══════════════════════════════════════════════════

const DB_SQL = {
  mssql: `CREATE TABLE exercises (
  id                NVARCHAR(10)  PRIMARY KEY,
  name              NVARCHAR(255) NOT NULL,
  category          NVARCHAR(100),
  body_part         NVARCHAR(100),
  equipment         NVARCHAR(100),
  instructions_en   NVARCHAR(MAX),
  instructions_es   NVARCHAR(MAX),
  instructions_it   NVARCHAR(MAX),
  instructions_tr   NVARCHAR(MAX),
  instructions_ru   NVARCHAR(MAX),
  instructions_zh   NVARCHAR(MAX),
  muscle_group      NVARCHAR(100),
  secondary_muscles NVARCHAR(MAX),  -- JSON array stored as string
  target            NVARCHAR(100),
  image             NVARCHAR(500),
  gif_url           NVARCHAR(500),
  created_at        DATETIME2
);`,
  postgresql: `CREATE TABLE exercises (
  id                VARCHAR(10)  PRIMARY KEY,
  name              VARCHAR(255) NOT NULL,
  category          VARCHAR(100),
  body_part         VARCHAR(100),
  equipment         VARCHAR(100),
  instructions_en   TEXT,
  instructions_es   TEXT,
  instructions_it   TEXT,
  instructions_tr   TEXT,
  instructions_ru   TEXT,
  instructions_zh   TEXT,
  muscle_group      VARCHAR(100),
  secondary_muscles JSONB,
  target            VARCHAR(100),
  image             VARCHAR(500),
  gif_url           VARCHAR(500),
  created_at        TIMESTAMPTZ
);`,
  mysql: `CREATE TABLE exercises (
  id                VARCHAR(10)  PRIMARY KEY,
  name              VARCHAR(255) NOT NULL,
  category          VARCHAR(100),
  body_part         VARCHAR(100),
  equipment         VARCHAR(100),
  instructions_en   TEXT,
  instructions_es   TEXT,
  instructions_it   TEXT,
  instructions_tr   TEXT,
  instructions_ru   TEXT,
  instructions_zh   TEXT,
  muscle_group      VARCHAR(100),
  secondary_muscles JSON,
  target            VARCHAR(100),
  image             VARCHAR(500),
  gif_url           VARCHAR(500),
  created_at        DATETIME
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`,
  sqlite: `CREATE TABLE exercises (
  id                TEXT PRIMARY KEY,
  name              TEXT NOT NULL,
  category          TEXT,
  body_part         TEXT,
  equipment         TEXT,
  instructions_en   TEXT,
  instructions_es   TEXT,
  instructions_it   TEXT,
  instructions_tr   TEXT,
  instructions_ru   TEXT,
  instructions_zh   TEXT,
  muscle_group      TEXT,
  secondary_muscles TEXT,  -- JSON array stored as string
  target            TEXT,
  image             TEXT,
  gif_url           TEXT,
  created_at        TEXT
);`
};

// ══════════════════════════════════════════════════
// ── API Code Templates ─────────────────────────────
// ══════════════════════════════════════════════════

const API_TEMPLATES = {
  curl: {
    getOne: (base) =>
`# GET single exercise by ID
curl -s "${base}/exercises/0001"

# Pretty-print the response (Python 3 built-in)
curl -s "${base}/exercises/0001" | python3 -m json.tool`,

    getAll: (base) =>
`# GET all exercises — page 1, 20 per page
curl -s "${base}/exercises?page=1&limit=20"

# Page 2, 50 per page
curl -s "${base}/exercises?page=2&limit=50"

# Response shape:
# {
#   "data": [ ...exercises ],
#   "total": 1324,
#   "page": 1,
#   "limit": 20,
#   "totalPages": 67
# }`,

    getFiltered: (base) =>
`# Filter by category
curl -s "${base}/exercises?category=Strength&page=1&limit=20"

# Filter by body part
curl -s "${base}/exercises?body_part=Chest&page=1&limit=20"

# Combine filters
curl -s "${base}/exercises?category=Strength&body_part=Chest&page=1&limit=20"

# With equipment filter
curl -s "${base}/exercises?equipment=Barbell&target=Pectorals&page=1&limit=20"`,
  },

  js: {
    getOne: (base) =>
`const BASE_URL = '${base}';

// GET /exercises/:id — fetch a single exercise
async function getExercise(id) {
  const res = await fetch(\`\${BASE_URL}/exercises/\${id}\`);
  if (!res.ok) throw new Error(\`HTTP \${res.status}\`);
  return res.json();
}

// Usage
const exercise = await getExercise('0001');
console.log(exercise.name);        // "Barbell Bench Press"
console.log(exercise.category);    // "Strength"
console.log(exercise.gif_url);     // "videos/0001.gif"`,

    getAll: (base) =>
`const BASE_URL = '${base}';

// GET /exercises?page=&limit= — paginated list
async function getExercises({ page = 1, limit = 20 } = {}) {
  const params = new URLSearchParams({ page, limit });
  const res = await fetch(\`\${BASE_URL}/exercises?\${params}\`);
  if (!res.ok) throw new Error(\`HTTP \${res.status}\`);
  return res.json();
}

// Usage
const result = await getExercises({ page: 1, limit: 20 });
console.log(result.data);       // Array of exercises
console.log(result.total);      // 1324
console.log(result.page);       // 1
console.log(result.totalPages); // 67`,

    getFiltered: (base) =>
`const BASE_URL = '${base}';

// GET /exercises with filters
async function getExercisesFiltered({
  page = 1,
  limit = 20,
  category,
  bodyPart,
  equipment,
  target,
} = {}) {
  const params = new URLSearchParams({ page, limit });
  if (category)  params.set('category', category);
  if (bodyPart)  params.set('body_part', bodyPart);
  if (equipment) params.set('equipment', equipment);
  if (target)    params.set('target', target);

  const res = await fetch(\`\${BASE_URL}/exercises?\${params}\`);
  if (!res.ok) throw new Error(\`HTTP \${res.status}\`);
  return res.json();
}

// Usage
const result = await getExercisesFiltered({
  category: 'Strength',
  bodyPart: 'Chest',
  page: 1,
  limit: 20,
});
console.log(result.data[0].name); // e.g. "Barbell Bench Press"`,
  },

  python: {
    getOne: (base) =>
`import requests

BASE_URL = "${base}"

# GET /exercises/:id — fetch a single exercise
def get_exercise(exercise_id: str) -> dict:
    res = requests.get(f"{BASE_URL}/exercises/{exercise_id}")
    res.raise_for_status()
    return res.json()

# Usage
exercise = get_exercise("0001")
print(exercise["name"])      # Barbell Bench Press
print(exercise["category"])  # Strength
print(exercise["gif_url"])   # videos/0001.gif`,

    getAll: (base) =>
`import requests

BASE_URL = "${base}"

# GET /exercises?page=&limit= — paginated list
def get_exercises(page: int = 1, limit: int = 20) -> dict:
    res = requests.get(f"{BASE_URL}/exercises", params={
        "page": page,
        "limit": limit,
    })
    res.raise_for_status()
    return res.json()

# Usage
result = get_exercises(page=1, limit=20)
print(result["data"])        # list of exercises
print(result["total"])       # 1324
print(result["page"])        # 1
print(result["totalPages"])  # 67`,

    getFiltered: (base) =>
`import requests

BASE_URL = "${base}"

# GET /exercises with filters
def get_exercises_filtered(
    page: int = 1,
    limit: int = 20,
    category: str = None,
    body_part: str = None,
    equipment: str = None,
    target: str = None,
) -> dict:
    params = {"page": page, "limit": limit}
    if category:  params["category"]  = category
    if body_part: params["body_part"] = body_part
    if equipment: params["equipment"] = equipment
    if target:    params["target"]    = target

    res = requests.get(f"{BASE_URL}/exercises", params=params)
    res.raise_for_status()
    return res.json()

# Usage
result = get_exercises_filtered(
    category="Strength",
    body_part="Chest",
    page=1,
    limit=20,
)
print(result["data"][0]["name"])  # e.g. Barbell Bench Press`,
  },

  csharp: {
    getOne: (base) =>
`using System.Net.Http.Json;

var client = new HttpClient { BaseAddress = new Uri("${base}") };

// GET /exercises/:id — fetch a single exercise
async Task<Exercise?> GetExerciseAsync(string id)
{
    var res = await client.GetAsync($"/exercises/{id}");
    res.EnsureSuccessStatusCode();
    return await res.Content.ReadFromJsonAsync<Exercise>();
}

// Usage
var exercise = await GetExerciseAsync("0001");
Console.WriteLine(exercise?.Name);     // Barbell Bench Press
Console.WriteLine(exercise?.Category); // Strength
Console.WriteLine(exercise?.GifUrl);   // videos/0001.gif

// Model
record Exercise(
    string Id, string Name, string Category, string BodyPart,
    string Equipment, string? InstructionsEn, string? InstructionsEs, string? InstructionsIt, string? InstructionsTr, string? InstructionsRu,
    string MuscleGroup, string[] SecondaryMuscles, string Target,
    string Image, string GifUrl, string? CreatedAt
);`,

    getAll: (base) =>
`using System.Net.Http.Json;

var client = new HttpClient { BaseAddress = new Uri("${base}") };

// GET /exercises?page=&limit= — paginated list
async Task<PagedResult<Exercise>> GetExercisesAsync(int page = 1, int limit = 20)
{
    return await client.GetFromJsonAsync<PagedResult<Exercise>>(
        $"/exercises?page={page}&limit={limit}"
    ) ?? throw new Exception("Null response");
}

// Usage
var result = await GetExercisesAsync(page: 1, limit: 20);
Console.WriteLine(result.Total);      // 1324
Console.WriteLine(result.TotalPages); // 67

// Models
record Exercise(string Id, string Name, string Category, string BodyPart,
    string Equipment, string? InstructionsEn, string Target, string GifUrl);
record PagedResult<T>(T[] Data, int Total, int Page, int Limit, int TotalPages);`,

    getFiltered: (base) =>
`using System.Net.Http.Json;
using System.Web;

var client = new HttpClient { BaseAddress = new Uri("${base}") };

// GET /exercises with filters
async Task<PagedResult<Exercise>> GetExercisesFilteredAsync(
    int page = 1, int limit = 20,
    string? category = null, string? bodyPart = null,
    string? equipment = null, string? target = null)
{
    var qs = HttpUtility.ParseQueryString(string.Empty);
    qs["page"]  = page.ToString();
    qs["limit"] = limit.ToString();
    if (category  != null) qs["category"]  = category;
    if (bodyPart  != null) qs["body_part"] = bodyPart;
    if (equipment != null) qs["equipment"] = equipment;
    if (target    != null) qs["target"]    = target;

    return await client.GetFromJsonAsync<PagedResult<Exercise>>(
        $"/exercises?{qs}"
    ) ?? throw new Exception("Null response");
}

// Usage
var result = await GetExercisesFilteredAsync(
    category: "Strength", bodyPart: "Chest", page: 1, limit: 20);
Console.WriteLine(result.Data[0].Name); // e.g. Barbell Bench Press`,
  },

  java: {
    getOne: (base) =>
`import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String BASE_URL = "${base}";

// GET /exercises/:id — fetch a single exercise
String getExercise(String id) throws Exception {
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(BASE_URL + "/exercises/" + id))
        .header("Accept", "application/json")
        .GET()
        .build();

    HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());

    if (response.statusCode() != 200)
        throw new RuntimeException("HTTP " + response.statusCode());

    return response.body(); // parse with Jackson/Gson
}

// Usage (with Jackson ObjectMapper)
// ObjectMapper mapper = new ObjectMapper();
// Map<?,?> exercise = mapper.readValue(getExercise("0001"), Map.class);`,

    getAll: (base) =>
`import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();
String BASE_URL = "${base}";

// GET /exercises?page=&limit= — paginated list
String getExercises(int page, int limit) throws Exception {
    String url = BASE_URL + "/exercises?page=" + page + "&limit=" + limit;

    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Accept", "application/json")
        .GET()
        .build();

    HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());

    return response.body();
    // { "data": [...], "total": 1324, "page": 1, "limit": 20, "totalPages": 67 }
}

// Usage
String json = getExercises(1, 20);`,

    getFiltered: (base) =>
`import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;

HttpClient client = HttpClient.newHttpClient();
String BASE_URL = "${base}";

// GET /exercises with filters
String getExercisesFiltered(
    int page, int limit,
    String category, String bodyPart, String equipment
) throws Exception {
    StringBuilder url = new StringBuilder(
        BASE_URL + "/exercises?page=" + page + "&limit=" + limit
    );
    if (category  != null)
        url.append("&category=").append(URLEncoder.encode(category, StandardCharsets.UTF_8));
    if (bodyPart  != null)
        url.append("&body_part=").append(URLEncoder.encode(bodyPart, StandardCharsets.UTF_8));
    if (equipment != null)
        url.append("&equipment=").append(URLEncoder.encode(equipment, StandardCharsets.UTF_8));

    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url.toString()))
        .header("Accept", "application/json")
        .GET()
        .build();

    return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
}

// Usage
String json = getExercisesFiltered(1, 20, "Strength", "Chest", null);`,
  },

  php: {
    getOne: (base) =>
`<?php

define('BASE_URL', '${base}');

// GET /exercises/:id — fetch a single exercise
function getExercise(string $id): array
{
    $url  = BASE_URL . '/exercises/' . urlencode($id);
    $json = file_get_contents($url);
    if ($json === false) throw new RuntimeException("Request failed");
    return json_decode($json, true);
}

// Usage
$exercise = getExercise('0001');
echo $exercise['name'];      // Barbell Bench Press
echo $exercise['category'];  // Strength
echo $exercise['gif_url'];   // videos/0001.gif`,

    getAll: (base) =>
`<?php

define('BASE_URL', '${base}');

// GET /exercises?page=&limit= — paginated list
function getExercises(int $page = 1, int $limit = 20): array
{
    $params = http_build_query(['page' => $page, 'limit' => $limit]);
    $json   = file_get_contents(BASE_URL . '/exercises?' . $params);
    if ($json === false) throw new RuntimeException("Request failed");
    return json_decode($json, true);
}

// Usage
$result = getExercises(1, 20);
print_r($result['data']);       // array of exercises
echo $result['total'];          // 1324
echo $result['totalPages'];     // 67`,

    getFiltered: (base) =>
`<?php

define('BASE_URL', '${base}');

// GET /exercises with filters
function getExercisesFiltered(
    int $page = 1,
    int $limit = 20,
    ?string $category  = null,
    ?string $bodyPart  = null,
    ?string $equipment = null,
    ?string $target    = null
): array {
    $params = ['page' => $page, 'limit' => $limit];
    if ($category)  $params['category']  = $category;
    if ($bodyPart)  $params['body_part'] = $bodyPart;
    if ($equipment) $params['equipment'] = $equipment;
    if ($target)    $params['target']    = $target;

    $json = file_get_contents(BASE_URL . '/exercises?' . http_build_query($params));
    if ($json === false) throw new RuntimeException("Request failed");
    return json_decode($json, true);
}

// Usage
$result = getExercisesFiltered(1, 20, 'Strength', 'Chest');
echo $result['data'][0]['name']; // e.g. Barbell Bench Press`,
  },

  go: {
    getOne: (base) =>
`package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

const baseURL = "${base}"

// GetExercise fetches a single exercise by ID
func GetExercise(id string) (map[string]any, error) {
    resp, err := http.Get(baseURL + "/exercises/" + id)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != 200 {
        return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
    }

    var result map[string]any
    return result, json.NewDecoder(resp.Body).Decode(&result)
}

// Usage
func main() {
    exercise, err := GetExercise("0001")
    if err != nil { panic(err) }
    fmt.Println(exercise["name"])     // Barbell Bench Press
    fmt.Println(exercise["category"]) // Strength
}`,

    getAll: (base) =>
`package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "strconv"
)

const baseURL = "${base}"

// GetExercises fetches paginated exercises
func GetExercises(page, limit int) (map[string]any, error) {
    params := url.Values{
        "page":  {strconv.Itoa(page)},
        "limit": {strconv.Itoa(limit)},
    }
    resp, err := http.Get(baseURL + "/exercises?" + params.Encode())
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result map[string]any
    return result, json.NewDecoder(resp.Body).Decode(&result)
}

// Usage
func main() {
    result, err := GetExercises(1, 20)
    if err != nil { panic(err) }
    fmt.Println(result["total"])      // 1324
    fmt.Println(result["totalPages"]) // 67
}`,

    getFiltered: (base) =>
`package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "strconv"
)

const baseURL = "${base}"

// GetExercisesFiltered fetches exercises with optional filters
func GetExercisesFiltered(page, limit int, category, bodyPart, equipment string) (map[string]any, error) {
    params := url.Values{
        "page":  {strconv.Itoa(page)},
        "limit": {strconv.Itoa(limit)},
    }
    if category  != "" { params.Set("category",  category) }
    if bodyPart  != "" { params.Set("body_part", bodyPart) }
    if equipment != "" { params.Set("equipment", equipment) }

    resp, err := http.Get(baseURL + "/exercises?" + params.Encode())
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result map[string]any
    return result, json.NewDecoder(resp.Body).Decode(&result)
}

// Usage
func main() {
    result, err := GetExercisesFiltered(1, 20, "Strength", "Chest", "")
    if err != nil { panic(err) }
    data := result["data"].([]any)
    fmt.Println(data[0].(map[string]any)["name"]) // e.g. Barbell Bench Press
}`,
  },
};

// ══════════════════════════════════════════════════
// ── LLM Prompt Templates ───────────────────────────
// ══════════════════════════════════════════════════

const FRAMEWORK_META = {
  express:  { name: 'Express.js (Node.js)', lang: 'JavaScript', pkg: 'express, pg / mysql2 / better-sqlite3', run: 'node index.js' },
  fastapi:  { name: 'FastAPI (Python)',      lang: 'Python',     pkg: 'fastapi, uvicorn, sqlalchemy, psycopg2-binary', run: 'uvicorn main:app --reload' },
  aspnet:   { name: 'ASP.NET Core (C#)',     lang: 'C#',         pkg: 'Npgsql / MySql.Data / Microsoft.Data.Sqlite', run: 'dotnet run' },
  spring:   { name: 'Spring Boot (Java)',    lang: 'Java',       pkg: 'spring-web, spring-data-jpa, db driver', run: 'mvn spring-boot:run' },
  laravel:  { name: 'Laravel (PHP)',         lang: 'PHP',        pkg: 'laravel/laravel, db driver', run: 'php artisan serve' },
  gin:      { name: 'Gin (Go)',              lang: 'Go',         pkg: 'gin-gonic/gin, database/sql + db driver', run: 'go run main.go' },
};

const DB_META = {
  postgresql: { name: 'PostgreSQL' },
  mysql:      { name: 'MySQL' },
  mssql:      { name: 'SQL Server' },
  sqlite:     { name: 'SQLite' },
};

function buildLlmPrompt(fwKey, dbKey) {
  const fw = FRAMEWORK_META[fwKey];
  const db = DB_META[dbKey];
  const schema = DB_SQL[dbKey];

  return `You are a senior ${fw.lang} developer. Build a complete REST API using ${fw.name} for an exercise/fitness database.

## Dataset Overview
- 1,324 fitness exercises
- Fields: id (string, e.g. "0001"), name, category, body_part, equipment, instructions_en (full text), instructions_es (Spanish text), instructions_it (Italian text), instructions_tr (Turkish text), instructions_ru (Russian text), instructions_zh (Chinese text), muscle_group, secondary_muscles (JSON array of strings), target, image (relative path like "images/0001.jpg"), gif_url (relative path like "videos/0001.gif"), created_at

## Database Schema (${db.name})
\`\`\`sql
${schema}
\`\`\`

## Required Endpoints

### 1. GET /exercises/:id
- Return a single exercise by its id
- Return 404 JSON error if not found: { "error": "Exercise not found" }

### 2. GET /exercises
Query parameters (all optional):
- page (integer, default: 1)
- limit (integer, default: 20, max: 100)
- category (string, case-insensitive partial match)
- body_part (string, case-insensitive partial match)
- equipment (string, case-insensitive partial match)
- muscle_group (string, case-insensitive partial match)
- target (string, case-insensitive partial match)

Response format:
\`\`\`json
{
  "data": [ /* array of exercise objects */ ],
  "total": 1324,
  "page": 1,
  "limit": 20,
  "totalPages": 67
}
\`\`\`

### 3. GET /exercises/random
- Return 1 random exercise object

### 4. GET /categories
- Return sorted array of unique category strings

### 5. GET /body-parts
- Return sorted array of unique body_part strings

### 6. GET /equipment
- Return sorted array of unique equipment strings

## Technical Requirements
- Read DB connection string from an environment variable
- Use parameterized queries (never interpolate user input into SQL)
- Return JSON with Content-Type: application/json
- Enable CORS for all origins (or read allowed origins from env var \`ALLOWED_ORIGINS\`)
- Validate page/limit: must be positive integers; reject with 400 if invalid
- Return 500 with { "error": "Internal server error" } on unexpected errors
- Log each request: method, path, status code, duration in ms

## Packages to use
${fw.pkg}

## Deliverables
1. Complete, runnable source code (not pseudocode — every file needed)
2. Brief setup instructions:
   - Install dependencies
   - Run: \`${fw.run}\`

Write clean, production-quality code. Do not skip error handling.`;
}

// ══════════════════════════════════════════════════
// ── State ──────────────────────────────────────────
// ══════════════════════════════════════════════════

let currentDb   = 'mssql';
let currentLang = 'curl';
let currentFw   = 'express';
let currentLlmDb = 'postgresql';
let currentBaseUrl = 'https://api.yourapp.com';
let EXERCISES = null; // lazy-loaded when generate is clicked

// ══════════════════════════════════════════════════
// ── DB Setup ───────────────────────────────────────
// ══════════════════════════════════════════════════

const createSqlEl   = document.getElementById('create-table-sql');
const copyCreateBtn = document.getElementById('copy-create-btn');
const generateBtn   = document.getElementById('generate-sql-btn');
const generateStatus = document.getElementById('generate-status');

function switchDbTab(db) {
  currentDb = db;
  document.querySelectorAll('#db-tabs .tab-btn').forEach(t => {
    t.classList.toggle('active', t.dataset.db === db);
  });
  createSqlEl.textContent = DB_SQL[db];
}

function copyToClipboard(text, btn, resetLabel = 'Copy') {
  navigator.clipboard.writeText(text).then(() => {
    btn.textContent = 'Copied!';
    btn.classList.add('copied');
    setTimeout(() => {
      btn.textContent = resetLabel;
      btn.classList.remove('copied');
    }, 2000);
  });
}

function escStr(val, db) {
  if (val === null || val === undefined) return 'NULL';
  const s = String(val).replace(/'/g, "''");
  return db === 'mssql' ? `N'${s}'` : `'${s}'`;
}

function buildInserts(exercises, db) {
  const lines = [];
  const header = db === 'mssql' ? 'BEGIN TRANSACTION;\nGO\n' : 'BEGIN;\n';
  const footer = db === 'mssql' ? '\nCOMMIT;\nGO' : '\nCOMMIT;';
  lines.push(header);

  exercises.forEach((ex, i) => {
    const muscles = JSON.stringify(Array.isArray(ex.secondary_muscles) ? ex.secondary_muscles : []);
    const instrEn = ex.instructions && ex.instructions.en
      ? ex.instructions.en
      : (Array.isArray(ex.instruction_steps) ? ex.instruction_steps.join(' ') : (ex.instructions || ''));
    const instrEs = ex.instructions && ex.instructions.es ? ex.instructions.es : '';
    const instrIt = ex.instructions && ex.instructions.it ? ex.instructions.it : '';
    const instrTr = ex.instructions && ex.instructions.tr ? ex.instructions.tr : '';
    const instrRu = ex.instructions && ex.instructions.ru ? ex.instructions.ru : '';
    const instrZh = ex.instructions && ex.instructions.zh ? ex.instructions.zh : '';

    const vals = [
      escStr(ex.id, db),
      escStr(ex.name, db),
      escStr(ex.category, db),
      escStr(ex.body_part, db),
      escStr(ex.equipment, db),
      escStr(instrEn, db),
      escStr(instrEs, db),
      escStr(instrIt, db),
      escStr(instrTr, db),
      escStr(instrRu, db),
      escStr(instrZh, db),
      escStr(ex.muscle_group, db),
      escStr(muscles, db),
      escStr(ex.target, db),
      escStr(ex.image, db),
      escStr(ex.gif_url, db),
      escStr(ex.created_at, db),
    ].join(', ');

    lines.push(`INSERT INTO exercises (id, name, category, body_part, equipment, instructions_en, instructions_es, instructions_it, instructions_tr, instructions_ru, instructions_zh, muscle_group, secondary_muscles, target, image, gif_url, created_at) VALUES (${vals});`);

    if (db === 'mssql' && (i + 1) % 50 === 0 && i + 1 < exercises.length) {
      lines.push('GO');
    }
  });

  lines.push(footer);
  return lines.join('\n');
}

async function generateInsertSql() {
  generateBtn.disabled = true;

  try {
    if (!EXERCISES) {
      generateStatus.textContent = 'Loading exercise data…';
      const res = await fetch('data/exercises.json');
      if (!res.ok) throw new Error('Could not load exercises.json');
      EXERCISES = await res.json();
    }

    generateStatus.textContent = 'Generating…';

    await new Promise(r => setTimeout(r, 10));

    const sql  = buildInserts(EXERCISES, currentDb);
    const blob = new Blob([sql], { type: 'text/plain;charset=utf-8' });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement('a');
    a.href     = url;
    a.download = `exercises_insert_${currentDb}.sql`;
    a.click();
    URL.revokeObjectURL(url);
    generateStatus.textContent = `✓ Downloaded exercises_insert_${currentDb}.sql`;
  } catch (e) {
    generateStatus.textContent = 'Error generating file.';
    console.error(e);
  }

  generateBtn.disabled = false;
}

// Wire DB tab events
document.querySelectorAll('#db-tabs .tab-btn').forEach(tab => {
  tab.addEventListener('click', () => switchDbTab(tab.dataset.db));
});
copyCreateBtn.addEventListener('click', () => copyToClipboard(createSqlEl.textContent, copyCreateBtn));
generateBtn.addEventListener('click', generateInsertSql);

// Init
switchDbTab('mssql');

// ══════════════════════════════════════════════════
// ── API Integration ────────────────────────────────
// ══════════════════════════════════════════════════

const baseUrlInput = document.getElementById('base-url-input');

function renderApiCode() {
  const templates = API_TEMPLATES[currentLang];
  const base = currentBaseUrl || 'https://api.yourapp.com';
  document.getElementById('code-getOne').textContent      = templates.getOne(base);
  document.getElementById('code-getAll').textContent      = templates.getAll(base);
  document.getElementById('code-getFiltered').textContent = templates.getFiltered(base);
}

function switchLangTab(lang) {
  currentLang = lang;
  document.querySelectorAll('#lang-tabs .tab-btn').forEach(t => {
    t.classList.toggle('active', t.dataset.lang === lang);
  });
  renderApiCode();
}

baseUrlInput.addEventListener('input', () => {
  currentBaseUrl = baseUrlInput.value.trim() || 'https://api.yourapp.com';
  renderApiCode();
});

document.querySelectorAll('#lang-tabs .tab-btn').forEach(tab => {
  tab.addEventListener('click', () => switchLangTab(tab.dataset.lang));
});

// Copy button for code blocks
function copyBlock(btn) {
  const pre = btn.nextElementSibling;
  copyToClipboard(pre.textContent, btn);
}

// Init
renderApiCode();

// ══════════════════════════════════════════════════
// ── Ask Your LLM ───────────────────────────────────
// ══════════════════════════════════════════════════

const llmPromptBox  = document.getElementById('llm-prompt');
const copyPromptBtn = document.getElementById('copy-prompt-btn');

function renderLlmPrompt() {
  llmPromptBox.value = buildLlmPrompt(currentFw, currentLlmDb);
}

document.querySelectorAll('#framework-tabs .selector-btn').forEach(btn => {
  btn.addEventListener('click', () => {
    currentFw = btn.dataset.fw;
    document.querySelectorAll('#framework-tabs .selector-btn').forEach(b => b.classList.remove('active'));
    btn.classList.add('active');
    renderLlmPrompt();
  });
});

document.querySelectorAll('#llm-db-tabs .selector-btn').forEach(btn => {
  btn.addEventListener('click', () => {
    currentLlmDb = btn.dataset.llmdb;
    document.querySelectorAll('#llm-db-tabs .selector-btn').forEach(b => b.classList.remove('active'));
    btn.classList.add('active');
    renderLlmPrompt();
  });
});

copyPromptBtn.addEventListener('click', () => {
  navigator.clipboard.writeText(llmPromptBox.value).then(() => {
    copyPromptBtn.textContent = '✓ Copied!';
    copyPromptBtn.classList.add('copied');
    setTimeout(() => {
      copyPromptBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="9" height="11" rx="1.5"/><path d="M11 4V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h1"/></svg> Copy Prompt`;
      copyPromptBtn.classList.remove('copied');
    }, 2500);
  });
});

// Init
renderLlmPrompt();

// ══════════════════════════════════════════════════
// ── Scrollspy ──────────────────────────────────────
// ══════════════════════════════════════════════════

const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav-link[data-section]');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      navLinks.forEach(link => {
        link.classList.toggle('active', link.dataset.section === entry.target.id);
      });
    }
  });
}, { rootMargin: '-20% 0px -70% 0px', threshold: 0 });

sections.forEach(s => observer.observe(s));
</script>
</body>
</html>
