<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>M1730 Scale Generator — Baumann Works</title>
  <script defer src="https://umami.baumann.gr/script.js" data-website-id="80b6816b-2a2d-4fab-a315-c389542bcd17"></script>
  <script defer src="https://umami.baumann.gr/recorder.js" data-website-id="80b6816b-2a2d-4fab-a315-c389542bcd17" data-sample-rate="1" data-mask-level="moderate" data-max-duration="300000"></script>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@600;700&family=DM+Sans:wght@400;500&family=Space+Mono&display=swap" rel="stylesheet">
  <style>
    :root {
      --bg:      #f5f1ea;
      --ink:     #1c1814;
      --accent:  #c45c28;
      --muted:   #6b6159;
      --rule:    #ddd6cc;
      --white:   #ffffff;
      --f-disp:  'Barlow Condensed', sans-serif;
      --f-body:  'DM Sans', sans-serif;
      --f-mono:  'Space Mono', monospace;
    }

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

    body {
      background: var(--bg);
      color: var(--ink);
      font-family: var(--f-body);
      font-size: 15px;
      min-height: 100vh;
    }

    /* ── Header ── */
    header {
      display: flex;
      align-items: center;
      gap: 1rem;
      padding: 1.25rem 2.5rem;
      border-bottom: 1px solid var(--rule);
      background: var(--white);
    }

    header img {
      height: 52px;
      width: auto;
      display: block;
    }

    .wordmark {
      display: flex;
      flex-direction: column;
      gap: 0.1rem;
    }

    .wordmark-name {
      font-family: var(--f-disp);
      font-weight: 700;
      font-size: 1.45rem;
      letter-spacing: 0.07em;
      text-transform: uppercase;
      line-height: 1;
      color: var(--ink);
    }

    .wordmark-sub {
      font-family: var(--f-body);
      font-size: 0.75rem;
      font-weight: 400;
      color: var(--muted);
      letter-spacing: 0.03em;
    }

    /* ── Layout ── */
    main {
      max-width: 900px;
      margin: 0 auto;
      padding: 2.5rem 2rem 4rem;
    }

    /* ── Form sections ── */
    .section {
      padding-bottom: 1.75rem;
      margin-bottom: 1.75rem;
      border-bottom: 1px solid var(--rule);
    }

    .section-eye {
      font-family: var(--f-disp);
      font-weight: 600;
      font-size: 0.68rem;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--accent);
      margin-bottom: 0.9rem;
    }

    .row {
      display: grid;
      gap: 1.25rem 2rem;
    }
    .row-2 { grid-template-columns: 1fr 1fr; }
    .row-3 { grid-template-columns: 1fr 1fr 1fr; }

    /* ── Fields ── */
    .field { display: flex; flex-direction: column; gap: 0.35rem; }

    .field label {
      font-size: 0.75rem;
      font-weight: 500;
      color: var(--muted);
      letter-spacing: 0.025em;
    }

    .field input {
      background: var(--white);
      border: 1px solid var(--rule);
      border-radius: 1px;
      padding: 0.5rem 0.7rem;
      font-family: var(--f-mono);
      font-size: 0.875rem;
      color: var(--ink);
      width: 100%;
      transition: border-color 0.12s;
      outline: none;
    }

    .field input[type="text"] {
      font-family: var(--f-body);
      font-size: 0.925rem;
    }

    .field input:focus { border-color: var(--accent); }

    /* ── Mode toggle ── */
    .mode-toggle {
      display: inline-flex;
      border: 1px solid var(--rule);
      border-radius: 1px;
      overflow: hidden;
    }

    .mode-btn {
      font-family: var(--f-mono);
      font-size: 0.72rem;
      padding: 0.35rem 0.85rem;
      border: none;
      background: var(--white);
      color: var(--muted);
      cursor: pointer;
      transition: background 0.12s, color 0.12s;
      line-height: 1;
      outline: none;
    }

    .mode-btn + .mode-btn {
      border-left: 1px solid var(--rule);
    }

    .mode-btn:focus,
    .mode-btn:focus-visible {
      outline: none;
      box-shadow: none;
    }

    .mode-btn.active {
      background: var(--ink);
      color: var(--white);
    }

    /* ── Actions ── */
    .actions {
      display: flex;
      gap: 0.75rem;
      align-items: center;
      padding-top: 1.75rem;
      flex-wrap: wrap;
    }

    .btn {
      font-family: var(--f-disp);
      font-weight: 600;
      font-size: 0.9rem;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      border-radius: 1px;
      padding: 0.55rem 1.6rem;
      cursor: pointer;
      border: none;
      transition: background 0.12s, color 0.12s, border-color 0.12s;
    }

    .btn-primary {
      background: var(--accent);
      color: var(--white);
    }
    .btn-primary:hover { background: #a84d20; }

    .btn-outline {
      background: transparent;
      color: var(--ink);
      border: 1px solid var(--rule);
    }
    .btn-outline:hover:not(:disabled) { border-color: var(--ink); }
    .btn-outline:disabled { opacity: 0.35; cursor: not-allowed; }

    #status {
      font-family: var(--f-mono);
      font-size: 0.75rem;
      color: var(--muted);
    }

    /* ── Preview / light table ── */
    .preview-eye {
      font-family: var(--f-disp);
      font-weight: 600;
      font-size: 0.68rem;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--muted);
      margin-bottom: 0.9rem;
      margin-top: 2.5rem;
    }

    .light-table {
      background-color: #e8e3db;
      background-image: radial-gradient(circle, #bfb8ae 1px, transparent 1px);
      background-size: 18px 18px;
      border-radius: 2px;
      padding: 2.5rem 2rem;
      display: flex;
      align-items: center;
      justify-content: center;
      min-height: 120px;
    }

    .light-table svg {
      width: 100%;
      max-width: 740px;
      height: auto;
      display: block;
      box-shadow: 0 1px 4px rgba(0,0,0,0.1), 0 4px 16px rgba(0,0,0,0.08);
    }

    #preview-placeholder {
      font-family: var(--f-mono);
      font-size: 0.8rem;
      color: #a89e94;
    }

    /* ── Responsive ── */
    @media (max-width: 600px) {
      header { padding: 1rem 1.25rem; }
      main   { padding: 1.5rem 1.25rem 3rem; }
      .row-2, .row-3 { grid-template-columns: 1fr; }
    }

    @media (prefers-reduced-motion: reduce) {
      * { transition: none !important; }
    }

    .version-tag {
      position: fixed;
      bottom: 0.75rem;
      right: 1rem;
      font-family: var(--f-mono);
      font-size: 0.65rem;
      color: var(--muted);
      pointer-events: none;
      user-select: none;
    }

    .version-tag a {
      color: var(--muted);
      text-decoration: underline;
      pointer-events: all;
    }

    .version-tag a:hover {
      color: var(--ink);
    }
  </style>
