import { h } from 'preact';
import { useState } from 'preact/hooks';

interface Props {
  initialSection?: string;
  onClose: () => void;
}

// ── SVG Diagrams ─────────────────────────────────────────────────────────────

function DiagramViewMode() {
  return (
    <svg viewBox="0 0 580 230" class="help-diagram" role="img" aria-label="View mode layout diagram">
      {/* outer bg */}
      <rect width="580" height="230" rx="8" fill="#0f1117" />

      {/* toolbar */}
      <rect width="580" height="38" rx="0" fill="#1a1f2e" />
      <rect y="37" width="580" height="1" fill="#2d3748" />
      <text x="12" y="24" fill="#4a9eff" font-size="12" font-weight="bold">uopi</text>
      <text x="54" y="24" fill="#e2e8f0" font-size="11">Control Room</text>
      {/* live chip */}
      <rect x="488" y="10" width="80" height="18" rx="9" fill="#1e3a1e" />
      <circle cx="500" cy="19" r="4" fill="#22c55e" />
      <text x="509" y="23" fill="#22c55e" font-size="9">live</text>
      <rect x="454" y="10" width="30" height="18" rx="4" fill="#2d3748" />
      <text x="469" y="23" fill="#e2e8f0" font-size="9" text-anchor="middle">Edit</text>

      {/* interface list panel */}
      <rect x="0" y="38" width="148" height="192" fill="#151b2e" />
      <rect x="0" y="38" width="148" height="192" fill="none" stroke="#2d3748" />
      <text x="10" y="57" fill="#64748b" font-size="8">INTERFACES</text>
      <rect x="6" y="63" width="136" height="22" rx="4" fill="#2d3748" />
      <text x="14" y="79" fill="#4a9eff" font-size="10">Control Room</text>
      <rect x="6" y="89" width="136" height="22" rx="4" fill="none" />
      <text x="14" y="105" fill="#94a3b8" font-size="10">Vacuum System</text>
      <rect x="6" y="115" width="136" height="22" rx="4" fill="none" />
      <text x="14" y="131" fill="#94a3b8" font-size="10">RF Status</text>

      {/* canvas area */}
      <rect x="148" y="38" width="432" height="192" fill="#0f1117" />

      {/* gauge widget */}
      <rect x="162" y="52" width="108" height="88" rx="4" fill="#1a1f2e" stroke="#2d3748" />
      <text x="216" y="69" fill="#64748b" font-size="8" text-anchor="middle">Temperature</text>
      <circle cx="216" cy="108" r="26" fill="none" stroke="#2d3748" stroke-width="5" />
      <circle cx="216" cy="108" r="26" fill="none" stroke="#4a9eff" stroke-width="5"
        stroke-dasharray="55 108" stroke-dashoffset="27" stroke-linecap="round" />
      <text x="216" y="113" fill="#e2e8f0" font-size="10" text-anchor="middle" font-weight="bold">67°C</text>

      {/* LED */}
      <rect x="282" y="52" width="90" height="38" rx="4" fill="#1a1f2e" stroke="#2d3748" />
      <circle cx="300" cy="71" r="8" fill="#22c55e" />
      <text x="314" y="75" fill="#94a3b8" font-size="9">Beam ON</text>

      {/* text view */}
      <rect x="282" y="98" width="90" height="30" rx="4" fill="#1a1f2e" stroke="#2d3748" />
      <text x="327" y="118" fill="#e2e8f0" font-size="12" text-anchor="middle" font-weight="bold">1.23 mA</text>

      {/* bar */}
      <rect x="162" y="150" width="108" height="68" rx="4" fill="#1a1f2e" stroke="#2d3748" />
      <text x="216" y="167" fill="#64748b" font-size="8" text-anchor="middle">Pressure</text>
      <rect x="172" y="174" width="88" height="14" rx="3" fill="#2d3748" />
      <rect x="172" y="174" width="57" height="14" rx="3" fill="#4a9eff" />
      <text x="216" y="204" fill="#94a3b8" font-size="8" text-anchor="middle">65 %</text>

      {/* plot */}
      <rect x="386" y="52" width="182" height="166" rx="4" fill="#1a1f2e" stroke="#2d3748" />
      <text x="477" y="69" fill="#64748b" font-size="8" text-anchor="middle">Beam Current</text>
      <polyline points="398,195 418,180 438,170 458,175 478,160 498,155 518,158 548,145"
        fill="none" stroke="#4a9eff" stroke-width="1.5" />
      <line x1="398" y1="75" x2="398" y2="200" stroke="#2d3748" stroke-width="1" />
      <line x1="398" y1="200" x2="560" y2="200" stroke="#2d3748" stroke-width="1" />

      {/* Annotations */}
      {/* arrow to interface list */}
      <line x1="74" y1="218" x2="74" y2="170" stroke="#fbbf24" stroke-width="1.5" marker-end="url(#arr)" />
      <text x="74" y="228" fill="#fbbf24" font-size="9" text-anchor="middle">① Interface list</text>

      {/* arrow to canvas */}
      <line x1="360" y1="218" x2="360" y2="180" stroke="#fbbf24" stroke-width="1.5" marker-end="url(#arr)" />
      <text x="360" y="228" fill="#fbbf24" font-size="9" text-anchor="middle">② Live canvas</text>

      <defs>
        <marker id="arr" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto">
          <path d="M0,0 L6,3 L0,6 Z" fill="#fbbf24" />
        </marker>
      </defs>
    </svg>
  );
}

