'use strict';
/**
 * ElectrolysisSim v2 — Электролиз водных растворов
 * Закон Фарадея: m = M·I·t / (n·F),  F = 96485 Кл/моль
 * Чистый рерайт: стабильная физика, ионная анимация, пузырьки, осадок.
 */
class ElectrolysisSim {
  static F    = 96485;
  static BG   = '#0b0b1a';
  static FONT = 'Manrope, system-ui, sans-serif';

  static ELECTROLYTES = {
    NaCl: {
      name: 'NaCl', displayName: 'NaCl (водный р-р)',
      cation: 'Na\u207A', anion: 'Cl\u207B',
      M: 2, n: 2, R: 8,
      solColor: [160, 200, 230],
      cathodeProduct: 'H\u2082', anodeProduct: 'Cl\u2082',
      depositColor: null,
      cathodeBubColor: 'rgba(160,210,255,0.55)',
      anodeBubColor:   'rgba(180,255,140,0.50)',
      cathodeEq: '2H\u2082O + 2e\u207B \u2192 H\u2082 + 2OH\u207B',
      anodeEq:   '2Cl\u207B \u2212 2e\u207B \u2192 Cl\u2082',
    },
    CuSO4: {
      name: 'CuSO\u2084', displayName: 'CuSO\u2084 (водный р-р)',
      cation: 'Cu\u00B2\u207A', anion: 'SO\u2084\u00B2\u207B',
      M: 63.546, n: 2, R: 12,
      solColor: [55, 120, 210],
      cathodeProduct: 'Cu\u2193', anodeProduct: 'O\u2082',
      depositColor: '#b87333',
      cathodeBubColor: null,
      anodeBubColor:   'rgba(200,210,255,0.50)',
      cathodeEq: 'Cu\u00B2\u207A + 2e\u207B \u2192 Cu\u2193',
      anodeEq:   '2H\u2082O \u2212 4e\u207B \u2192 O\u2082 + 4H\u207A',
    },
    H2SO4: {
      name: 'H\u2082SO\u2084', displayName: 'H\u2082SO\u2084 (водный р-р)',
      cation: 'H\u207A', anion: 'SO\u2084\u00B2\u207B',
      M: 2, n: 2, R: 6,
      solColor: [200, 200, 215],
      cathodeProduct: 'H\u2082', anodeProduct: 'O\u2082',
      depositColor: null,
      cathodeBubColor: 'rgba(160,210,255,0.55)',
      anodeBubColor:   'rgba(200,210,255,0.50)',
      cathodeEq: '2H\u207A + 2e\u207B \u2192 H\u2082',
      anodeEq:   '2H\u2082O \u2212 4e\u207B \u2192 O\u2082 + 4H\u207A',
    },
  };

  constructor(canvas) {
    this.canvas = canvas;
    this.ctx    = canvas.getContext('2d');
    this.W = 0; this.H = 0;

    this.voltage     = 6;
    this.electrolyte = 'NaCl';
    this.speed       = 1;

    this._time         = 0;
    this._massDeposit  = 0;
    this._gasVolume    = 0;
    this._depositH     = 0;
    this._ions         = [];
    this._bubbles      = [];
    this._electronPhase = 0;

    this.playing  = false;
    this._raf     = null;
    this._lastTs  = null;
    this.onUpdate = null;

    new ResizeObserver(() => { this.fit(); this.draw(); }).observe(canvas.parentElement);
  }

  // ── public API ────────────────────────────────────────────────

  fit() {
    const dpr = window.devicePixelRatio || 1;
    const w = this.canvas.offsetWidth  || 640;
    const h = this.canvas.offsetHeight || 420;
    this.canvas.width  = w * dpr;
    this.canvas.height = h * dpr;
    this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    this.W = w; this.H = h;
    this._initIons();
  }