</head>
<body>

  <header>
    <img src="data:image/png;base64,{{ logo_b64 }}" alt="Baumann Works">
    <div class="wordmark">
      <span class="wordmark-name">Baumann Works</span>
      <span class="wordmark-sub">M1730 Scale Generator</span>
    </div>
  </header>

  <main>
    <form id="form">

      <div class="section">
        <div class="section-eye">Labels</div>
        <div class="row row-2">
          <div class="field">
            <label>Unit</label>
            <input name="unit" type="text" value="%" required>
            <span style="font-family:var(--f-mono);font-size:0.65rem;color:var(--muted)">Markup: cm^2 · H_2O · {1/2} fraction · nests, e.g. {m/s^2}</span>
            <div style="display:flex;align-items:center;gap:0.5rem;margin-top:0.2rem">
              <div class="mode-toggle" role="group" aria-label="Unit height">
                <button type="button" class="mode-btn active" data-unit-height="normal">Full height</button>
                <button type="button" class="mode-btn" data-unit-height="half"
                  title="Restrict unit label to the upper half of the panel — use when numeric labels visually crowd the unit">Half height</button>
              </div>
              <input type="hidden" name="half_height_unit" id="half-height-unit" value="0">
            </div>
          </div>
          <div class="field">
            <label>Range label</label>
            <input name="range_label" type="text" value="5mA" required>
          </div>
        </div>
      </div>

      <div class="section">
        <div class="section-eye">Scale range</div>
        <div class="row row-3">
          <div class="field">
            <label>Mapping</label>
            <div class="mode-toggle" role="group" aria-label="Scale mapping">
              <button type="button" class="mode-btn active" data-scale="linear"
                title="Values spaced evenly along the scale">Linear</button>
              <button type="button" class="mode-btn" data-scale="sqrt"
                title="Square-root scale — ticks crowd at the low end; used for RMS current/power meters. Min must be ≥ 0">√</button>
              <button type="button" class="mode-btn" data-scale="log"
                title="Values spaced logarithmically — requires min and max greater than 0">Log</button>
              <button type="button" class="mode-btn" data-scale="reciprocal"
                title="Reciprocal (1/x) scale — values decrease left to right; used for resistance (ohms) scales. Min and Max must be greater than 0, Max greater than Min">1/x</button>
            </div>
          </div>
          <div class="field">
            <label>Min value</label>
            <input name="min" type="number" step="any" value="0" required>
          </div>
          <div class="field" id="field-max">
            <label>Max value</label>
            <input name="max" id="input-max" type="number" step="any" value="100" required>
            <button type="button" id="btn-inf" style="display:none;font-family:var(--f-mono);font-size:0.65rem;color:var(--muted);background:none;border:none;padding:0;cursor:pointer;text-align:left">set max = ∞</button>
            <input type="hidden" name="max_is_inf" id="max-is-inf" value="">
          </div>
        </div>
        <input type="hidden" name="scale_type" id="scale-type" value="linear">
      </div>

      <div class="section">
        <div class="section-eye">Tick marks</div>
        <div class="row row-3">
          <div class="field">
            <label>Major intervals</label>
            <input name="big_ticks" type="number" min="1" max="50" value="10" required>
          </div>
          <div class="field">
            <label>Minor per interval</label>
            <input name="small_ticks" type="number" min="0" max="20" value="4" required>
          </div>
          <div class="field">
            <label>Labels</label>
            <input name="label_count" type="number" min="2" value="6" required>
            <span style="font-family:var(--f-mono);font-size:0.65rem;color:var(--muted)">(Labels minus one) should divide evenly into Major intervals for best appearance</span>
          </div>
        </div>
        <div class="row row-2" style="margin-top:1.25rem">
          <div class="field">
            <label>Custom labels (optional)</label>
            <input name="labels" type="text" placeholder="e.g. Lo, 25, Mid, 75, Hi — leave blank to use calculated values">
            <span style="font-family:var(--f-mono);font-size:0.65rem;color:var(--muted)">Comma-separated. When set, overrides the calculated numbers and its count sets the number of labels.</span>
          </div>
          <div class="field">
            <label>Label size (mm)</label>
            <input name="label_size" type="number" step="any" min="0.5" max="25" value="9" required>
          </div>
        </div>
        <div class="row row-2" style="margin-top:1.25rem">
          <div class="field">
            <label>Font</label>
            <select name="font" id="font-select" style="background:var(--white);border:1px solid var(--rule);border-radius:1px;padding:0.5rem 0.7rem;font-family:var(--f-body);font-size:0.875rem;color:var(--ink);width:100%;outline:none;cursor:pointer">
              {% for f in fonts %}
              <option value="{{ f.key }}"{% if f.key == default_font %} selected{% endif %}>{{ f.label }}</option>
              {% endfor %}
            </select>
          </div>
        </div>
      </div>

      <div class="actions">
        <button type="submit" class="btn btn-primary">Generate</button>
        <button type="button" id="btn-download" class="btn btn-outline" disabled>Download SVG</button>
        <div style="display:flex;flex-direction:column;gap:0.3rem;align-items:flex-start">
          <div class="mode-toggle" role="group" aria-label="Output mode">
            <button type="button" class="mode-btn active" data-mode="paths"
              title="Text converted to outlines — opens correctly in Inkscape, Illustrator, and cutting plotters; no font required">Paths</button>
            <button type="button" class="mode-btn" data-mode="text"
              title="Live &lt;text&gt; nodes with the font embedded as base64 — smaller file, editable in SVG editors that support embedded fonts">Text</button>
          </div>
          <span id="mode-desc" style="font-family:var(--f-mono);font-size:0.65rem;color:var(--muted)">Glyphs as outlines — no font required</span>
        </div>
        <input type="hidden" name="output_mode" id="output-mode" value="paths">
        <span id="status"></span>
      </div>

    </form>

    <div class="preview-eye">Preview</div>
    <div class="light-table" id="light-table">
      <span id="preview-placeholder">—</span>
    </div>
  </main>

  <span class="version-tag">v{{ version }} — <a href="https://git.baumann.gr/adebaumann/M1730-Scale" target="_blank" rel="noopener">Source</a></span>

  <script>
    let lastSvg = '';

    function resetInf() {
      const inp = document.getElementById('input-max');
      const hidden = document.getElementById('max-is-inf');
      const btn = document.getElementById('btn-inf');
      inp.style.display = '';
      inp.required = true;
      hidden.value = '';
      btn.textContent = 'set max = ∞';
    }

    document.getElementById('btn-inf').addEventListener('click', () => {
      const inp = document.getElementById('input-max');
      const hidden = document.getElementById('max-is-inf');
      const btn = document.getElementById('btn-inf');
      if (hidden.value === '1') {
        resetInf();
      } else {
        inp.style.display = 'none';
        inp.required = false;
        hidden.value = '1';
        btn.textContent = 'max = ∞ (click to reset)';
      }
      doGenerate();
    });

    async function doGenerate() {
      const status = document.getElementById('status');
      status.textContent = 'Generating…';
      try {
        const base = window.location.href.split('?')[0].replace(/\/?$/, '/');
        const resp = await fetch(base + 'generate', {
          method: 'POST',
          body: new FormData(document.getElementById('form')),
        });
        if (!resp.ok) throw new Error(await resp.text());
        lastSvg = await resp.text();
        document.getElementById('light-table').innerHTML = lastSvg;
        document.getElementById('btn-download').disabled = false;
        status.textContent = '';
      } catch (err) {
        status.textContent = 'Error: ' + err.message;
      }
    }

    document.getElementById('form').addEventListener('submit', e => {
      e.preventDefault();
      doGenerate();
    });

    const modeDesc = {
      paths: 'Glyphs as outlines — no font required',
      text:  'Live text with embedded font — editable in SVG editors',
    };

    document.querySelectorAll('.mode-toggle').forEach(group => {
      group.querySelectorAll('.mode-btn').forEach(btn => {
        btn.addEventListener('click', () => {
          group.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
          btn.classList.add('active');
          if (btn.dataset.mode !== undefined) {
            document.getElementById('output-mode').value = btn.dataset.mode;
            document.getElementById('mode-desc').textContent = modeDesc[btn.dataset.mode];
          }
          if (btn.dataset.scale !== undefined) {
            document.getElementById('scale-type').value = btn.dataset.scale;
            const minInput = document.querySelector('input[name="min"]');
            const maxInput = document.querySelector('input[name="max"]');
            if (btn.dataset.scale === 'log') {
              if (!(parseFloat(minInput.value) > 0)) minInput.value = '1';
              if (!(parseFloat(maxInput.value) > 0)) maxInput.value = '100';
            } else if (btn.dataset.scale === 'sqrt') {
              if (parseFloat(minInput.value) < 0) minInput.value = '0';
            } else if (btn.dataset.scale === 'reciprocal') {
              if (!(parseFloat(minInput.value) > 0)) minInput.value = '1';
              if (!(parseFloat(maxInput.value) > parseFloat(minInput.value))) maxInput.value = String(parseFloat(minInput.value) * 10);
            }
            // Show/hide ∞ toggle
            const infBtn = document.getElementById('btn-inf');
            if (btn.dataset.scale === 'reciprocal') {
              infBtn.style.display = '';
            } else {
              infBtn.style.display = 'none';
              resetInf();
            }
          }
          doGenerate();
        });
      });
    });

    document.getElementById('btn-download').addEventListener('click', () => {
      if (!lastSvg) return;
      const a = Object.assign(document.createElement('a'), {
        href: URL.createObjectURL(new Blob([lastSvg], { type: 'image/svg+xml' })),
        download: 'M1730-Scale.svg',
      });
      a.click();
      URL.revokeObjectURL(a.href);
    });

    document.getElementById('font-select').addEventListener('change', doGenerate);

    document.querySelectorAll('[data-unit-height]').forEach(btn => {
      btn.addEventListener('click', () => {
        document.querySelectorAll('[data-unit-height]').forEach(b => b.classList.remove('active'));
        btn.classList.add('active');
        document.getElementById('half-height-unit').value = btn.dataset.unitHeight === 'half' ? '1' : '0';
        doGenerate();
      });
    });

    doGenerate();
  </script>
</body>
</html>