function DiagramEditMode() {
  return (
    <svg viewBox="0 0 580 230" class="help-diagram" role="img" aria-label="Edit mode layout diagram">
      <rect width="580" height="230" rx="8" fill="#0f1117" />
      {/* toolbar */}
      <rect width="580" height="38" fill="#1a1f2e" />
      <rect y="37" width="580" height="1" fill="#2d3748" />
      <text x="12" y="24" fill="#4a9eff" font-size="12" font-weight="bold">uopi</text>
      <rect x="50" y="12" width="26" height="14" rx="3" fill="#1e40af" />
      <text x="63" y="23" fill="#93c5fd" font-size="8" text-anchor="middle">Edit</text>
      <text x="86" y="24" fill="#e2e8f0" font-size="11">My Panel</text>
      {/* toolbar buttons */}
      <rect x="350" y="11" width="28" height="16" rx="3" fill="#2d3748" />
      <text x="364" y="23" fill="#e2e8f0" font-size="9" text-anchor="middle">↩</text>
      <rect x="382" y="11" width="28" height="16" rx="3" fill="#2d3748" />
      <text x="396" y="23" fill="#e2e8f0" font-size="9" text-anchor="middle">↪</text>
      <rect x="480" y="11" width="36" height="16" rx="3" fill="#2563eb" />
      <text x="498" y="23" fill="#fff" font-size="9" text-anchor="middle">Save</text>
      <rect x="520" y="11" width="40" height="16" rx="3" fill="#2d3748" />
      <text x="540" y="23" fill="#e2e8f0" font-size="9" text-anchor="middle">✕ Close</text>

      {/* signal tree */}
      <rect x="0" y="38" width="140" height="192" fill="#151b2e" />
      <rect x="0" y="38" width="140" height="192" fill="none" stroke="#2d3748" />
      <text x="10" y="57" fill="#64748b" font-size="8">SIGNALS</text>
      <rect x="4" y="63" width="132" height="20" rx="3" fill="#1a2236" stroke="#2d3748" />
      <text x="10" y="78" fill="#64748b" font-size="9">Filter signals…</text>
      <text x="10" y="100" fill="#94a3b8" font-size="9">▾ epics</text>
      <text x="20" y="115" fill="#cbd5e1" font-size="9">  CURRENT</text>
      <text x="20" y="129" fill="#cbd5e1" font-size="9">  PRESSURE</text>
      <text x="10" y="148" fill="#94a3b8" font-size="9">▾ stub</text>
      <text x="20" y="163" fill="#cbd5e1" font-size="9">  sine_1hz</text>
      <text x="20" y="177" fill="#cbd5e1" font-size="9">  ramp_10s</text>

      {/* canvas */}
      <rect x="140" y="38" width="288" height="192" fill="#0f1117" />
      {/* grid dots */}
      {[0,1,2,3,4,5,6,7,8].map(col =>
        [0,1,2,3,4,5,6].map(row => (
          <circle key={`${col}-${row}`} cx={140 + 10 + col * 30} cy={38 + 10 + row * 26}
            r="0.8" fill="#2d3748" />
        ))
      )}
      {/* selected widget with handles */}
      <rect x="185" y="75" width="120" height="60" rx="4" fill="#1a1f2e" stroke="#4a9eff" stroke-width="1.5" />
      <text x="245" y="110" fill="#e2e8f0" font-size="11" text-anchor="middle" font-weight="bold">42.3°C</text>
      <text x="245" y="90" fill="#64748b" font-size="8" text-anchor="middle">Temperature</text>
      {/* resize handles */}
      <rect x="182" y="72" width="8" height="8" rx="1" fill="#4a9eff" />
      <rect x="301" y="72" width="8" height="8" rx="1" fill="#4a9eff" />
      <rect x="182" y="131" width="8" height="8" rx="1" fill="#4a9eff" />
      <rect x="301" y="131" width="8" height="8" rx="1" fill="#4a9eff" />
      {/* another widget */}
      <rect x="220" y="155" width="80" height="30" rx="4" fill="#1a1f2e" stroke="#2d3748" />
      <rect x="230" y="163" width="60" height="10" rx="2" fill="#2d3748" />
      <rect x="230" y="163" width="38" height="10" rx="2" fill="#22c55e" />

      {/* properties pane */}
      <rect x="428" y="38" width="152" height="192" fill="#1a1f2e" />
      <rect x="428" y="38" width="152" height="192" fill="none" stroke="#2d3748" />
      <text x="438" y="56" fill="#64748b" font-size="8">PROPERTIES</text>
      <line x1="428" y1="61" x2="580" y2="61" stroke="#2d3748" />
      <text x="438" y="78" fill="#94a3b8" font-size="8">Signal</text>
      <rect x="438" y="82" width="132" height="16" rx="3" fill="#1a2236" stroke="#2d3748" />
      <text x="444" y="94" fill="#e2e8f0" font-size="9">TEMPERATURE</text>
      <text x="438" y="112" fill="#94a3b8" font-size="8">X</text>
      <rect x="438" y="116" width="58" height="14" rx="3" fill="#1a2236" stroke="#2d3748" />
      <text x="444" y="127" fill="#e2e8f0" font-size="9">185</text>
      <text x="504" y="112" fill="#94a3b8" font-size="8">Y</text>
      <rect x="504" y="116" width="58" height="14" rx="3" fill="#1a2236" stroke="#2d3748" />
      <text x="510" y="127" fill="#e2e8f0" font-size="9">75</text>
      <text x="438" y="145" fill="#94a3b8" font-size="8">Width</text>
      <rect x="438" y="149" width="58" height="14" rx="3" fill="#1a2236" stroke="#2d3748" />
      <text x="444" y="160" fill="#e2e8f0" font-size="9">120</text>
      <text x="504" y="145" fill="#94a3b8" font-size="8">Height</text>
      <rect x="504" y="149" width="58" height="14" rx="3" fill="#1a2236" stroke="#2d3748" />
      <text x="510" y="160" fill="#e2e8f0" font-size="9">60</text>

      {/* Annotations */}
      <text x="70" y="218" fill="#fbbf24" font-size="9" text-anchor="middle">① Signal tree</text>
      <text x="284" y="218" fill="#fbbf24" font-size="9" text-anchor="middle">② Canvas</text>
      <text x="504" y="218" fill="#fbbf24" font-size="9" text-anchor="middle">③ Properties</text>
    </svg>
  );
}