  getParams() { return { voltage: this.voltage, electrolyte: this.electrolyte }; }
  setParams({ voltage, electrolyte } = {}) {
    if (voltage !== undefined) this.voltage = Math.max(1, Math.min(12, +voltage));
    if (electrolyte !== undefined) {
      // accept both 'nacl' (from lab.html) and 'NaCl' (canonical)
      const keyMap = { nacl: 'NaCl', cuso4: 'CuSO4', h2so4: 'H2SO4' };
      const key = keyMap[String(electrolyte).toLowerCase()] || electrolyte;
      if (ElectrolysisSim.ELECTROLYTES[key] && this.electrolyte !== key) {
        this.electrolyte = key;
        this.reset(); return;
      }
    }
    this.draw(); this._emit();
  }

  preset(name) {
    const map = { nacl: ['NaCl', 6], cuso4: ['CuSO4', 4], h2so4: ['H2SO4', 3] };
    const [el, v] = map[name] || map.nacl;
    this.voltage = v; this.electrolyte = el;
    this.reset();
  }

  reset() {
    this.pause();
    this._time = 0; this._massDeposit = 0;
    this._gasVolume = 0; this._depositH = 0;
    this._bubbles = []; this._electronPhase = 0;
    this._initIons();
    this.draw(); this._emit();
  }

  play()  { if (this.playing) return; this.playing = true; this._lastTs = null; this._tick(); }
  pause() { this.playing = false; if (this._raf) { cancelAnimationFrame(this._raf); this._raf = null; } }
  start() { this.play(); }
  stop()  { this.pause(); }

  info() {
    return {
      voltage:       this.voltage,
      current:       +this._current().toFixed(3),
      electrolyte:   this.electrolyte,
      massDeposited: +this._massDeposit.toFixed(4),
      gasVolume:     +this._gasVolume.toFixed(2),
      time:          +this._time.toFixed(1),
    };
  }

  // ── internals ─────────────────────────────────────────────────

  _emit()    { if (this.onUpdate) this.onUpdate(this.info()); }
  _el()      { return ElectrolysisSim.ELECTROLYTES[this.electrolyte]; }
  _current() { return this.voltage / this._el().R; }

  _cell() {
    const { W, H } = this;
    const cw = Math.min(W * 0.50, 300);
    const ch = Math.min(H * 0.48, 210);
    return { cx: (W - cw) / 2, cy: H * 0.28, cw, ch };
  }

  _electrodes() {
    const { cx, cy, cw, ch } = this._cell();
    const ew = 13, eh = ch * 0.70, gap = cw * 0.12;
    const ey = cy + ch - eh - 8;
    return {
      cathode: { x: cx + gap,            y: ey, w: ew, h: eh },
      anode:   { x: cx + cw - gap - ew,  y: ey, w: ew, h: eh },
    };
  }

  _initIons() {
    this._ions = [];
    const { cx, cy, cw, ch } = this._cell();
    if (!cw || !ch) return;
    const el = this._el();
    for (let i = 0; i < 30; i++) {
      const isCat = i < 15;
      this._ions.push({
        x: cx + 18 + Math.random() * (cw - 36),
        y: cy + 12 + Math.random() * (ch - 24),
        vx: (Math.random() - 0.5) * 0.7,
        vy: (Math.random() - 0.5) * 0.5,
        charge: isCat ? 1 : -1,
        label: isCat ? el.cation : el.anion,
        color: isCat ? '#EF476F' : '#06D6E0',
      });
    }
  }

  _spawnIon(charge) {
    const { cx, cy, cw, ch } = this._cell();
    const el = this._el();
    this._ions.push({
      x:  charge > 0 ? cx + 8 : cx + cw - 8,
      y:  cy + 12 + Math.random() * (ch - 24),
      vx: charge > 0 ? 0.55 : -0.55,
      vy: (Math.random() - 0.5) * 0.4,
      charge,
      label: charge > 0 ? el.cation : el.anion,
      color: charge > 0 ? '#EF476F' : '#06D6E0',
    });
  }

