// editor.jsx — The map editor: sidebar nav + panels + canvas with preview.

const { useState: useStateE, useEffect: useEffectE, useRef: useRefE } = React;

// Poster preview width: full on desktop, scaled to fit the top strip on mobile
// (real width change → MapLibre stays interactive at the correct scale).
function computePosterW(aspectHW) {
  const vw = window.innerWidth, vh = window.innerHeight;
  if (vw > 720) {
    // Desktop: as big as fits the stage, leaving clearance for the bottom
    // "sin marca de agua" note so it never overlaps the poster.
    const availH = vh - 252; // topbar + dock + stage padding + note clearance
    return Math.max(320, Math.min(560, Math.floor(availH / aspectHW)));
  }
  // Mobile: fill the stage above the compact dock (chips + buy bar) as much as possible.
  const availH = vh - 176; // topbar + dock + padding
  return Math.max(200, Math.min(vw - 28, Math.floor(availH / aspectHW)));
}
function useResponsivePosterWidth(aspectHW) {
  const [w, setW] = useStateE(() => computePosterW(aspectHW));
  useEffectE(() => {
    const onResize = () => setW(computePosterW(aspectHW));
    setW(computePosterW(aspectHW)); // recompute when the size/aspect changes
    window.addEventListener('resize', onResize);
    window.addEventListener('orientationchange', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
      window.removeEventListener('orientationchange', onResize);
    };
  }, [aspectHW]);
  return w;
}