function DiagramDragDrop() {
  return (
    <svg viewBox="0 0 580 200" class="help-diagram" role="img" aria-label="Drag and drop workflow">
      <rect width="580" height="200" rx="8" fill="#0f1117" />

      {/* Step 1 */}
      <rect x="10" y="10" width="160" height="180" rx="6" fill="#151b2e" stroke="#2d3748" />
      <text x="90" y="30" fill="#64748b" font-size="9" text-anchor="middle">① SIGNAL TREE</text>
      <text x="20" y="55" fill="#94a3b8" font-size="9">▾ epics</text>
      <rect x="16" y="62" width="148" height="22" rx="4" fill="#1e3a5f" stroke="#4a9eff" />
      <text x="26" y="78" fill="#4a9eff" font-size="10">TEMPERATURE</text>
      <text x="26" y="78" fill="none" stroke="#4a9eff" stroke-width="0" />
      <text x="20" y="100" fill="#94a3b8" font-size="9">  PRESSURE</text>
      <text x="20" y="115" fill="#94a3b8" font-size="9">  CURRENT</text>
      {/* drag cursor hint */}
      <text x="90" y="175" fill="#64748b" font-size="8" text-anchor="middle">drag signal →</text>

      {/* Arrow 1→2 */}
      <line x1="178" y1="100" x2="218" y2="100" stroke="#fbbf24" stroke-width="2"
        marker-end="url(#arr2)" stroke-dasharray="4 2" />

      {/* Step 2 */}
      <rect x="220" y="10" width="160" height="180" rx="6" fill="#0f1117" stroke="#4a9eff" stroke-dasharray="4 3" />
      <text x="300" y="30" fill="#64748b" font-size="9" text-anchor="middle">② DROP ON CANVAS</text>
      {/* ghost drag indicator */}
      <rect x="235" y="75" width="130" height="50" rx="4" fill="#1e3a5f" stroke="#4a9eff" stroke-width="1.5" stroke-dasharray="4 2" />
      <text x="300" y="105" fill="#4a9eff" font-size="10" text-anchor="middle">TEMPERATURE</text>
      <text x="300" y="175" fill="#64748b" font-size="8" text-anchor="middle">drop here</text>

      {/* Arrow 2→3 */}
      <line x1="388" y1="100" x2="408" y2="100" stroke="#fbbf24" stroke-width="2"
        marker-end="url(#arr2)" stroke-dasharray="4 2" />

      {/* Step 3: widget picker */}
      <rect x="410" y="10" width="160" height="180" rx="6" fill="#1a1f2e" stroke="#2d3748" />
      <text x="490" y="30" fill="#64748b" font-size="9" text-anchor="middle">③ PICK WIDGET TYPE</text>
      <rect x="420" y="40" width="140" height="24" rx="4" fill="#2d3748" />
      <text x="490" y="57" fill="#e2e8f0" font-size="10" text-anchor="middle">Gauge</text>
      <rect x="420" y="68" width="140" height="24" rx="4" fill="#2d3748" />
      <text x="490" y="85" fill="#e2e8f0" font-size="10" text-anchor="middle">Bar (horizontal)</text>
      <rect x="420" y="96" width="140" height="24" rx="4" fill="#2d3748" />
      <text x="490" y="113" fill="#e2e8f0" font-size="10" text-anchor="middle">Text View</text>
      <rect x="420" y="124" width="140" height="24" rx="4" fill="#1e40af" />
      <text x="490" y="141" fill="#93c5fd" font-size="10" text-anchor="middle">Plot</text>
      <rect x="420" y="152" width="140" height="24" rx="4" fill="#2d3748" />
      <text x="490" y="169" fill="#e2e8f0" font-size="10" text-anchor="middle">LED</text>

      <defs>
        <marker id="arr2" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
          <path d="M0,0 L6,3 L0,6 Z" fill="#fbbf24" />
        </marker>
      </defs>
    </svg>
  );
}

function DiagramWidgets() {
  return (
    <svg viewBox="0 0 580 220" class="help-diagram" role="img" aria-label="Widget types overview">
      <rect width="580" height="220" rx="8" fill="#0f1117" />
      {/* Text View */}
      <g>
        <rect x="20" y="20" width="110" height="56" rx="4" fill="#1a1f2e" stroke="#2d3748" />
        <text x="75" y="50" fill="#e2e8f0" font-size="14" text-anchor="middle" font-weight="bold">42.7 °C</text>
        <text x="75" y="66" fill="#64748b" font-size="8" text-anchor="middle">Text View</text>
      </g>
      {/* Gauge */}
      <g>
        <rect x="150" y="20" width="110" height="90" rx="4" fill="#1a1f2e" stroke="#2d3748" />
        <circle cx="205" cy="82" r="26" fill="none" stroke="#2d3748" stroke-width="5" />
        <circle cx="205" cy="82" r="26" fill="none" stroke="#4a9eff" stroke-width="5"
          stroke-dasharray="52 108" stroke-dashoffset="27" stroke-linecap="round" />
        <text x="205" y="87" fill="#e2e8f0" font-size="10" text-anchor="middle">67%</text>
        <text x="205" y="34" fill="#64748b" font-size="8" text-anchor="middle">Gauge</text>
      </g>
      {/* Bar H */}
      <g>
        <rect x="280" y="20" width="130" height="56" rx="4" fill="#1a1f2e" stroke="#2d3748" />
        <text x="345" y="36" fill="#64748b" font-size="8" text-anchor="middle">Bar (horiz.)</text>
        <rect x="290" y="44" width="110" height="18" rx="3" fill="#2d3748" />
        <rect x="290" y="44" width="71" height="18" rx="3" fill="#4a9eff" />
        <text x="345" y="66" fill="#94a3b8" font-size="8" text-anchor="middle">65 %</text>
      </g>
      {/* LED */}
      <g>
        <rect x="430" y="20" width="130" height="56" rx="4" fill="#1a1f2e" stroke="#2d3748" />
        <circle cx="460" cy="48" r="12" fill="#22c55e" />
        <text x="477" y="46" fill="#94a3b8" font-size="9">Beam ON</text>
        <text x="477" y="59" fill="#64748b" font-size="8">LED</text>
      </g>
      {/* Plot */}
      <g>
        <rect x="20" y="130" width="260" height="80" rx="4" fill="#1a1f2e" stroke="#2d3748" />
        <text x="150" y="148" fill="#64748b" font-size="8" text-anchor="middle">Time-series Plot</text>
        <line x1="30" y1="152" x2="30" y2="200" stroke="#2d3748" />
        <line x1="30" y1="200" x2="270" y2="200" stroke="#2d3748" />
        <polyline points="32,195 55,182 78,175 101,180 124,165 147,160 170,163 193,150 216,155 239,142 262,138"
          fill="none" stroke="#4a9eff" stroke-width="1.5" />
        <polyline points="32,190 55,188 78,192 101,185 124,179 147,183 170,175 193,170 216,175 239,168 262,162"
          fill="none" stroke="#22c55e" stroke-width="1.5" />
      </g>
      {/* Set Value */}
      <g>
        <rect x="300" y="130" width="130" height="80" rx="4" fill="#1a1f2e" stroke="#2d3748" />
        <text x="365" y="150" fill="#64748b" font-size="8" text-anchor="middle">Set-point Control</text>
        <rect x="310" y="157" width="110" height="22" rx="4" fill="#1a2236" stroke="#2d3748" />
        <text x="365" y="173" fill="#e2e8f0" font-size="11" text-anchor="middle">100.0</text>
        <rect x="320" y="185" width="90" height="18" rx="4" fill="#2563eb" />
        <text x="365" y="198" fill="#fff" font-size="9" text-anchor="middle">Set</text>
      </g>
      {/* Button */}
      <g>
        <rect x="450" y="130" width="110" height="80" rx="4" fill="#1a1f2e" stroke="#2d3748" />
        <text x="505" y="150" fill="#64748b" font-size="8" text-anchor="middle">Button</text>
        <rect x="460" y="158" width="90" height="30" rx="6" fill="#2563eb" />
        <text x="505" y="178" fill="#fff" font-size="11" text-anchor="middle">RESET</text>
      </g>
    </svg>
  );
}