  _spawnBubble(x, y, color) {
    this._bubbles.push({
      x, y,
      r:     1.5 + Math.random() * 2.5,
      vx:    (Math.random() - 0.5) * 0.3,
      vy:    -(0.5 + Math.random() * 0.9),
      life:  1,
      decay: 0.005 + Math.random() * 0.007,
      color,
    });
  }

  // ── simulation tick ────────────────────────────────────────────

  _tick() {
    if (!this.playing) return;
    this._raf = requestAnimationFrame(ts => {
      if (!this._lastTs) this._lastTs = ts;
      const dt = Math.min((ts - this._lastTs) / 1000, 0.05) * this.speed;
      this._lastTs = ts;
      this._step(dt); this.draw(); this._emit(); this._tick();
    });
  }

  _step(dt) {
    const el = this._el(), I = this._current();
    const { cx, cy, cw, ch } = this._cell();
    const elec = this._electrodes();

    this._time += dt;
    this._electronPhase = (this._electronPhase + dt * I * 1.2) % 1;

    // Faraday's law
    const molesPS = I / (el.n * ElectrolysisSim.F);
    if (el.depositColor) {
      this._massDeposit += el.M * molesPS * dt;
      this._depositH = Math.min(elec.cathode.h * 0.72, this._depositH + dt * 0.14 * I);
    }
    this._gasVolume += molesPS * 22400 * dt;

    // Ion drift + thermal jitter
    const drift = I * 0.45;
    for (const ion of this._ions) {
      ion.vx += (ion.charge > 0 ? -drift : drift) * dt + (Math.random() - 0.5) * 0.18;
      ion.vy += (Math.random() - 0.5) * 0.14;
      ion.vx  = Math.max(-3.5, Math.min(3.5, ion.vx * 0.96));
      ion.vy  = Math.max(-3.5, Math.min(3.5, ion.vy * 0.96));
      ion.x  += ion.vx; ion.y += ion.vy;
      ion.x   = Math.max(cx + 4, Math.min(cx + cw - 4, ion.x));
      ion.y   = Math.max(cy + 4, Math.min(cy + ch - 4, ion.y));
    }

    // Ions reaching electrodes → discharge + bubbles
    const rm = new Set();
    for (let i = 0; i < this._ions.length; i++) {
      const ion = this._ions[i];
      if (ion.charge > 0 && ion.x <= elec.cathode.x + elec.cathode.w + 5) {
        rm.add(i);
        if (el.cathodeBubColor) {
          for (let b = 0; b < 2; b++)
            this._spawnBubble(
              elec.cathode.x + elec.cathode.w + 2 + Math.random() * 4,
              elec.cathode.y + Math.random() * elec.cathode.h,
              el.cathodeBubColor);
        }
      }
      if (ion.charge < 0 && ion.x >= elec.anode.x - 5) {
        rm.add(i);
        if (el.anodeBubColor) {
          for (let b = 0; b < 2; b++)
            this._spawnBubble(
              elec.anode.x - 2 - Math.random() * 4,
              elec.anode.y + Math.random() * elec.anode.h,
              el.anodeBubColor);
        }
      }
    }
    this._ions = this._ions.filter((_, i) => !rm.has(i));

    // Replenish ions to keep count ~15 each
    let cat = 0, an = 0;
    for (const ion of this._ions) ion.charge > 0 ? cat++ : an++;
    while (cat < 15) { this._spawnIon(1);  cat++; }
    while (an  < 15) { this._spawnIon(-1); an++;  }

    // Bubble physics
    this._bubbles = this._bubbles.filter(b => {
      b.x += b.vx + Math.sin(b.life * 22) * 0.12;
      b.y += b.vy;
      b.life -= b.decay;
      return b.life > 0 && b.y > cy + 2;
    });
  }

  // ── draw ──────────────────────────────────────────────────────