function Editor({ design, setDesign, onBack, onCheckout, cartCount = 0, onOpenCart }) {
  const { t, price } = React.useContext(window.LangContext);
  // activePanel === null → no control sheet open, the poster is shown full-size.
  const [activePanel, setActivePanel] = useStateE(null);
  const aspectHW = window.sizeAspectHW(design.size || '30x40');
  const posterW = useResponsivePosterWidth(aspectHW);

  // Order of the control chips (same lower dock on desktop and mobile).
  const chips = [
    { id: 'location', label: t('editor.railLocation'), icon: <IconLocation /> },
    { id: 'style',    label: t('editor.railStyle'),    icon: <IconStyle /> },
    { id: 'text',     label: t('editor.railText'),     icon: <IconText /> },
    { id: 'pin',      label: t('editor.railPin'),      icon: <IconPin /> },
    { id: 'size',     label: t('editor.railSize'),     icon: <IconSize /> },
  ];
  const toggle = (id) => setActivePanel(p => (p === id ? null : id));

  return (
    <div className="editor">
      <div className="editor-topbar">
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <button className="btn-ghost" style={{ padding: '8px 14px', fontSize: 12 }} onClick={onBack}>
            {t('editor.back')}
          </button>
          <div className="nav-brand">WIS<i>maps</i></div>
        </div>
        <div className="editor-topbar-actions">
          <LangToggle />
          <button className="eb-cart-btn" onClick={onOpenCart} aria-label="Carrito">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.7 13.4a2 2 0 0 0 2 1.6h9.7a2 2 0 0 0 2-1.6L23 6H6"/></svg>
            {cartCount > 0 && <span className="eb-cart-badge">{cartCount}</span>}
          </button>
        </div>
      </div>

      {/* Big poster/map stage — the preview is dominant on every screen. */}
      <div className={`editor-canvas ${activePanel ? 'sheet-open' : ''}`}>
        <div className="canvas-stage">
          <div className="poster-shell">
            <Poster
              design={design}
              setDesign={setDesign}
              width={posterW}
              frameId="none"
              withWatermark={true}
              draggable={true}
              interactive={true}
            />
          </div>
        </div>

        <div className="canvas-watermark-note">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/>
          </svg>
          <span>{t('editor.watermarkNote1')}<b>{t('editor.watermarkNoteBold')}</b>{t('editor.watermarkNote2')}</span>
        </div>

        {/* Control sheet: bottom sheet on mobile, floating card on desktop. */}
        {activePanel && (
          <div className="eb-sheet">
            <div className="eb-sheet-grip" />
            <button className="eb-sheet-close" onClick={() => setActivePanel(null)} aria-label={t('editor.close')}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M18 6 6 18M6 6l12 12"/></svg>
            </button>
            <div className="eb-sheet-body">
              <PanelContent activePanel={activePanel} design={design} setDesign={setDesign} setActivePanel={setActivePanel} />
            </div>
          </div>
        )}
      </div>

      {/* Lower dock: chips (open a control) + always-visible price & buy button. */}
      <div className="eb-dock">
        <div className="eb-chips">
          {chips.map(c => (
            <button key={c.id} className={`eb-chip ${activePanel === c.id ? 'active' : ''}`} onClick={() => toggle(c.id)}>
              {c.icon}<span>{c.label}</span>
            </button>
          ))}
        </div>
        <div className="eb-buybar">
          <div className="eb-price">
            <span className="price">{price.display}</span>
            <span className="eb-file">{t('editor.footFile')}</span>
          </div>
          <button className="btn-buy" onClick={onCheckout}>
            <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
              <circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/>
              <path d="M1 1h4l2.7 13.4a2 2 0 0 0 2 1.6h9.7a2 2 0 0 0 2-1.6L23 6H6"/>
            </svg>
            {t('editor.addCart')}
          </button>
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// PANELS
// ─────────────────────────────────────────────────────────────────────────

function PanelContent({ activePanel, design, setDesign, setActivePanel }) {
  if (activePanel === 'size')     return <PanelSize     design={design} setDesign={setDesign} />;
  if (activePanel === 'location') return <PanelLocation design={design} setDesign={setDesign} />;
  if (activePanel === 'style')    return <PanelStyle    design={design} setDesign={setDesign} />;
  if (activePanel === 'text')     return <PanelText     design={design} setDesign={setDesign} />;
  if (activePanel === 'pin')      return <PanelPin      design={design} setDesign={setDesign} />;
  return null;
}

function PanelSize({ design, setDesign }) {
  const { t, lang } = React.useContext(window.LangContext);
  return (
    <>
      <div className="panel-header">
        <h3>{t('pSize.title')}</h3>
        <p>{t('pSize.sub')}</p>
      </div>
      <div className="panel-body">
        <div className="size-grid">
          {window.PRINT_SIZES.map(s => (
            <button
              key={s.id}
              className={`size-card ${design.size === s.id ? 'active' : ''}`}
              onClick={() => setDesign(d => ({ ...d, size: s.id }))}
            >
              <span className="size-card-name">{s.name}</span>
              <span className="size-card-meta">{t('sizeMeta.' + s.id)}</span>
              {s.popular && <span className="size-card-tag">{lang === 'en' ? 'Best seller' : 'Más vendido'}</span>}
            </button>
          ))}
        </div>
        <div style={{
          padding: 12, background: 'var(--c-panel-2)', borderRadius: 8, fontSize: 12,
          color: 'var(--c-text-soft)', lineHeight: 1.5, marginTop: 4,
        }}>
          {t('pSize.hint')}
        </div>
      </div>
    </>
  );
}

// Format a coordinate for display: up to 6 decimals, no trailing zeros.
const COORD_DECIMALS = 6; // meaningful map precision (~0.11 m); further digits add nothing
function fmtCoord(v) {
  if (v == null || isNaN(v)) return '';
  return Number(v).toFixed(COORD_DECIMALS);
}
// While typing: keep only a valid number and cap the decimals to COORD_DECIMALS.
function sanitizeCoordInput(v) {
  v = String(v).replace(/[^0-9.-]/g, '');
  const neg = v.startsWith('-');
  v = (neg ? '-' : '') + v.replace(/-/g, '');
  const parts = v.split('.');
  if (parts.length > 1) {
    v = parts[0] + '.' + parts.slice(1).join('').slice(0, COORD_DECIMALS);
  }
  return v;
}
// Block typing more decimals than allowed (so the 7th digit can't be entered),
// and commit on Enter.
function coordKeyDown(e, commit) {
  if (e.key === 'Enter') { commit(); e.target.blur(); return; }
  if (/^[0-9]$/.test(e.key) && !e.ctrlKey && !e.metaKey && !e.altKey) {
    const el = e.target, val = el.value, dot = val.indexOf('.');
    if (dot >= 0 && el.selectionStart === el.selectionEnd &&
        el.selectionStart > dot && (val.length - dot - 1) >= COORD_DECIMALS) {
      e.preventDefault();
    }
  }
}

function PanelLocation({ design, setDesign }) {
  const { t } = React.useContext(window.LangContext);
  const [q, setQ] = useStateE(design.location || '');
  const [results, setResults] = useStateE([]);
  const [open, setOpen] = useStateE(false);
  const timerRef = useRefE(null);

  useEffectE(() => {
    setQ(design.location || '');
  }, [design.location]);

  // Local text state for the lat/lng inputs. Typing only edits the text; the map
  // moves when the user presses Enter or leaves the field (so it never fights the
  // user by auto-filling while they type). Dragging the map still updates them.
  const pcOf = d => d.pinCoords || d.coords;
  const [latText, setLatText] = useStateE(() => fmtCoord(pcOf(design)[1]));
  const [lngText, setLngText] = useStateE(() => fmtCoord(pcOf(design)[0]));
  const editingRef = useRefE(false);
  useEffectE(() => {
    if (editingRef.current) return;               // don't clobber while typing
    setLatText(fmtCoord(pcOf(design)[1]));
    setLngText(fmtCoord(pcOf(design)[0]));
  }, [pcOf(design)[0], pcOf(design)[1]]);

  function commitLat() {
    let n = parseFloat(latText);
    if (isNaN(n)) { setLatText(fmtCoord(pcOf(design)[1])); return; }    // invalid → restore
    n = Math.max(-90, Math.min(90, Number(n.toFixed(COORD_DECIMALS)))); // round to N decimals
    setLatText(n.toFixed(COORD_DECIMALS));                              // pad with zeros
    setDesign(d => { const lng = pcOf(d)[0]; const loc = [lng, n]; return { ...d, coords: loc, pinCoords: loc, pinPlaced: true }; });
  }
  function commitLng() {
    let n = parseFloat(lngText);
    if (isNaN(n)) { setLngText(fmtCoord(pcOf(design)[0])); return; }      // invalid → restore
    n = Math.max(-180, Math.min(180, Number(n.toFixed(COORD_DECIMALS)))); // round to N decimals
    setLngText(n.toFixed(COORD_DECIMALS));                                // pad with zeros
    setDesign(d => { const lat = pcOf(d)[1]; const loc = [n, lat]; return { ...d, coords: loc, pinCoords: loc, pinPlaced: true }; });
  }

  function onInput(v) {
    setQ(v);
    if (timerRef.current) clearTimeout(timerRef.current);
    if (!v || v.length < 2) { setResults([]); setOpen(false); return; }
    timerRef.current = setTimeout(async () => {
      const r = await window.geoSearch(v);
      setResults(r);
      setOpen(true);
    }, 280);
  }

  function pick(r) {
    setDesign(d => ({
      ...d,
      location: r.name,
      headline: r.name.toUpperCase(),
      coords: [r.lon, r.lat],
      pinCoords: [r.lon, r.lat],
      pinPlaced: false,
      zoom: 14,
    }));
    setQ(r.name);
    setOpen(false);
  }

  return (
    <>
      <div className="panel-header">
        <h3>{t('pLocation.title')}</h3>
        <p>{t('pLocation.sub')}</p>
      </div>
      <div className="panel-body">
        <div className="field">
          <label className="field-label">{t('pLocation.searchLabel')}</label>
          <div className="search-wrap">
            <input
              type="text"
              className="input"
              placeholder={t('pLocation.searchPlaceholder')}
              value={q}
              onChange={e => onInput(e.target.value)}
              onFocus={() => results.length && setOpen(true)}
              onBlur={() => setTimeout(() => setOpen(false), 150)}
            />
            {open && results.length > 0 && (
              <div className="search-results">
                {results.map((r, i) => (
                  <div key={i} className="search-row" onMouseDown={() => pick(r)}>
                    <div>{r.name}</div>
                    <div className="sub">{r.full}</div>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        <div className="field">
          <label className="field-label">{t('pLocation.zoom')} · {design.zoom.toFixed(1)}</label>
          <input
            type="range" min="9" max="17" step="0.5" value={design.zoom}
            onChange={e => setDesign(d => ({ ...d, zoom: parseFloat(e.target.value) }))}
            style={{ width: '100%' }}
          />
        </div>

        <div className="field">
          <label className="field-label">{t('pLocation.latlng')}</label>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            <input className="input" type="text" inputMode="decimal" value={latText}
              onFocus={() => { editingRef.current = true; }}
              onChange={e => setLatText(sanitizeCoordInput(e.target.value))}
              onKeyDown={e => coordKeyDown(e, commitLat)}
              onBlur={() => { editingRef.current = false; commitLat(); }} />
            <input className="input" type="text" inputMode="decimal" value={lngText}
              onFocus={() => { editingRef.current = true; }}
              onChange={e => setLngText(sanitizeCoordInput(e.target.value))}
              onKeyDown={e => coordKeyDown(e, commitLng)}
              onBlur={() => { editingRef.current = false; commitLng(); }} />
          </div>
          <div style={{ fontSize: 11, color: 'var(--c-text-soft)', marginTop: 4 }}>{t('pLocation.coordsHint')}</div>
        </div>

        <div style={{
          padding: 12, background: 'var(--c-panel-2)', borderRadius: 8, fontSize: 12,
          color: 'var(--c-text-soft)', lineHeight: 1.5
        }}>
          {t('pLocation.hint')}
        </div>
      </div>
    </>
  );
}

function PanelStyle({ design, setDesign }) {
  const { t } = React.useContext(window.LangContext);
  const selected = (window.MAP_STYLES || []).find(s => s.id === design.style);
  const is3D = !!(selected && selected.is3D);
  return (
    <>
      <div className="panel-header">
        <h3>{t('pStyle.title')}</h3>
        <p>{t('pStyle.sub')}</p>
      </div>
      <div className="panel-body">
        <div className="style-grid">
          {window.MAP_STYLES.map(s => (
            <button
              key={s.id}
              className={`style-tile ${design.style === s.id ? 'active' : ''}`}
              onClick={() => setDesign(d => ({ ...d, style: s.id }))}
            >
              <MapThumb style={s} />
              <span className="style-tile-label">{t('styles.' + s.id)}</span>
            </button>
          ))}
        </div>

        {/* 3D orientation — only visible with the 3D style selected */}
        {is3D && (
          <div style={{ marginTop: 20, paddingTop: 18, borderTop: '1px solid var(--c-line)' }}>
            <div style={{ marginBottom: 10 }}>
              <label className="field-label" style={{ display: 'block' }}>{t('pStyle.orient')}</label>
              <p style={{ fontSize: 12, color: 'var(--c-text-soft)', margin: '2px 0 0', lineHeight: 1.5 }}>
                {t('pStyle.orientSub')}
              </p>
            </div>
            <Tilt3DControls design={design} setDesign={setDesign} />
          </div>
        )}
      </div>
    </>
  );
}

function PanelFrame({ design, setDesign }) {
  return (
    <>
      <div className="panel-header">
        <h3>Marco</h3>
        <p>Solo para previsualización en tu pared.</p>
      </div>
      <div className="panel-body">
        <div className="frame-grid">
          {window.FRAMES.map(f => (
            <button
              key={f.id}
              className={`frame-tile ${design.frame === f.id ? 'active' : ''}`}
              onClick={() => setDesign(d => ({ ...d, frame: f.id }))}
              title={f.name}
            >
              <FrameSwatch id={f.id} />
            </button>
          ))}
        </div>
        <div style={{ fontSize: 11, color: 'var(--c-text-soft)', marginTop: 4 }}>
          El archivo se entrega sin marco — tú decides cómo enmarcarlo al imprimirlo.
        </div>
      </div>
    </>
  );
}

function FrameSwatch({ id }) {
  const styles = {
    none:   { border: '1px dashed var(--c-line)', background: 'var(--c-bg)' },
    white:  { background: 'linear-gradient(135deg, #fcfbf8, #f5f1ea)', border: '1px solid var(--c-line)' },
    black:  { background: 'linear-gradient(135deg, #1d1d1d, #0d0d0d)' },
    walnut: { background: 'linear-gradient(135deg, #6e4a2e, #4d3320)' },
  };
  return (
    <div style={{
      width: '70%', height: '70%',
      ...styles[id],
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <div style={{ width: '60%', height: '70%', background: 'var(--c-panel)' }} />
    </div>
  );
}

function PanelText({ design, setDesign }) {
  const { t } = React.useContext(window.LangContext);
  return (
    <>
      <div className="panel-header">
        <h3>{t('pText.title')}</h3>
        <p>{t('pText.sub')}</p>
      </div>
      <div className="panel-body">
        <div className="field">
          <label className="field-label">{t('pText.headlineLabel')}</label>
          <input className="input" value={design.headline}
            onChange={e => setDesign(d => ({ ...d, headline: e.target.value }))} />
        </div>

        <div className="field">
          <label className="field-label">{t('pText.styleLabel')}</label>
          <div className="toggle-group">
            <button className={design.headlineCase === 'upper' ? 'active' : ''}
              onClick={() => setDesign(d => ({ ...d, headlineCase: 'upper' }))}>{t('pText.caseUpper')}</button>
            <button className={design.headlineCase === 'normal' ? 'active' : ''}
              onClick={() => setDesign(d => ({ ...d, headlineCase: 'normal' }))}>{t('pText.caseNormal')}</button>
          </div>
        </div>

        <div className="field">
          <label className="field-label">{t('pText.phraseLabel')}</label>
          <input className="input" value={design.tagline}
            placeholder={t('pText.phrasePlaceholder')}
            onChange={e => setDesign(d => ({ ...d, tagline: e.target.value }))} />
        </div>

        <div className="toggle-group">
          <button className={design.showCoords ? 'active' : ''}
            onClick={() => setDesign(d => ({ ...d, showCoords: true }))}>{t('pText.withCoords')}</button>
          <button className={!design.showCoords ? 'active' : ''}
            onClick={() => setDesign(d => ({ ...d, showCoords: false }))}>{t('pText.withoutCoords')}</button>
        </div>

        <div style={{ fontSize: 11, color: 'var(--c-text-soft)', lineHeight: 1.5, padding: '10px 0' }}>
          {t('pText.hint')}
        </div>
      </div>
    </>
  );
}

function PanelPin({ design, setDesign }) {
  const { t } = React.useContext(window.LangContext);
  return (
    <>
      <div className="panel-header">
        <h3>{t('pPin.title')}</h3>
        <p>{t('pPin.sub')}</p>
      </div>
      <div className="panel-body">
        <div className="field">
          <label className="field-label">{t('pPin.iconLabel')}</label>
          <div className="icon-picker">
            {window.PIN_ICONS.map(ic => (
              <button
                key={ic.id}
                className={`icon-tile ${design.pinIcon === ic.id ? 'active' : ''}`}
                onClick={() => setDesign(d => ({ ...d, pinIcon: ic.id }))}
              >
                <PinSvg type={ic.id} color={design.pinColor || '#c9694e'} size={28} />
                <span>{t('pins.' + ic.id)}</span>
              </button>
            ))}
          </div>
        </div>

        <div className="field">
          <label className="field-label">{t('pPin.colorLabel')}</label>
          <div className="swatch-row">
            {window.PIN_COLORS.map(c => (
              <button
                key={c}
                className={`swatch ${design.pinColor === c ? 'active' : ''}`}
                style={{ background: c }}
                onClick={() => setDesign(d => ({ ...d, pinColor: c }))}
                aria-label={c}
              />
            ))}
          </div>
        </div>

        <div style={{
          padding: 14, background: 'var(--c-panel-2)', borderRadius: 8, fontSize: 12,
          color: 'var(--c-text-soft)', lineHeight: 1.6,
        }}>
          <b style={{ color: 'var(--c-text)' }}>{t('pPin.mandatoryTitle')}</b><br />
          {t('pPin.mandatoryBody')}
        </div>
      </div>
    </>
  );
}

// Reusable 3D orientation controls (tilt pad + sliders), embedded in PanelStyle.
function Tilt3DControls({ design, setDesign }) {
  const { t } = React.useContext(window.LangContext);
  const padRef = useRefE(null);
  const knobX = ((design.bearing % 360 + 360) % 360) / 360 * 100;
  const knobY = (1 - (design.pitch ?? 55) / 85) * 100;

  function onPad(e) {
    if (!padRef.current) return;
    const r = padRef.current.getBoundingClientRect();
    const x = Math.max(0, Math.min(1, (e.clientX - r.left) / r.width));
    const y = Math.max(0, Math.min(1, (e.clientY - r.top) / r.height));
    setDesign(d => ({
      ...d,
      bearing: Math.round(x * 360),
      pitch: Math.round((1 - y) * 85),
    }));
  }
  function handler(e) {
    onPad(e);
    function move(ev) { onPad(ev); }
    function up() {
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
    }
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
  }

  return (
    <>
      <div className="tilt-pad" ref={padRef} onMouseDown={handler}>
        <div className="tilt-knob" style={{ left: `${knobX}%`, top: `${knobY}%` }} />
        <div className="tilt-hint">
          Pitch {Math.round(design.pitch ?? 55)}° · Bearing {Math.round(design.bearing ?? 20)}°
        </div>
      </div>

      <div className="field">
        <label className="field-label">{t('pStyle.incline')} · {Math.round(design.pitch ?? 55)}°</label>
        <input type="range" min="0" max="85" value={design.pitch ?? 55}
          onChange={e => setDesign(d => ({ ...d, pitch: parseFloat(e.target.value) }))}
          style={{ width: '100%' }} />
      </div>
      <div className="field">
        <label className="field-label">{t('pStyle.rotation')} · {Math.round(design.bearing ?? 20)}°</label>
        <input type="range" min="0" max="360" value={design.bearing ?? 20}
          onChange={e => setDesign(d => ({ ...d, bearing: parseFloat(e.target.value) }))}
          style={{ width: '100%' }} />
      </div>
    </>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// ICONS
// ─────────────────────────────────────────────────────────────────────────
function IconSize()     { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><rect x="5" y="3" width="14" height="18" rx="1.5"/><path d="M9 8h6M9 12h6M9 16h4"/></svg>; }
function IconLocation() { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0z"/><circle cx="12" cy="10" r="3"/></svg>; }
function IconStyle()    { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20M12 2a15 15 0 0 0 0 20"/></svg>; }
function IconFrame()    { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><rect x="3" y="3" width="18" height="18" rx="1"/><rect x="7" y="7" width="10" height="10"/></svg>; }
function IconText()     { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M4 7V4h16v3M9 20h6M12 4v16"/></svg>; }
function IconPin()      { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0z"/><circle cx="12" cy="10" r="3" fill="currentColor"/></svg>; }
function Icon3D()       { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M3 7l9-4 9 4-9 4-9-4z"/><path d="M3 17l9 4 9-4M3 12l9 4 9-4"/></svg>; }
function IconHelp()     { return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="12" cy="12" r="10"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3M12 17h.01"/></svg>; }

Object.assign(window, { Editor });