function DiagramHistory() {
  return (
    <svg viewBox="0 0 580 130" class="help-diagram" role="img" aria-label="Historical data toolbar">
      <rect width="580" height="130" rx="8" fill="#0f1117" />

      {/* toolbar */}
      <rect width="580" height="44" fill="#1a1f2e" />
      <rect y="43" width="580" height="1" fill="#2d3748" />

      {/* Live button (inactive) */}
      <rect x="14" y="12" width="50" height="20" rx="10" fill="#2d3748" />
      <circle cx="26" cy="22" r="4" fill="#64748b" />
      <text x="42" y="27" fill="#94a3b8" font-size="9" text-anchor="middle">Live</text>

      {/* start input */}
      <rect x="72" y="12" width="148" height="20" rx="4" fill="#1a2236" stroke="#2d3748" />
      <text x="80" y="26" fill="#e2e8f0" font-size="9">2026-01-15  09:00</text>

      <text x="228" y="26" fill="#475569" font-size="11" text-anchor="middle">→</text>

      {/* end input */}
      <rect x="242" y="12" width="148" height="20" rx="4" fill="#1a2236" stroke="#63b3ed" />
      <text x="250" y="26" fill="#e2e8f0" font-size="9">2026-01-15  10:00</text>

      {/* Load button */}
      <rect x="398" y="12" width="44" height="20" rx="4" fill="#2d3748" />
      <text x="420" y="26" fill="#e2e8f0" font-size="9" text-anchor="middle">Load</text>

      {/* Plot showing historical data */}
      <rect x="10" y="54" width="560" height="66" rx="4" fill="#1a1f2e" stroke="#2d3748" />
      <text x="290" y="70" fill="#64748b" font-size="8" text-anchor="middle">Loaded 3 600 historical points · 09:00 → 10:00</text>
      <line x1="22" y1="75" x2="22" y2="112" stroke="#2d3748" />
      <line x1="22" y1="112" x2="560" y2="112" stroke="#2d3748" />
      <polyline
        points="24,108 70,100 116,92 162,96 208,85 254,80 300,84 346,75 392,78 438,70 484,73 530,65 558,62"
        fill="none" stroke="#4a9eff" stroke-width="1.5" />

      {/* annotations */}
      <line x1="39" y1="54" x2="39" y2="44" stroke="#fbbf24" stroke-width="1.5" />
      <text x="39" y="122" fill="#fbbf24" font-size="8" text-anchor="middle">① Live</text>
      <line x1="146" y1="54" x2="146" y2="44" stroke="#fbbf24" stroke-width="1.5" />
      <text x="146" y="122" fill="#fbbf24" font-size="8" text-anchor="middle">② Start time</text>
      <line x1="316" y1="54" x2="316" y2="44" stroke="#fbbf24" stroke-width="1.5" />
      <text x="316" y="122" fill="#fbbf24" font-size="8" text-anchor="middle">③ End time</text>
      <line x1="420" y1="54" x2="420" y2="44" stroke="#fbbf24" stroke-width="1.5" />
      <text x="420" y="122" fill="#fbbf24" font-size="8" text-anchor="middle">④ Load</text>
    </svg>
  );
}

function DiagramSignalTree() {
  return (
    <svg viewBox="0 0 280 280" class="help-diagram help-diagram-narrow" role="img" aria-label="Signal tree">
      <rect width="280" height="280" rx="8" fill="#151b2e" />

      {/* header */}
      <text x="14" y="22" fill="#e2e8f0" font-size="11" font-weight="bold">Signals</text>
      <rect x="240" y="8" width="28" height="20" rx="4" fill="#2d3748" />
      <text x="254" y="23" fill="#e2e8f0" font-size="10" text-anchor="middle">◀</text>
      <line x1="0" y1="32" x2="280" y2="32" stroke="#2d3748" />

      {/* search */}
      <rect x="8" y="38" width="194" height="20" rx="4" fill="#1a2236" stroke="#2d3748" />
      <text x="16" y="53" fill="#64748b" font-size="9">Filter signals…</text>
      <rect x="208" y="38" width="64" height="20" rx="4" fill="#2d3748" />
      <text x="240" y="53" fill="#e2e8f0" font-size="8" text-anchor="middle">🔍 CF</text>
      <line x1="0" y1="63" x2="280" y2="63" stroke="#2d3748" />

      {/* toolbar */}
      <rect x="8" y="68" width="80" height="18" rx="4" fill="#2d3748" />
      <text x="48" y="81" fill="#e2e8f0" font-size="8" text-anchor="middle">+ Synthetic</text>
      <rect x="94" y="68" width="38" height="18" rx="4" fill="#2d3748" />
      <text x="113" y="81" fill="#e2e8f0" font-size="8" text-anchor="middle">CSV</text>
      <rect x="138" y="68" width="28" height="18" rx="4" fill="#2d3748" />
      <text x="152" y="81" fill="#e2e8f0" font-size="9" text-anchor="middle">↺</text>
      <line x1="0" y1="90" x2="280" y2="90" stroke="#2d3748" />

      {/* epics group */}
      <text x="10" y="110" fill="#94a3b8" font-size="10">▾ epics</text>
      <rect x="250" y="100" width="22" height="16" rx="3" fill="none" />
      <text x="261" y="112" fill="#64748b" font-size="9" text-anchor="middle">+</text>
      <rect x="8" y="118" width="264" height="20" rx="3" fill="none" />
      <text x="22" y="133" fill="#cbd5e1" font-size="10" style="cursor:grab">TEMPERATURE</text>
      <text x="22" y="152" fill="#cbd5e1" font-size="10">PRESSURE</text>
      <text x="22" y="170" fill="#cbd5e1" font-size="10">BEAM_CURRENT</text>

      {/* stub group */}
      <line x1="0" y1="180" x2="280" y2="180" stroke="#2d3748" stroke-dasharray="2 2" />
      <text x="10" y="198" fill="#94a3b8" font-size="10">▾ stub</text>
      <text x="22" y="217" fill="#cbd5e1" font-size="10">sine_1hz</text>
      <text x="22" y="235" fill="#cbd5e1" font-size="10">ramp_10s</text>
      <text x="22" y="253" fill="#94a3b8" font-size="9" font-style="italic">my_custom_pv</text>
      <rect x="250" y="243" width="22" height="14" rx="3" fill="none" />
      <text x="261" y="254" fill="#ef4444" font-size="9" text-anchor="middle">✕</text>

      {/* label custom */}
      <rect x="100" y="246" width="52" height="12" rx="3" fill="#2d3748" />
      <text x="126" y="256" fill="#fbbf24" font-size="7" text-anchor="middle">custom</text>
    </svg>
  );
}

// ── Section content ───────────────────────────────────────────────────────────

const SECTIONS = [
  { id: 'start',     label: 'Getting Started' },
  { id: 'tips',      label: 'Quick Tips' },
  { id: 'view',      label: 'View Mode' },
  { id: 'edit',      label: 'Edit Mode' },
  { id: 'widgets',   label: 'Widgets' },
  { id: 'plots',     label: 'Plot Panels' },
  { id: 'signals',   label: 'Signals' },
  { id: 'logic',     label: 'Panel Logic' },
  { id: 'control',   label: 'Control Logic' },
  { id: 'sharing',   label: 'Sharing & Access' },
  { id: 'audit',     label: 'Audit Log' },
  { id: 'history',   label: 'Historical Data' },
  { id: 'shortcuts', label: 'Keyboard Shortcuts' },
];