  draw() {
    const { ctx, W, H } = this;
    if (!W || !H) return;

    // Background
    ctx.fillStyle = ElectrolysisSim.BG; ctx.fillRect(0, 0, W, H);

    // Dot grid
    ctx.fillStyle = 'rgba(255,255,255,0.018)';
    for (let x = 22; x < W; x += 22)
      for (let y = 22; y < H; y += 22) {
        ctx.beginPath(); ctx.arc(x, y, 0.7, 0, Math.PI * 2); ctx.fill();
      }

    this._drawWiresAndBattery();
    this._drawCellBody();
    this._drawSolution();
    this._drawDeposit();
    this._drawElectrodes();
    this._drawBubbles();
    this._drawIons();
    this._drawLabels();
    this._drawInfoPanel();
  }

  _drawCellBody() {
    const { ctx } = this;
    const { cx, cy, cw, ch } = this._cell();
    ctx.save();
    ctx.strokeStyle = 'rgba(255,255,255,0.15)'; ctx.lineWidth = 2;
    ctx.beginPath(); ctx.roundRect(cx, cy, cw, ch, 6); ctx.stroke();
    // glass shimmer
    const gg = ctx.createLinearGradient(cx, cy, cx + 14, cy);
    gg.addColorStop(0, 'rgba(255,255,255,0.05)');
    gg.addColorStop(1, 'rgba(255,255,255,0)');
    ctx.fillStyle = gg; ctx.fillRect(cx + 1, cy + 1, 14, ch - 2);
    ctx.restore();
  }

  _drawSolution() {
    const { ctx } = this;
    const { cx, cy, cw, ch } = this._cell();
    const [r, g, b] = this._el().solColor;
    ctx.save();
    ctx.beginPath(); ctx.roundRect(cx + 2, cy + 2, cw - 4, ch - 4, 4); ctx.clip();
    const sg = ctx.createLinearGradient(cx, cy, cx, cy + ch);
    sg.addColorStop(0, `rgba(${r},${g},${b},0.06)`);
    sg.addColorStop(1, `rgba(${r},${g},${b},0.22)`);
    ctx.fillStyle = sg; ctx.fillRect(cx + 2, cy + 2, cw - 4, ch - 4);
    ctx.restore();
  }

  _drawElectrodes() {
    const { ctx } = this;
    const e = this._electrodes();
    const FN = ElectrolysisSim.FONT;

    ctx.fillStyle = '#42425a';
    ctx.beginPath(); ctx.roundRect(e.cathode.x, e.cathode.y, e.cathode.w, e.cathode.h, 3); ctx.fill();
    ctx.strokeStyle = 'rgba(6,214,224,0.3)'; ctx.lineWidth = 1;
    ctx.beginPath(); ctx.roundRect(e.cathode.x, e.cathode.y, e.cathode.w, e.cathode.h, 3); ctx.stroke();

    ctx.fillStyle = '#525268';
    ctx.beginPath(); ctx.roundRect(e.anode.x, e.anode.y, e.anode.w, e.anode.h, 3); ctx.fill();
    ctx.strokeStyle = 'rgba(239,71,111,0.3)'; ctx.lineWidth = 1;
    ctx.beginPath(); ctx.roundRect(e.anode.x, e.anode.y, e.anode.w, e.anode.h, 3); ctx.stroke();

    ctx.font = `bold 16px ${FN}`; ctx.textAlign = 'center'; ctx.textBaseline = 'bottom';
    ctx.fillStyle = '#06D6E0';
    ctx.fillText('\u2212', e.cathode.x + e.cathode.w / 2, e.cathode.y - 3);
    ctx.fillStyle = '#EF476F';
    ctx.fillText('+', e.anode.x + e.anode.w / 2, e.anode.y - 3);
  }

  _drawDeposit() {
    const el = this._el();
    if (!el.depositColor || this._depositH < 1) return;
    const { ctx } = this;
    const c = this._electrodes().cathode;
    const dh = Math.min(this._depositH, c.h * 0.72);
    ctx.save();
    const dg = ctx.createLinearGradient(c.x + c.w, c.y + c.h - dh, c.x + c.w + 10, c.y + c.h);
    dg.addColorStop(0, 'rgba(184,115,51,0.35)');
    dg.addColorStop(1, 'rgba(184,115,51,0.85)');
    ctx.fillStyle = dg;
    ctx.beginPath(); ctx.roundRect(c.x + c.w, c.y + c.h - dh, 10, dh, [2, 2, 0, 0]); ctx.fill();
    ctx.shadowColor = '#b87333'; ctx.shadowBlur = 6;
    ctx.fillStyle = 'rgba(210,150,80,0.5)';
    ctx.beginPath(); ctx.roundRect(c.x + c.w, c.y + c.h - dh, 10, 3, [2, 2, 0, 0]); ctx.fill();
    ctx.restore();
  }

  _drawIons() {
    const { ctx } = this;
    const FN = ElectrolysisSim.FONT;
    ctx.save();
    for (const ion of this._ions) {
      const g = ctx.createRadialGradient(ion.x, ion.y, 0, ion.x, ion.y, 11);
      g.addColorStop(0, ion.color + '2a'); g.addColorStop(1, ion.color + '00');
      ctx.fillStyle = g;
      ctx.beginPath(); ctx.arc(ion.x, ion.y, 11, 0, Math.PI * 2); ctx.fill();
      ctx.fillStyle = ion.color;
      ctx.beginPath(); ctx.arc(ion.x, ion.y, 4, 0, Math.PI * 2); ctx.fill();
      ctx.fillStyle = 'rgba(255,255,255,0.68)';
      ctx.font = `8px ${FN}`; ctx.textAlign = 'center'; ctx.textBaseline = 'bottom';
      ctx.fillText(ion.label, ion.x, ion.y - 5);
    }
    ctx.restore();
  }