// Quick tips, grouped by the mode they apply to. Each links to the manual
// section that covers it in depth. (Previously surfaced via a floating
// contextual-help popover; now consolidated here in the manual.)
const TIPS: Record<'view' | 'edit', Array<{ text: string; section: string }>> = {
  view: [
    { text: 'Click any interface in the left panel to load it on the canvas.', section: 'view' },
    { text: 'Right-click a widget to view signal info or export data as CSV.', section: 'view' },
    { text: 'The ● Live button shows live streaming is active. Use the date pickers to load historical data.', section: 'history' },
    { text: 'Click Edit (top-right) to open the drag-and-drop panel builder.', section: 'edit' },
    { text: 'Use the Share button on a panel to grant access to users or groups.', section: 'sharing' },
    { text: 'The ⚙ Control logic button opens server-side automation that runs even with no panel open.', section: 'control' },
    { text: 'The 🛡 Audit button (for permitted users) opens the log of system-affecting actions.', section: 'audit' },
    { text: 'The status chip turns green and shows your username when the WebSocket is connected.', section: 'view' },
  ],
  edit: [
    { text: 'Drag a signal from the left tree onto the canvas to create a widget.', section: 'edit' },
    { text: 'Click a widget to select it, then adjust its properties on the right.', section: 'edit' },
    { text: 'Ctrl+click to select multiple widgets; then use the align toolbar.', section: 'edit' },
    { text: 'Ctrl+Z to undo, Ctrl+Y to redo. Up to 50 steps are remembered.', section: 'shortcuts' },
    { text: 'Use "+ Synthetic" in the signal tree to create computed/filtered signals.', section: 'signals' },
    { text: 'Switch to the Logic tab to add interactive behaviour with a node-graph flow editor.', section: 'logic' },
    { text: 'Add Local variables for set-points and counters that live inside the panel.', section: 'edit' },
    { text: 'Arrow keys nudge the selected widget by 1 px (Shift for grid size).', section: 'shortcuts' },
  ],
};