  _drawBubbles() {
    const { ctx } = this;
    ctx.save();
    for (const b of this._bubbles) {
      ctx.globalAlpha = b.life * 0.65;
      ctx.strokeStyle = b.color; ctx.lineWidth = 0.8;
      ctx.beginPath(); ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2); ctx.stroke();
      ctx.fillStyle = `rgba(255,255,255,${b.life * 0.18})`;
      ctx.beginPath(); ctx.arc(b.x - b.r * 0.3, b.y - b.r * 0.3, b.r * 0.3, 0, Math.PI * 2); ctx.fill();
    }
    ctx.restore();
  }

  _drawWiresAndBattery() {
    const { ctx } = this;
    const { cx, cy, cw } = this._cell();
    const e = this._electrodes();
    const FN = ElectrolysisSim.FONT;

    const cXt = e.cathode.x + e.cathode.w / 2;  // cathode top center
    const aXt = e.anode.x   + e.anode.w   / 2;  // anode top center
    const bx  = cx + cw / 2;                     // battery center x
    const by  = cy - Math.max(42, this.H * 0.09); // battery y

    ctx.save();
    ctx.strokeStyle = 'rgba(255,255,255,0.2)'; ctx.lineWidth = 1.5;

    // Cathode wire: up then right to battery −
    ctx.beginPath();
    ctx.moveTo(cXt, e.cathode.y); ctx.lineTo(cXt, by); ctx.lineTo(bx - 22, by);
    ctx.stroke();

    // Anode wire: up then left to battery +
    ctx.beginPath();
    ctx.moveTo(aXt, e.anode.y); ctx.lineTo(aXt, by); ctx.lineTo(bx + 22, by);
    ctx.stroke();

    // Electron flow dots (cathode side: from battery − toward cathode)
    const dist = (bx - 22) - cXt;
    for (let i = 0; i < 4; i++) {
      const t = ((this._electronPhase + i / 4) % 1);
      const ex = (bx - 22) - t * dist;
      const ey = by;
      if (ex >= cXt - 1 && ex <= bx - 22 + 1) {
        ctx.fillStyle = '#4CC9F0'; ctx.shadowColor = '#4CC9F0'; ctx.shadowBlur = 5;
        ctx.beginPath(); ctx.arc(ex, ey, 2.5, 0, Math.PI * 2); ctx.fill();
        ctx.shadowBlur = 0;
      }
    }

    // Battery symbol — two plates
    ctx.strokeStyle = '#EF476F'; ctx.lineWidth = 3;
    ctx.beginPath(); ctx.moveTo(bx + 22, by - 14); ctx.lineTo(bx + 22, by + 14); ctx.stroke();
    ctx.strokeStyle = '#06D6E0'; ctx.lineWidth = 1.8;
    ctx.beginPath(); ctx.moveTo(bx - 22, by - 8);  ctx.lineTo(bx - 22, by + 8);  ctx.stroke();
    // Connecting wire between battery plates
    ctx.strokeStyle = 'rgba(255,255,255,0.12)'; ctx.lineWidth = 1;
    ctx.beginPath(); ctx.moveTo(bx - 22, by); ctx.lineTo(bx + 22, by); ctx.stroke();

    // Voltage label
    ctx.fillStyle = '#FFD166';
    ctx.font = `bold 12px ${FN}`; ctx.textAlign = 'center'; ctx.textBaseline = 'bottom';
    ctx.fillText(this.voltage.toFixed(1) + ' V', bx, by - 18);

    // +/− labels on battery
    ctx.fillStyle = '#EF476F'; ctx.font = `bold 10px ${FN}`; ctx.textAlign = 'left';  ctx.textBaseline = 'middle';
    ctx.fillText('+', bx + 26, by);
    ctx.fillStyle = '#06D6E0'; ctx.textAlign = 'right';
    ctx.fillText('\u2212', bx - 26, by);

    ctx.restore();
  }

  _drawLabels() {
    const { ctx } = this;
    const el = this._el(), e = this._electrodes();
    const { cx, cy, cw, ch } = this._cell();
    const FN = ElectrolysisSim.FONT;

    ctx.save();
    ctx.font = `10px ${FN}`; ctx.textAlign = 'center'; ctx.textBaseline = 'top';

    ctx.fillStyle = '#06D6E0';
    ctx.fillText('\u041A\u0430\u0442\u043E\u0434 (\u2212)', e.cathode.x + e.cathode.w / 2, cy + ch + 6);
    ctx.fillStyle = '#EF476F';
    ctx.fillText('\u0410\u043D\u043E\u0434 (+)', e.anode.x + e.anode.w / 2, cy + ch + 6);

    ctx.fillStyle = 'rgba(255,255,255,0.42)';
    ctx.fillText(el.cathodeProduct, e.cathode.x + e.cathode.w / 2, cy + ch + 20);
    ctx.fillText(el.anodeProduct,   e.anode.x   + e.anode.w   / 2, cy + ch + 20);

    ctx.fillStyle = '#9B5DE5'; ctx.font = `bold 11px ${FN}`;
    ctx.fillText(el.displayName, cx + cw / 2, cy + ch + 36);

    ctx.font = `8px ${FN}`;
    ctx.fillStyle = 'rgba(6,214,224,0.48)';
    ctx.fillText(el.cathodeEq, e.cathode.x + e.cathode.w / 2, cy + ch + 52);
    ctx.fillStyle = 'rgba(239,71,111,0.48)';
    ctx.fillText(el.anodeEq,   e.anode.x   + e.anode.w   / 2, cy + ch + 52);

    ctx.restore();
  }

  _drawInfoPanel() {
    const { ctx } = this;
    const inf = this.info(), el = this._el();
    const FN = ElectrolysisSim.FONT;
    const px = 12, py = 10, pw = 170, lh = 17;

    const rows = [
      ['U', inf.voltage.toFixed(1) + ' \u0412'],
      ['I', this._current().toFixed(3) + ' \u0410'],
      ['\u0422\u0432\u0440\u0435\u043C\u044F', this._fmtTime(inf.time)],
    ];
    if (el.depositColor) rows.push(['m(Cu)', inf.massDeposited.toFixed(4) + ' \u0433']);
    rows.push(['V(\u0433\u0430\u0437)', inf.gasVolume.toFixed(2) + ' \u043C\u043B']);

    const ph = 12 + rows.length * lh + 8;
    ctx.save();
    ctx.fillStyle = 'rgba(5,5,20,0.86)';
    ctx.beginPath(); ctx.roundRect(px, py, pw, ph, 7); ctx.fill();
    ctx.strokeStyle = 'rgba(255,255,255,0.07)'; ctx.lineWidth = 1;
    ctx.beginPath(); ctx.roundRect(px, py, pw, ph, 7); ctx.stroke();

    ctx.font = `10px ${FN}`; ctx.textBaseline = 'middle';
    rows.forEach(([k, v], i) => {
      const ry = py + 10 + i * lh + lh / 2;
      ctx.fillStyle = 'rgba(255,255,255,0.38)'; ctx.textAlign = 'left';  ctx.fillText(k, px + 10, ry);
      ctx.fillStyle = 'rgba(255,255,255,0.88)'; ctx.textAlign = 'right'; ctx.fillText(v, px + pw - 10, ry);
    });

    ctx.fillStyle = 'rgba(255,255,255,0.16)';
    ctx.font = `italic 8px ${FN}`; ctx.textAlign = 'left';
    ctx.fillText('m = M\u00B7I\u00B7t / (n\u00B7F)', px + 10, py + ph + 10);
    ctx.restore();
  }

  _fmtTime(s) {
    if (s < 60) return s.toFixed(1) + ' \u0441';
    return Math.floor(s / 60) + ' \u043C\u0438\u043D ' + (s % 60).toFixed(0) + ' \u0441';
  }
}