function SectionTips({ onNavigate }: { onNavigate: (section: string) => void }) {
  const groups: Array<['view' | 'edit', string]> = [
    ['view', 'In View mode'],
    ['edit', 'In Edit mode'],
  ];
  return (
    <div>
      <p class="help-lead">
        A handful of quick pointers to get the most out of uopi. Click any tip to jump to the
        section that explains it in detail.
      </p>
      {groups.map(([mode, title]) => (
        <div key={mode}>
          <h3 class="help-h3">{title}</h3>
          <ul class="help-tips-list">
            {TIPS[mode].map(tip => (
              <li key={tip.text}>
                <button class="help-tip-item" onClick={() => onNavigate(tip.section)}>
                  <span class="help-tip-icon">💡</span>
                  <span class="help-tip-text">{tip.text}</span>
                  <span class="help-tip-go">↗</span>
                </button>
              </li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

function SectionStart() {
  return (
    <div>
      <p class="help-lead">
        <strong>uopi</strong> is a web-based HMI for monitoring and controlling EPICS-based scientific and industrial systems.
        It runs as a single binary — open a browser, no installation required.
      </p>

      <h3 class="help-h3">How it works</h3>
      <ol class="help-list">
        <li>The server connects to your control system (EPICS, synthetic signals).</li>
        <li>Open <code>http://&lt;host&gt;:8080</code> in any browser.</li>
        <li>Pick an HMI panel from the left sidebar to view live data.</li>
        <li>Switch to <strong>Edit mode</strong> to build or modify panels.</li>
      </ol>

      <h3 class="help-h3">Two modes</h3>
      <div class="help-cards">
        <div class="help-card">
          <div class="help-card-title">👁 View Mode</div>
          <p>Monitor live signals in read-only HMI panels. Right-click any widget for signal info or to export data as CSV.</p>
        </div>
        <div class="help-card">
          <div class="help-card-title">✏ Edit Mode</div>
          <p>Drag signals from the tree onto the canvas, resize and configure widgets, then save your panel to the server.</p>
        </div>
      </div>

      <h3 class="help-h3">Quick navigation</h3>
      <ul class="help-list">
        <li>Click an interface in the left list to open it in view mode.</li>
        <li>Click <strong>Edit</strong> (top-right) to switch to the editor.</li>
        <li>Click <strong>✕ Close</strong> in Edit mode to return to View mode.</li>
        <li>Press <kbd>?</kbd> anywhere to open this help.</li>
      </ul>
    </div>
  );
}

function SectionView() {
  return (
    <div>
      <p class="help-lead">
        View mode displays a live HMI panel. Widget values update in real time via WebSocket.
      </p>

      <DiagramViewMode />
      <p class="help-caption">View mode layout: ① interface list · ② live canvas · WebSocket status top-right.</p>

      <h3 class="help-h3">Interface list</h3>
      <ul class="help-list">
        <li>Click any panel name to load it on the canvas.</li>
        <li>Use the action buttons (edit ✎, clone ⧉, delete ✕) that appear on hover.</li>
        <li>Collapse the list by clicking the <strong>◀</strong> button at the top.</li>
      </ul>

      <h3 class="help-h3">Context menu</h3>
      <p>Right-click any widget to:</p>
      <ul class="help-list">
        <li>View the signal name and current value.</li>
        <li>Copy the signal name to the clipboard.</li>
        <li>Export the last 1 000 samples as a CSV file.</li>
      </ul>

      <h3 class="help-h3">Connection status</h3>
      <p>
        The coloured chip in the top-right shows the WebSocket connection state. When connected it
        reads <em>"connected as &lt;your username&gt;"</em>, so you can confirm the identity your
        actions are attributed to in the audit log. A red <em>"disconnected"</em> banner appears at
        the top while reconnecting — live values will resume automatically.
      </p>
    </div>
  );
}

function SectionEdit() {
  return (
    <div>
      <p class="help-lead">
        Edit mode gives you a drag-and-drop canvas, a signal tree on the left, and a properties pane on the right.
      </p>

      <DiagramEditMode />
      <p class="help-caption">Edit mode layout: ① signal tree · ② canvas with snap grid · ③ properties pane.</p>

      <h3 class="help-h3">Adding widgets</h3>
      <DiagramDragDrop />
      <p class="help-caption">Drag a signal from the tree → drop on canvas → choose a widget type.</p>

      <h3 class="help-h3">Selecting and moving</h3>
      <ul class="help-list">
        <li>Click a widget to select it — resize handles appear at the corners and edges.</li>
        <li>Drag the widget body to move it; drag a handle to resize.</li>
        <li><kbd>Ctrl</kbd>+click to add/remove widgets from the selection.</li>
        <li>Drag on empty canvas to rubber-band select multiple widgets.</li>
        <li>Arrow keys nudge the selection by 1 px (or grid size with <kbd>Shift</kbd>).</li>
      </ul>

      <h3 class="help-h3">Align &amp; distribute</h3>
      <p>When 2+ widgets are selected the alignment toolbar appears. Buttons align edges or centres; with 3+ selected you can distribute spacing evenly.</p>

      <h3 class="help-h3">Layout &amp; Logic tabs</h3>
      <p>The centre of the editor has two tabs:</p>
      <ul class="help-list">
        <li><strong>Layout</strong> — the drag-and-drop widget canvas described above.</li>
        <li><strong>Logic</strong> — a visual flow editor that adds interactive behaviour to the
          panel (buttons that run actions, thresholds that pop up dialogs, timers, …). See the
          <em>Panel Logic</em> section. The tab is hidden if you are not permitted to edit logic.</li>
      </ul>

      <h3 class="help-h3">Local variables</h3>
      <p>
        Panels can define their own <strong>local variables</strong> — lightweight values that live
        only inside the panel (no data source needed). Add them from the signal tree's
        <em>Local</em> group (or the Logic palette). They are written by Set-value widgets, buttons
        and logic actions, and referenced anywhere a signal is, making them handy for set-points,
        toggles and counters used by panel logic.
      </p>

      <h3 class="help-h3">Saving</h3>
      <ul class="help-list">
        <li><strong>Save</strong> — stores the panel on the server (XML). A <span style="color:#f59e0b">●</span> indicates unsaved changes.</li>
        <li><strong>Export</strong> — downloads the XML file locally.</li>
        <li><strong>Import</strong> — loads an XML file from disk (replaces the current canvas).</li>
      </ul>
    </div>
  );
}

function SectionPlots() {
  return (
    <div>
      <p class="help-lead">
        A <strong>plot panel</strong> is a special interface dedicated to charts. Instead of
        free-form boxes, plots <em>fill the viewport</em> and you split the space between them like
        panes in a tiling window manager (tmux / IDE style).
      </p>

      <h3 class="help-h3">Creating one</h3>
      <ul class="help-list">
        <li>Click <strong>+ Plot</strong> in the interface list to create a plot panel — it opens
          with one full-viewport empty plot.</li>
      </ul>

      <h3 class="help-h3">Splitting &amp; arranging</h3>
      <ul class="help-list">
        <li>Hover a pane and use its split buttons (<strong>⬌</strong> vertical / <strong>⬍</strong>
          horizontal) to divide it — a new empty plot appears in the freed half.</li>
        <li>Drag the divider between two panes to resize them; nesting is unlimited.</li>
        <li>Click a pane to select it, then configure its plot in the Properties pane (plot type,
          time window, range, legend, per-signal colour).</li>
        <li>Drag a signal from the tree onto a pane to add it to that pane's plot.</li>
        <li>Use a pane's <strong>✕</strong> to remove it; the layout collapses onto its sibling.</li>
      </ul>
      <p>In view mode the saved split layout fills the screen with live, streaming plots.</p>
    </div>
  );
}

function SectionLogic() {
  return (
    <div>
      <p class="help-lead">
        The <strong>Logic</strong> tab in the panel editor is a node-graph (Node-RED style) flow
        editor. You wire <em>trigger</em> nodes to <em>action</em> nodes to give a panel interactive
        behaviour. Logic runs entirely client-side while the panel is open in view mode.
      </p>

      <h3 class="help-h3">Building a flow</h3>
      <ul class="help-list">
        <li>Drag blocks from the left palette onto the canvas (or click to add), then drag from a
          node's output port to another node's input port to connect them.</li>
        <li>Select a node to edit its parameters in the right inspector.</li>
        <li>Expression fields accept arithmetic/logic over signals — reference a signal as
          <code>{'{ds:name}'}</code> and a panel-local variable by its bare name.</li>
        <li>The editor has its own undo/redo and copy/paste (see Keyboard Shortcuts).</li>
      </ul>

      <h3 class="help-h3">Node types</h3>
      <div class="help-widget-table">
        {[
          { type: 'Triggers',  desc: 'Button press, threshold crossing, value change, timer/interval, panel loop, and On-open / On-close lifecycle.' },
          { type: 'Logic',     desc: 'AND gate, If (then/else branches), and Loop (count or while).' },
          { type: 'Actions',   desc: 'Write a value to a signal/variable, Delay, Log (debug), and Accumulate / Export-CSV / Clear for in-memory data arrays.' },
          { type: 'Dialogs',   desc: 'Info and Error pop-ups, and a Set-point prompt that asks the user for a number and writes it to a target.' },
        ].map(w => (
          <div key={w.type} class="help-widget-row">
            <span class="help-widget-name">{w.type}</span>
            <span class="help-widget-desc">{w.desc}</span>
          </div>
        ))}
      </div>

      <h3 class="help-h3">System helpers</h3>
      <ul class="help-list">
        <li><code>{'{sys:time}'}</code> — current time in epoch seconds.</li>
        <li><code>{'{sys:dt}'}</code> — seconds since the firing trigger last fired (useful for rates).</li>
      </ul>
    </div>
  );
}

function SectionControl() {
  return (
    <div>
      <p class="help-lead">
        <strong>Control logic</strong> is server-side automation. Unlike panel logic (which only
        runs while a panel is open in a browser), control-logic graphs run continuously on the
        server, independent of any client. Open them with the <strong>⚙ Control logic</strong>
        button in the view-mode toolbar.
      </p>

      <h3 class="help-h3">What it can do</h3>
      <ul class="help-list">
        <li>React to <em>cron</em> schedules and signal <em>alarm</em>/threshold conditions.</li>
        <li>Run a <strong>Lua</strong> block for custom logic and compute writes back to signals.</li>
        <li>Each graph can be enabled/disabled independently; changes reload the engine live.</li>
      </ul>

      <p class="help-callout">
        Editing both panel logic and control logic can be restricted to an allowlist of users or
        groups (<code>server.logic_editors</code> in the config). When restricted, users not on the
        list keep full access to everything else but cannot add or change logic — the Logic tab and
        the ⚙ Control logic button are hidden for them.
      </p>
    </div>
  );
}

function SectionSharing() {
  return (
    <div>
      <p class="help-lead">
        uopi has graduated, server-enforced access control. Identity comes from a trusted
        reverse-proxy header (with a configurable default for unproxied/LAN use) — there is no login
        page in the app itself.
      </p>

      <h3 class="help-h3">Global access levels</h3>
      <ul class="help-list">
        <li>Every user is trusted with full <strong>write</strong> access by default.</li>
        <li>A config blacklist can downgrade specific users to <strong>read-only</strong> or
          <strong>no access</strong>. Read-only users see panels but cannot edit or write signals.</li>
        <li>Named <strong>groups</strong> are defined in the config and used by panel sharing.</li>
      </ul>

      <h3 class="help-h3">Panel ownership &amp; sharing</h3>
      <ul class="help-list">
        <li>New panels are <strong>private</strong> to their owner by default.</li>
        <li>Use the <strong>Share</strong> button on a panel (in the interface list) to grant
          read or write to specific users/groups, or make it public.</li>
        <li>Your global level always caps the per-panel permission (read-only stays read-only).</li>
      </ul>

      <h3 class="help-h3">Folders</h3>
      <ul class="help-list">
        <li>Organise panels into nested <strong>folders</strong> in the interface list.</li>
        <li>Permissions inherit down the folder chain.</li>
        <li>Drag panels to reorder them or move them between folders.</li>
      </ul>
    </div>
  );
}

function SectionAudit() {
  return (
    <div>
      <p class="help-lead">
        When the server is configured with <code>[audit] enabled = true</code>, every
        <strong> system-affecting action</strong> is appended to an append-only log so it can be
        reviewed later. Permitted users open it with the <strong>🛡 Audit</strong> button in the
        view-mode toolbar.
      </p>

      <h3 class="help-h3">What gets recorded</h3>
      <ul class="help-list">
        <li><strong>Signal writes</strong> — both user-initiated (set-value widgets, buttons, panel
          logic) and automated writes from the control-logic engine.</li>
        <li><strong>Interface changes</strong> — panels created, updated, or deleted.</li>
        <li><strong>Control-logic changes</strong> — graphs created, updated, or deleted.</li>
      </ul>
      <p>Each entry records the time, the actor (username, or the graph name for automated
        actions), the action, the affected data source / signal and value, the client address, and
        whether it succeeded.</p>

      <h3 class="help-h3">Viewing the log</h3>
      <ul class="help-list">
        <li>The viewer lists the most recent events first.</li>
        <li>Filter by user, action, signal, or a start/end time range.</li>
        <li>System (automated) actions are tagged distinctly from user actions.</li>
      </ul>

      <p class="help-callout">
        Access to the audit log is gated by an allowlist (<code>audit.readers</code> in the config),
        following the same model as logic editing: an empty list means every user may view it,
        otherwise only the listed users or groups can. The 🛡 Audit button is hidden for users who
        are not permitted.
      </p>
    </div>
  );
}

function SectionWidgets() {
  return (
    <div>
      <p class="help-lead">
        Each widget is configured in the Properties pane on the right.
        The available options depend on the widget type.
      </p>

      <DiagramWidgets />
      <p class="help-caption">Widget type overview — drag a signal from the tree to place one.</p>

      <div class="help-widget-table">
        {[
          { type: 'Text View',    desc: 'Shows the current numeric or string value. Supports alarm colouring.' },
          { type: 'Gauge',        desc: 'Circular arc gauge with configurable range and colour zones.' },
          { type: 'Bar (H / V)',  desc: 'Horizontal or vertical filled bar, great for percentages or levels.' },
          { type: 'LED',          desc: 'Coloured indicator. Turns green/red based on a threshold or enum value.' },
          { type: 'Multi-LED',    desc: 'Bitset indicator — shows individual bits of an integer signal as LEDs.' },
          { type: 'Set Value',    desc: 'Text input that writes a value back to a writable signal on Enter or click.' },
          { type: 'Button',       desc: 'Sends a fixed value to a signal when clicked.' },
          { type: 'Plot',         desc: 'Multi-signal chart. Supports time-series (uPlot), FFT, waterfall, histogram, bar chart, logic analyser.' },
          { type: 'Text Label',   desc: 'Static annotation text — no signal required.' },
          { type: 'Image',        desc: 'Static image loaded from a URL.' },
          { type: 'Link',         desc: 'Navigates to another interface when clicked.' },
        ].map(w => (
          <div key={w.type} class="help-widget-row">
            <span class="help-widget-name">{w.type}</span>
            <span class="help-widget-desc">{w.desc}</span>
          </div>
        ))}
      </div>

      <h3 class="help-h3">Plot widget details</h3>
      <ul class="help-list">
        <li>Add multiple signals via the <em>Signals</em> section in the Properties pane.</li>
        <li>Choose plot type: <em>timeseries</em>, <em>FFT</em>, <em>waterfall</em>, <em>histogram</em>, <em>bar</em>, or <em>logic</em>.</li>
        <li>Set <em>Time window</em> to control how many seconds of live data are shown.</li>
        <li>In Historical mode the entire requested range is loaded at once.</li>
      </ul>
    </div>
  );
}

function SectionSignals() {
  return (
    <div>
      <p class="help-lead">
        The signal tree (left pane in Edit mode) lets you browse, search, and manage the signals available from each data source.
      </p>

      <div class="help-two-col">
        <div>
          <h3 class="help-h3">Finding signals</h3>
          <ul class="help-list">
            <li>Type in the <em>Filter signals…</em> box to search by name or description.</li>
            <li>Click a group header to collapse or expand that data source.</li>
            <li>Drag any signal onto the canvas to create a widget.</li>
          </ul>

          <h3 class="help-h3">Adding custom signals</h3>
          <ul class="help-list">
            <li>Click <strong>+</strong> next to a group name to type a PV name manually.</li>
            <li>Custom signals are saved in your browser (localStorage) and persist across reloads.</li>
            <li>Click <strong>✕</strong> on a custom signal to remove it.</li>
          </ul>

          <h3 class="help-h3">CSV import</h3>
          <p>Click <strong>CSV</strong> to import a list of signals from a file. Each line should be either:</p>
          <ul class="help-list">
            <li><code>PV_NAME</code> — added to the <em>epics</em> data source.</li>
            <li><code>ds,PV_NAME</code> — added to the specified data source.</li>
          </ul>
          <p>Lines starting with <code>#</code> are treated as comments.</p>

          <h3 class="help-h3">Channel Finder</h3>
          <p>
            If your server is configured with an EPICS Channel Finder URL,
            a <strong>🔍</strong> button appears when you type in the filter box.
            Click it to search Channel Finder and import matching PV names.
          </p>

          <h3 class="help-h3">Synthetic signals</h3>
          <p>Click <strong>+ Synthetic</strong> to compose a new signal from existing ones. A quick
            <em>wizard</em> covers the common case (pick an input + a processing node such as gain,
            offset, moving average, lowpass/highpass, derivative, integral, clamp or a custom
            formula). For multi-input pipelines, the <strong>node-graph editor</strong> lets you wire
            inputs through a chain of DSP blocks visually. The new signal appears in the tree
            immediately.</p>
          <p>Each synthetic signal has a <strong>visibility scope</strong>: <em>panel</em> (only the
            panel that created it), <em>user</em>, or <em>global</em> (shared with everyone).</p>

          <h3 class="help-h3">Local variables</h3>
          <p>The <strong>Local</strong> group holds panel-local variables — values stored in the
            panel itself with no data source. Use them for set-points, toggles and counters driven by
            panel logic. They are added in the editor and saved with the panel.</p>
        </div>
        <DiagramSignalTree />
      </div>
    </div>
  );
}

function SectionHistory() {
  return (
    <div>
      <p class="help-lead">
        Historical data is retrieved from EPICS Archive Appliance when the server is configured with an <code>archive_url</code>.
        Only the <strong>Plot</strong> widget in <em>time-series</em> mode supports historical ranges.
      </p>

      <DiagramHistory />
      <p class="help-caption">Time navigation toolbar: ① Live button · ② start · ③ end datetime · ④ Load.</p>

      <h3 class="help-h3">How to load historical data</h3>
      <ol class="help-list">
        <li>Set the <strong>start</strong> and <strong>end</strong> date/time fields in the toolbar.</li>
        <li>Click <strong>Load</strong> — all plot widgets on the current panel will fetch the requested range.</li>
        <li>While loading, a <em>"Loading history…"</em> overlay appears on each plot.</li>
        <li>If no data is available, the overlay shows <em>"No archive data for this range"</em>.</li>
        <li>Click <strong>● Live</strong> (highlighted when active) to return to streaming mode.</li>
      </ol>

      <h3 class="help-h3">Notes</h3>
      <ul class="help-list">
        <li>Historical data is requested over the WebSocket connection (not REST).</li>
        <li>Requests time out after 15 seconds if the archive does not respond.</li>
        <li>Non-timeseries plot types (FFT, waterfall, etc.) remain in live mode regardless of the time range.</li>
        <li>Point-value widgets (gauge, bar, LED, …) are unaffected by the time range selection.</li>
      </ul>
    </div>
  );
}

function SectionShortcuts() {
  const shortcuts: Array<[string, string]> = [
    ['Ctrl+C',              'Copy selected widgets (Edit mode)'],
    ['Ctrl+V',              'Paste widgets from clipboard (Edit mode)'],
    ['Ctrl+Z',              'Undo last change (Edit mode)'],
    ['Ctrl+Y / Ctrl+Shift+Z', 'Redo (Edit mode)'],
    ['Ctrl+A',              'Select all widgets (Edit mode)'],
    ['Delete / Backspace',  'Delete selected widgets (Edit mode)'],
    ['Arrow keys',          'Nudge selection by 1 px (Edit mode)'],
    ['Shift+Arrow',         'Nudge by grid size (Edit mode)'],
    ['Ctrl+click',          'Add/remove widget from multi-selection'],
    ['Escape',              'Close dialogs / cancel input'],
    ['?',                   'Open this help'],
  ];

  return (
    <div>
      <p class="help-lead">All shortcuts that work while the canvas or modal has focus.</p>

      <table class="help-shortcut-table">
        <thead>
          <tr><th>Shortcut</th><th>Action</th></tr>
        </thead>
        <tbody>
          {shortcuts.map(([key, action]) => (
            <tr key={key}>
              <td><kbd class="help-kbd">{key}</kbd></td>
              <td>{action}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <h3 class="help-h3">API &amp; Metrics</h3>
      <ul class="help-list">
        <li>Signals/data: <code>/api/v1/datasources</code>, <code>/api/v1/signals</code>, <code>/api/v1/synthetic</code></li>
        <li>Panels: <code>/api/v1/interfaces</code>, <code>/api/v1/folders</code>, <code>/api/v1/interfaces/{'{id}'}/acl</code></li>
        <li>Identity &amp; groups: <code>/api/v1/me</code>, <code>/api/v1/usergroups</code></li>
        <li>Control logic: <code>/api/v1/controllogic</code></li>
        <li>Prometheus metrics: <code>/metrics</code> · Health check: <code>/healthz</code> · WebSocket: <code>ws://&lt;host&gt;/ws</code></li>
      </ul>

      <h3 class="help-h3">Configuration file</h3>
      <pre class="help-pre">[server]{'\n'}listen      = ":8080"{'\n'}storage_dir = "./interfaces"{'\n'}# optional: restrict who may edit panel/control logic{'\n'}# logic_editors = ["operators"]{'\n\n'}[datasource.epics]{'\n'}enabled     = true{'\n'}archive_url = "http://archiver:17665"{'\n\n'}[datasource.synthetic]{'\n'}enabled = true</pre>
    </div>
  );
}

const SECTION_COMPONENTS: Record<string, (props: { onNavigate: (section: string) => void }) => JSX.Element> = {
  start:     SectionStart,
  tips:      SectionTips,
  view:      SectionView,
  edit:      SectionEdit,
  widgets:   SectionWidgets,
  plots:     SectionPlots,
  signals:   SectionSignals,
  logic:     SectionLogic,
  control:   SectionControl,
  sharing:   SectionSharing,
  audit:     SectionAudit,
  history:   SectionHistory,
  shortcuts: SectionShortcuts,
};

// ── Main modal ────────────────────────────────────────────────────────────────

export default function HelpModal({ initialSection = 'start', onClose }: Props) {
  const [active, setActive] = useState(initialSection);

  const Content: (props: { onNavigate: (section: string) => void }) => JSX.Element =
    SECTION_COMPONENTS[active] ?? SectionStart;

  // Close on backdrop click
  function handleBackdrop(e: MouseEvent) {
    if ((e.target as Element).classList.contains('help-backdrop')) onClose();
  }

  // Close on Escape
  function handleKey(e: KeyboardEvent) {
    if (e.key === 'Escape') onClose();
  }

  return (
    <div class="help-backdrop" onClick={handleBackdrop} onKeyDown={handleKey} tabIndex={-1}>
      <div class="help-modal" role="dialog" aria-modal="true" aria-label="uopi Help Manual">
        {/* Header */}
        <div class="help-header">
          <span class="help-title">uopi — User Manual</span>
          <button class="icon-btn help-close" onClick={onClose} title="Close help">✕</button>
        </div>

        <div class="help-body">
          {/* Sidebar nav */}
          <nav class="help-sidebar">
            {SECTIONS.map(s => (
              <button
                key={s.id}
                class={`help-nav-item${active === s.id ? ' active' : ''}`}
                onClick={() => setActive(s.id)}
              >
                {s.label}
              </button>
            ))}
          </nav>

          {/* Content area */}
          <div class="help-content">
            <h2 class="help-h2">{SECTIONS.find(s => s.id === active)?.label}</h2>
            <Content onNavigate={setActive} />
          </div>
        </div>
      </div>
    </div>
  );
}