if (typeof module !== 'undefined') module.exports = ElectrolysisSim;

/* ─── lab UI init ─────────────────────────────────── */
  function _openElectrolysis() {
    document.getElementById('sim-topbar-title').textContent = 'Электролиз';
    _simShow('sim-electrolysis');
    _registerSimState('electrolysis', () => elecSim?.getParams(), st => elecSim?.setParams(st));
    if (_embedMode) _startStateEmit('electrolysis');
    requestAnimationFrame(() => requestAnimationFrame(() => {
      if (!elecSim) {
        elecSim = new ElectrolysisSim(document.getElementById('electrolysis-canvas'));
        elecSim.onUpdate = _elecUpdateUI;
      }
      elecSim.fit();
      elecSim.reset();
      elecSim.play();
    }));
  }

  function elecParam(name, val) {
    const v = parseFloat(val);
    if (name === 'voltage') document.getElementById('elec-V-val').textContent = v;
    if (elecSim) elecSim.setParams({ [name]: v });
  }

  function elecPreset(name, btn) {
    document.querySelectorAll('.elec-type-btn').forEach(b => b.classList.remove('active'));
    if (btn) btn.classList.add('active');
    const voltages = { nacl: 6, cuso4: 4, h2so4: 3 };
    const vt = voltages[name] || 6;
    document.getElementById('sl-elec-V').value = vt; document.getElementById('elec-V-val').textContent = vt;
    if (elecSim) { elecSim.setParams({ electrolyte: name, voltage: vt }); elecSim.reset(); elecSim.play(); }
  }

  function _elecUpdateUI(info) {
    const v = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val; };
    v('elecbar-v1', typeof info.current === 'number' ? info.current.toFixed(2) : '—');
    v('elecbar-v2', typeof info.massDeposited === 'number' ? info.massDeposited.toFixed(3) + ' г' : '—');
    v('elecbar-v3', typeof info.gasVolume === 'number' ? info.gasVolume.toFixed(1) : '—');
    v('elecbar-v4', typeof info.time === 'number' ? info.time.toFixed(0) + ' с' : '—');
  }

  /* ── waves ── */
