// Main plugin faceplate component — MENTESH2A
// Photoreal Teletronix LA-2A replica.
//
// Three variant set per brand spec (me-plugins-web/brand/MENTESH2A-BRAND.mdx
// §3 Faceplate variants, 2026-05-04). All three drive identical DSP — the
// difference is purely visual.
//
// Brand tokens reference:
//   amber   = #FFA94D
//   copper  = #B87333  (a.k.a. brass-tone)
//   bone    = #EAE0D0
//   bone2   = #D9CDB9
//   bg      = #12100E
// 2026-05-04 (operator): the original Claude-Designer mockup shipped FIVE
// distinct faceplates.  Silver Teletronix is the only one that's faithful to
// the operator's actual studio unit; the other four are chrome-only "presets"
// the user can switch to for visual taste.  All five drive identical DSP.
//
// Brand-spec naming (silver_teletronix / studio_black / me_classics) is kept
// as the canonical key; the other two (grey, purple) are first-class entries
// so the picker exposes them.  Old saves that referenced the legacy short
// keys still resolve via the alias block at the bottom.
const VARIANTS = {
  // Default — Silver Teletronix faceplate, MATCHES THE OPERATOR'S ACTUAL UNIT.
  // The MENTESH2A wordmark stays RED (Teletronix-heritage red, #b8211a) —
  // brass per the brand book §3 applies to hardware bits (knob caps, screws,
  // pinstripe), NOT the wordmark.  Brand spec misread on 2026-05-04 caused
  // the wordmark to flip to brass briefly; restored to red here.
  silver_teletronix: {
    name: 'Silver (Teletronix) — your unit',
    faceplate: 'linear-gradient(180deg, #f4f2ea 0%, #ede9dc 28%, #e4e0d0 70%, #d8d2c0 100%)',
    faceplateTexture: true,
    ink: '#1a1410',
    inkAccent: '#b8211a',   // Teletronix RED — iconic wordmark colour (NEVER brass on Silver)
    knobStyle: 'bakelite',
    accent: '#b8211a',
    rack: 'linear-gradient(180deg, #ede9dc 0%, #d8d2c0 100%)',
  },
  // UA reissue era — darker grey panel, amber accent.
  grey: {
    name: 'Grey (UA era)',
    faceplate: 'linear-gradient(180deg, #5e5a52 0%, #48443c 50%, #322e27 100%)',
    faceplateTexture: true,
    ink: '#0a0806',
    inkAccent: '#c89a50',
    knobStyle: 'bakelite',
    accent: '#c89a50',
    rack: 'linear-gradient(180deg, #5e5a52 0%, #322e27 100%)',
  },
  // Modern dark studio — bakelite faceplate, amber LED accents.  House
  // signature colourway (per brand spec §3 Studio Black).
  studio_black: {
    name: 'MENTESH Signature (Black)',
    faceplate: 'linear-gradient(180deg, #1C1917 0%, #12100E 60%, #0B0907 100%)',
    faceplateTexture: false,
    ink: '#EAE0D0',  // bone for ink readability
    inkAccent: '#FFA94D',  // amber per brand
    knobStyle: 'bakelite',
    accent: '#FFA94D',
    rack: 'linear-gradient(180deg, #1C1917 0%, #0B0907 100%)',
    showPinstripes: false,
    showWear: false,
  },
  // Deep purple — operator's favourite "loud" preset, reads on dark studios.
  purple: {
    name: 'Deep Purple',
    faceplate: 'linear-gradient(180deg, #3a2850 0%, #271838 50%, #180e24 100%)',
    faceplateTexture: true,
    ink: '#e8d8f8',
    inkAccent: '#c878f0',
    knobStyle: 'bakelite',
    accent: '#c878f0',
    rack: 'linear-gradient(180deg, #3a2850 0%, #180e24 100%)',
  },
  // Studio Cream — bone faceplate + brass accent (formerly named me_classics
  // in brand spec, exposed to user as "Studio Cream" matching the original
  // designer naming the operator referenced 2026-05-04).
  me_classics: {
    name: 'Studio Cream',
    faceplate: 'linear-gradient(180deg, #e8dcbc 0%, #d4c69e 50%, #b8a880 100%)',
    faceplateTexture: true,
    ink: '#2a1a0a',
    inkAccent: '#8a2818',
    knobStyle: 'bakelite',
    accent: '#6a4a20',
    rack: 'linear-gradient(180deg, #d8c89a 0%, #a88860 100%)',
  },
  // Backwards-compat aliases for old saves / brand-spec keys.
  silver: null,
  mentesh_black: null,
  cream: null,
};
// Wire up legacy aliases (avoid forward-ref in object literal)
VARIANTS.silver         = VARIANTS.silver_teletronix;  // brand-spec long key → short
VARIANTS.mentesh_black  = VARIANTS.studio_black;       // legacy alias
VARIANTS.cream          = VARIANTS.me_classics;        // legacy alias for Studio Cream

// === Per-faceplate DSP profiles (Phase 9i+, 2026-05-04, operator-locked) ====
// Each face shares the SAME compressor engine and SAME default knob
// positions (PR 40 / Gain 40 / Mix 100 / link off — matches the operator's
// modeled unit at neutral, identical to ParameterLayout defaults).
//
// Per-face DSP variance is TWO things only:
//   1. Photocell time-constant scale (aScale / rScale) → envelope follower
//      tau multiplier.  Models ±30% T4B cell variance (UA service notes).
//   2. Output HF shelf (outHfDb / outHfFcHz) → audio-path top-end tilt.
//      Models trafo + tube + cell HF response across cell ages / mods.
//
// Variant onChange (in tweaks.jsx) fires:
//   juceInvoke('setOptoTimeScale',     aScale, rScale)
//   juceInvoke('setOptoOutputHfShelf', outHfDb, outHfFcHz)
// Knobs never auto-move.  Per-use-case knob recall is handled by the
// orthogonal PresetBar (factory + user presets, bottom-left).
//
// Silver Teletronix = 1.00/1.00 + 0 dB = bit-identical to your studio unit
// (modded UA reissue: Sowter 8940 + RCA NOS = bright sweet).
// Each face = aScale/rScale (cell speed) + outHfDb/outHfFcHz (HF tilt) +
// outLfDb/outLfFcHz (LF tilt, beta10).  Silver = all-zero (untouched).
const VARIANT_DSP_PROFILES = {
  // The reference — your modded UA unit, all shelves bypassed.
  silver_teletronix: { aScale: 1.00, rScale: 1.00, outHfDb:  0.0, outHfFcHz: 6000, outLfDb:  0.0, outLfFcHz: 80 },
  // Grey UA-era — lazier cell + darker top (no Sowter mod).  LF flat.
  grey:              { aScale: 1.20, rScale: 1.60, outHfDb: -2.0, outHfFcHz: 6000, outLfDb:  0.0, outLfFcHz: 80 },
  // MENTESH Signature Black — tighter cell + brighter top (modern punch).  LF flat.
  studio_black:      { aScale: 0.70, rScale: 0.48, outHfDb: +1.0, outHfFcHz: 8000, outLfDb:  0.0, outLfFcHz: 80 },
  // Deep Purple — slowest cell + dark top + slight LF tuck for clarity in glue role.
  purple:            { aScale: 1.50, rScale: 2.00, outHfDb: -3.0, outHfFcHz: 5000, outLfDb: -2.0, outLfFcHz: 60 },
  // Studio Cream — slightly slower cell + warm top + gentle LF rolloff (vintage feel).
  me_classics:       { aScale: 1.00, rScale: 1.20, outHfDb: -2.0, outHfFcHz: 5000, outLfDb: -1.0, outLfFcHz: 80 },
};
// Aliases — match the VARIANTS alias map so old saves resolve cleanly.
VARIANT_DSP_PROFILES.silver        = VARIANT_DSP_PROFILES.silver_teletronix;
VARIANT_DSP_PROFILES.mentesh_black = VARIANT_DSP_PROFILES.studio_black;
VARIANT_DSP_PROFILES.cream         = VARIANT_DSP_PROFILES.me_classics;

function MENTESH2A({ tweaks, setTweaks, audioLevel = 0, isPlaying = false }) {
  const {
    variant = 'silver',
    wear = 0.12,
    tubeGlow = 0.7,
    needleColor = '#0a0604',
    showLogo = true,
    knobStyle: knobStyleTweak = 'bakelite',
    showScrews = true,
    lighting = 'bright',
  } = tweaks;

  const v = VARIANTS[variant];
  // Variant may force a specific knob style (e.g. Modern Black -> studio-satin).
  // The tweaks panel still overrides it if the user picks a different style explicitly.
  const knobStyle = knobStyleTweak === 'bakelite' && v.knobStyle ? v.knobStyle : knobStyleTweak;

  const [peakRed, setPeakRedRaw] = React.useState(0.40);
  const [gain, setGainRaw] = React.useState(0.40);
  const [mode, setModeRaw] = React.useState(true);
  const [power, setPowerRaw] = React.useState(true);
  const [meterMode, setMeterMode] = React.useState('GR');
  const [realGr, setRealGr] = React.useState(0);

  // ── JUCE bridge — Phase 7 wire (BISECT pass) ──
  const isWired = typeof window !== 'undefined' && !!window.__JUCE__ && !!window.__JUCE__.backend;

  // STEREO LINK (∞ button between knobs).  Industry-standard: ON = L/R
  // sidechains coupled (matched GR across channels), OFF = independent.
  // Maps to the continuous APVTS `link` 0..100% — UI exposes simple on/off
  // (100 / 0); host can still automate the underlying continuous param.
  // Phase 9e (2026-05-04): replaced the previous Gain↔PR knob-link feature
  // (see git history) per operator request — ∞ icon now exclusively means
  // stereo link, matching UAD/Slate/PluginAlliance convention.
  // 2026-05-04 operator decision: default OFF.  Engaging stereo link is now
  // an explicit choice (click the red ∞ dot to couple L/R sidechains), which
  // matches the bus-comp default convention used by SSL/UAD/PA.
  const [stereoLinked, setStereoLinked] = React.useState(false);

  const setPeakRed = React.useCallback((v) => {
    setPeakRedRaw(v);
    if (isWired) window.juceInvoke('setParam', 'peakRed', v);
  }, [isWired]);
  const setGain = React.useCallback((v) => {
    setGainRaw(v);
    if (isWired) window.juceInvoke('setParam', 'gain', v);
  }, [isWired]);
  const toggleStereoLink = React.useCallback(() => {
    setStereoLinked((wasLinked) => {
      const next = !wasLinked;
      if (isWired) window.juceInvoke('setParam', 'link', next ? 1.0 : 0.0); // normalized
      return next;
    });
  }, [isWired]);
  const setMode = React.useCallback((v) => {
    setModeRaw(v);
    if (isWired) window.juceInvoke('setParam', 'mode', v ? 1.0 : 0.0);
  }, [isWired]);
  const setPower = React.useCallback((v) => {
    setPowerRaw(v);
    if (isWired) window.juceInvoke('setParam', 'bypass', v ? 0.0 : 1.0);
  }, [isWired]);

  // Output level meter — pushed @ 30 Hz from C++ (post-DSP RMS in dBFS,
  // negative or 0).  Used by the +4 / -10 meter modes.
  const [realOutDb, setRealOutDb] = React.useState(-60);

  const [outputMode, setOutputMode] = React.useState(0);
  const [mix, setMixRaw] = React.useState(1.0);
  const [phaseInvert, setPhaseInvertRaw] = React.useState(false);
  const [scFilter, setScFilterRaw] = React.useState(0.0); // 0 = off, 1 = full HPF // 0..1, default 100% wet
  const [emphasis, setEmphasisRaw] = React.useState(0.0); // R37 LIM/RESP HF emphasis pot, 0..1 normalized (0 = factory-flat)
  const [outTrim,  setOutTrimRaw]  = React.useState(0.5);  // ±12 dB final trim, 0..1 normalized (0.5 = 0 dB)

  const setMix = React.useCallback((v) => {
    setMixRaw(v);
    if (isWired) window.juceInvoke('setParam', 'mix', v);     // 0..1 normalized
  }, [isWired]);
  const setScFilter = React.useCallback((v) => {
    setScFilterRaw(v);
    if (isWired) window.juceInvoke('setParam', 'scHpf', v);   // 0..1 normalized
  }, [isWired]);
  const setEmphasis = React.useCallback((v) => {
    setEmphasisRaw(v);
    if (isWired) window.juceInvoke('setParam', 'emphasis', v); // 0..1 normalized
  }, [isWired]);
  const setOutTrim = React.useCallback((v) => {
    setOutTrimRaw(v);
    if (isWired) window.juceInvoke('setParam', 'outTrim', v); // 0..1 normalized → -12..+12 dB
  }, [isWired]);

  React.useEffect(() => {
    if (!isWired) return;
    const backend = window.__JUCE__.backend;
    window.juceInvoke('getParamSnapshot').then((snap) => {
      if (!snap) return;
      if (typeof snap.peakRed === 'number') setPeakRedRaw(snap.peakRed);
      if (typeof snap.gain    === 'number') setGainRaw(snap.gain);
      if (typeof snap.mode    === 'number') setModeRaw(snap.mode >= 0.5);
      if (typeof snap.bypass  === 'number') setPowerRaw(snap.bypass < 0.5);
      if (typeof snap.mix     === 'number') setMixRaw(snap.mix);
      if (typeof snap.scHpf   === 'number') setScFilterRaw(snap.scHpf);
      if (typeof snap.emphasis === 'number') setEmphasisRaw(snap.emphasis);
      if (typeof snap.outTrim === 'number') setOutTrimRaw(snap.outTrim);
      if (typeof snap.link    === 'number') setStereoLinked(snap.link >= 0.5);
      if (typeof snap.phase   === 'number') setPhaseInvertRaw(snap.phase >= 0.5);
      if (typeof snap.satDrive === 'number') setSatDriveRaw(snap.satDrive);
      if (typeof snap.satMode === 'number') setSatModeRaw(snap.satMode >= 0.5 ? 'notch' : 'lift');
      if (typeof snap.satMatch === 'number') setSatMatchRaw(snap.satMatch >= 0.5);
      if (typeof snap.satLow  === 'number' ||
          typeof snap.satMid  === 'number' ||
          typeof snap.satHigh === 'number') {
        setSatBandsRaw({
          low:  snap.satLow  >= 0.5,
          mid:  snap.satMid  >= 0.5,
          high: snap.satHigh >= 0.5,
        });
      }
    });
    const onGrMeter = (e) => {
      if (e && typeof e.db === 'number') setRealGr(e.db);
    };
    const onOutLevel = (e) => {
      if (e && typeof e.db === 'number') setRealOutDb(e.db);
    };
    backend.addEventListener('grMeter', onGrMeter);
    backend.addEventListener('outLevel', onOutLevel);
    return () => {
      backend.removeEventListener('grMeter', onGrMeter);
      backend.removeEventListener('outLevel', onOutLevel);
    };
  }, [isWired]);
  // ── License state (polled from C++ LicenseManager via juceInvoke) ──
  const [licenseState, setLicenseState] = React.useState({
    state: 'inactive',          // 'inactive' | 'trial' | 'licensed' | 'demo'
    trialDaysLeft: 0,
    scope: '',
    maskedKey: '',
  });
  const [licenseDialogOpen, setLicenseDialogOpen] = React.useState(false);

  // Refresh license snapshot on plug load + every 60s after.
  React.useEffect(() => {
    if (!isWired) return;
    let cancelled = false;
    const refresh = () => {
      window.juceInvoke('getLicenseSnapshot').then((snap) => {
        if (!cancelled && snap) setLicenseState(snap);
      });
    };
    refresh();
    const id = setInterval(refresh, 60000);
    return () => { cancelled = true; clearInterval(id); };
  }, [isWired]);

  const activateLicense = React.useCallback(async (key) => {
    if (!isWired) return { ok: false, message: 'Not wired to host' };
    // Server caps machine_label at 80 chars. Build a short, recognisable label:
    // "MENTESH2A · macOS · <DAW host name from URL or "Studio">".
    const ua = (typeof navigator !== 'undefined' && navigator.userAgent) || '';
    const platformHint = /Mac/i.test(ua) ? 'macOS'
                       : /Win/i.test(ua) ? 'Windows'
                       : /Linux/i.test(ua) ? 'Linux' : 'Studio';
    const machineLabel = ('MENTESH2A · ' + platformHint).slice(0, 80);
    const res = await window.juceInvoke('activateLicense', key, machineLabel);
    // Re-read snapshot after activation
    const snap = await window.juceInvoke('getLicenseSnapshot');
    if (snap) setLicenseState(snap);
    return res;
  }, [isWired]);

  const deactivateLicense = React.useCallback(async () => {
    if (!isWired) return { ok: false, message: 'Not wired' };
    const res = await window.juceInvoke('deactivateLicense');
    const snap = await window.juceInvoke('getLicenseSnapshot');
    if (snap) setLicenseState(snap);
    return res;
  }, [isWired]);

  // Saturation insert (Saturn-inspired)
  const [satDrive, setSatDriveRaw] = React.useState(0.0);
  const [satMode, setSatModeRaw] = React.useState('lift'); // 'lift' | 'notch'
  const [satBands, setSatBandsRaw] = React.useState({ low: false, mid: true, high: false });
  const [satMatch, setSatMatchRaw] = React.useState(false);  // level-match LIFT to clean

  // ── Phase 7 wire pass 2 — phase invert + saturation ─────────────────
  const setPhaseInvert = React.useCallback((v) => {
    setPhaseInvertRaw(v);
    if (isWired) window.juceInvoke('setParam', 'phase', v ? 1.0 : 0.0);
  }, [isWired]);
  const setSatDrive = React.useCallback((v) => {
    setSatDriveRaw(v);
    if (isWired) window.juceInvoke('setParam', 'satDrive', v);   // 0..1
  }, [isWired]);
  const setSatMode = React.useCallback((v) => {
    setSatModeRaw(v);                                            // 'lift' | 'notch'
    if (isWired) window.juceInvoke('setParam', 'satMode', v === 'notch' ? 1.0 : 0.0);
  }, [isWired]);
  const setSatMatch = React.useCallback((v) => {
    setSatMatchRaw(v);
    if (isWired) window.juceInvoke('setParam', 'satMatch', v ? 1.0 : 0.0);
  }, [isWired]);
  const setSatBands = React.useCallback((next) => {
    // next can be a function or an object — notch React useState semantics
    setSatBandsRaw((prev) => {
      const v = typeof next === 'function' ? next(prev) : next;
      if (isWired) {
        window.juceInvoke('setParam', 'satLow',  v.low  ? 1.0 : 0.0);
        window.juceInvoke('setParam', 'satMid',  v.mid  ? 1.0 : 0.0);
        window.juceInvoke('setParam', 'satHigh', v.high ? 1.0 : 0.0);
      }
      return v;
    });
  }, [isWired]);

  const simulatedGR = React.useMemo(() => {
    if (!isPlaying) return -20;
    const threshold = 0.3 + (1 - peakRed) * 0.5;
    const over = Math.max(0, audioLevel - threshold);
    const gr = -over * 15 * (mode ? 1.3 : 1);
    return Math.max(-14, gr - 1.5);
  }, [audioLevel, peakRed, mode, isPlaying]);

  // GR meter: use real DSP value when wired (negative dB shown as
  // negative needle deflection); fall back to in-browser simulated curve
  // for design previews (no JUCE backend).
  const grForMeter = isWired ? -Math.abs(realGr) : simulatedGR;
  // Output level meter (+4 / -10).  When wired, realOutDb is post-DSP RMS in
  // dBFS (negative or 0).  +4 mode shows reference at +4 dBu nominal — we
  // map it so 0 dBFS plug output ≈ +6 on the meter; -10 mode is consumer
  // reference (slightly hotter looking for the same level).  Formula chosen
  // for "needle reads roughly correctly for typical mix levels" rather than
  // strict dBu calibration.
  const outForMeterPlus4   = isWired ? realOutDb + 6  : (isPlaying ? -4  + audioLevel * 10 : -20);
  const outForMeterMinus10 = isWired ? realOutDb + 12 : (isPlaying ? -10 + audioLevel * 13 : -20);
  const meterValue = meterMode === 'GR' ? grForMeter :
    meterMode === '+4' ? outForMeterPlus4 :
    outForMeterMinus10;

  return (
    <div style={{
      width: 1400, height: 410,
      display: 'flex',
      filter: lighting === 'dark' ? 'brightness(0.7) contrast(1.1)' : 'none',
      transition: 'filter 400ms',
    }}>
      <RackEar side="left" variant={v} />

      {/* License banner + dialog — desktop JUCE only, hidden on web demo.
          The web embed installs a no-op `window.juceInvoke` polyfill in
          MENTESH2A.html for tweaks parity, so the previous
          `typeof window.juceInvoke === 'function'` check was always
          truthy on the website (real bug seen in v1.0.1: the
          NOT ACTIVATED · CLICK TO ENTER LICENCE pill leaked into
          /mentesh2a). Discriminator that actually works: the iframe
          test — desktop JUCE WebView runs at top, the website embed
          runs inside an iframe. */}
      {typeof window !== 'undefined' &&
       (function () { try { return window.self === window.top; } catch (_) { return false; } })() && (
        <>
          <LicenseBanner
            state={licenseState}
            onClick={() => setLicenseDialogOpen(true)}
            v={v}
          />
          {licenseDialogOpen && (
            <LicenseDialog
              state={licenseState}
              onActivate={activateLicense}
              onDeactivate={deactivateLicense}
              onClose={() => setLicenseDialogOpen(false)}
              v={v}
            />
          )}
        </>
      )}

      <div style={{
        flex: 1,
        background: v.faceplate,
        position: 'relative',
        boxShadow: 'inset 0 2px 4px rgba(255,255,255,0.3), inset 0 -2px 6px rgba(0,0,0,0.3)',
        overflow: 'hidden',
        '--ink-engrave': v.ink,
      }}>
        {/* brushed metal — fine vertical grain */}
        {v.faceplateTexture && (
          <div style={{
            position: 'absolute', inset: 0,
            backgroundImage: `repeating-linear-gradient(90deg,
              rgba(255,255,255,0.05) 0px, rgba(255,255,255,0.05) 1px,
              rgba(0,0,0,0.04) 1px, rgba(0,0,0,0.04) 2px,
              transparent 2px, transparent 3px)`,
            pointerEvents: 'none', opacity: 0.7,
          }}/>
        )}
        {variant === 'silver' && (
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(180deg, rgba(255,255,255,0.06) 0%, transparent 30%, transparent 70%, rgba(0,0,0,0.06) 100%)',
            pointerEvents: 'none',
          }}/>
        )}

        {/* wear */}
        {wear > 0 && (
          <div style={{
            position: 'absolute', inset: 0,
            background: `radial-gradient(ellipse at 15% 85%, rgba(80,60,30,${wear * 0.4}) 0%, transparent 30%),
              radial-gradient(ellipse at 85% 15%, rgba(80,60,30,${wear * 0.3}) 0%, transparent 25%)`,
            pointerEvents: 'none', mixBlendMode: 'multiply',
          }}/>
        )}

        {/* corner screws — 4 corners, like reference */}
        {showScrews && (
          <>
            <div style={{ position: 'absolute', left: 14, top: 14 }}><Screw angle={32} size={14} /></div>
            <div style={{ position: 'absolute', right: 14, top: 14 }}><Screw angle={128} size={14} /></div>
            <div style={{ position: 'absolute', left: 14, bottom: 14 }}><Screw angle={77} size={14} /></div>
            <div style={{ position: 'absolute', right: 14, bottom: 14 }}><Screw angle={15} size={14} /></div>
            {/* center screws below VU */}
            <div style={{ position: 'absolute', left: '32%', bottom: 14 }}><Screw angle={50} size={12} /></div>
            <div style={{ position: 'absolute', right: '32%', bottom: 14 }}><Screw angle={108} size={12} /></div>
          </>
        )}

        {/* ── LEFT BLOCK: brand stack ── */}
        <div style={{
          position: 'absolute',
          left: 50, top: 24,
          display: 'flex', gap: 28, alignItems: 'flex-start',
        }}>
          {showLogo && (
            <div style={{ display: 'flex', gap: 22, alignItems: 'center', paddingTop: 10 }}>
              <div style={{
                position: 'relative',
                paddingTop: 6,
                display: 'flex', flexDirection: 'column', alignItems: 'center',
                gap: 2,
              }}>
                <div style={{
                  fontFamily: "'Oswald','Bebas Neue','Arial Narrow',sans-serif",
                  fontSize: 34, fontWeight: 700,
                  letterSpacing: '0.14em',
                  lineHeight: 1,
                  textShadow: '0 1px 0 rgba(255,255,255,0.35)',
                }}>
                  <span style={{ color: v.inkAccent }}>MENTESH</span><span style={{ color: v.ink, opacity: 0.75 }}>2A</span>
                </div>
                <div style={{
                  fontFamily: "'Oswald','Bebas Neue',sans-serif",
                  fontSize: 10, fontWeight: 500,
                  letterSpacing: '0.38em',
                  color: v.ink, opacity: 0.75,
                  textAlign: 'center', textIndent: '0.38em',
                }}>MENTESH ENGINEERING</div>
              </div>
            </div>
          )}
        </div>

        {/* LEVELING AMPLIFIER — engraved line */}
        <div style={{
          position: 'absolute',
          left: '50%', bottom: 82,
          transform: 'translateX(-50%)',
          textAlign: 'center',
          pointerEvents: 'none',
        }}>
          <div style={{
            fontFamily: 'var(--font-engrave)',
            fontSize: 10, fontWeight: 600,
            letterSpacing: '0.34em',
            color: v.ink, opacity: 0.78,
            textIndent: '0.34em',
            whiteSpace: 'nowrap',
          }}>LEVELING AMPLIFIER · MODEL MENTESH2A</div>
        </div>

        {/* LIMIT/COMPRESS toggle + small calibrate switch — bottom-left corner cluster */}
        <div style={{
          position: 'absolute',
          left: 60, bottom: 38,
          display: 'flex', gap: 18, alignItems: 'center',
        }}>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
            <div style={{
              fontFamily: 'var(--font-engrave)', fontSize: 9,
              letterSpacing: '0.22em', color: v.ink, fontWeight: 600,
              marginBottom: 1,
            }}>LIMIT</div>
            <BatToggle value={mode} onChange={setMode} />
            <div style={{
              fontFamily: 'var(--font-engrave)', fontSize: 9,
              letterSpacing: '0.22em', color: v.ink, fontWeight: 600,
              marginTop: 1,
            }}>COMP</div>
          </div>
          {/* Phase invert toggle — crisp SVG ∅ at 13px (was 18, too big).
              Stays vector-rendered so it scales cleanly with WebView zoom. */}
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
            <svg width="13" height="13" viewBox="0 0 18 18" style={{ marginBottom: 1 }}>
              <circle cx="9" cy="9" r="6.5" fill="none" stroke={v.ink} strokeWidth="1.6" />
              <line x1="3.6" y1="14.4" x2="14.4" y2="3.6" stroke={v.ink} strokeWidth="1.6" strokeLinecap="round" />
            </svg>
            <BatToggle value={phaseInvert} onChange={setPhaseInvert} />
            <div style={{
              fontFamily: 'var(--font-engrave)', fontSize: 9,
              letterSpacing: '0.20em', color: v.ink, fontWeight: 600,
              opacity: 0.6,
            }}>PHASE</div>
          </div>
          {/* SCF moved to right side */}
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, marginBottom: 4 }}>
            <div style={{
              fontFamily: 'var(--font-engrave)', fontSize: 9,
              letterSpacing: '0.22em', color: v.ink, fontWeight: 600,
            }}>MIX</div>
            <MixFader value={mix} onChange={setMix} v={v} />
            <div style={{
              fontFamily: 'var(--font-mono)', fontSize: 8,
              color: v.ink, opacity: 0.7,
            }}>{Math.round(mix * 100)}%</div>
          </div>
        </div>

        {/* GAIN knob — own absolute position so the curved scale can sit nicely */}
        <div style={{
          position: 'absolute',
          left: 250, top: 90,
        }}>
          <ScaleKnob
            value={gain}
            onChange={setGain}
            defaultValue={40/110}                                    /* Gain=40 normalized to 0..110 range */
            label="GAIN"
            v={v}
            knobStyle={knobStyle}
            labels={[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 'MAX']}  /* matches PR knob — operator's UA reissue Gain knob also has MAX position */
            pivotFrac={5/11}                                          /* "50" at 12 o'clock = index 5 of 11 */
          />
        </div>

        {/* ── GAIN ↔ PR LINK button (∞) ─────────────────────────────────
            Positioned centered under the VU between the two knobs.
            When ON, dragging one knob moves the other by the same delta
            (preserves their initial offset).  Operator-requested 2026-05-03. */}
        {/* ∞ icon — engraved below the LED dot.  Text label removed
            per operator request 2026-05-03; only the dot + icon remain. */}
        <div style={{
          position: 'absolute',
          left: '50%', top: 285,
          transform: 'translateX(-50%)',
          zIndex: 5, pointerEvents: 'none',
          fontFamily: "'Helvetica','Arial',sans-serif",
          fontSize: 13, fontWeight: 700, color: v.ink,
          opacity: stereoLinked ? 0.95 : 0.5,
          lineHeight: 1,
          transition: 'opacity 120ms',
        }}>∞</div>

        {/* ── SATURATION INSERT — horizontal strip below the VU window ── */}
        {tweaks.showSaturation !== false && (
          <div style={{
            position: 'absolute',
            left: '50%', bottom: 8,
            transform: 'translateX(-50%)',
            zIndex: 3,
          }}>
            <SatStrip
              drive={satDrive}
              setDrive={setSatDrive}
              mode={satMode}
              setMode={setSatMode}
              bands={satBands}
              setBands={setSatBands}
              match={satMatch}
              setMatch={setSatMatch}
              v={v}
              width={420}
            />
          </div>
        )}

        {/* ── CENTER: framed VU meter (with UA-Classics-style oval above) ── */}
        <div style={{
          position: 'absolute',
          left: '50%', top: 56,
          transform: 'translateX(-50%)',
        }}>
          {/* Maker mark above the VU window — operator-locked 2026-05-03 v2:
              first bump (50x36 / 17pt) was too big.  Settled at middle:
              38x28 SVG, ME 13pt, CLASSICS 12pt — readable but not loud. */}
          <div style={{
            position: 'absolute',
            left: '50%', top: -36,
            transform: 'translateX(-50%)',
            display: 'flex', alignItems: 'center', gap: 7,
            whiteSpace: 'nowrap',
          }}>
            <svg width="38" height="28" viewBox="0 0 38 28">
              <ellipse cx="19" cy="14" rx="17" ry="11" fill="none" stroke={v.ink} strokeWidth="1.0" />
              <text x="19" y="18" textAnchor="middle"
                fontFamily="var(--font-brand)" fontSize="13" fontWeight="900"
                fontStyle="italic" fill={v.ink}>ME</text>
            </svg>
            <span style={{
              fontFamily: 'var(--font-engrave)',
              fontSize: 12, fontWeight: 700,
              letterSpacing: '0.20em',
              color: v.ink,
            }}>CLASSICS</span>
          </div>

          <VUFrame v={v}>
            <VUMeter value={meterValue} size={220} mode={meterMode} widthFactor={1.35}
                     needleColor={needleColor} />
          </VUFrame>
          {/* tube glow */}
          {power && tubeGlow > 0 && (
            <div style={{
              position: 'absolute',
              left: '50%', top: '70%',
              transform: 'translate(-50%, -50%)',
              width: 200, height: 60,
              borderRadius: '50%',
              background: `radial-gradient(ellipse, rgba(255, 140, 50, ${tubeGlow * 0.35}) 0%, transparent 60%)`,
              filter: 'blur(14px)',
              pointerEvents: 'none',
              animation: 'tubeFlicker 3.7s ease-in-out infinite',
            }}/>
          )}
          {/* STEREO LINK LED — clickable indicator.  Dim when independent;
              bright orange when L/R sidechains are coupled.  ∞ icon below
              this dot reinforces the link metaphor. */}
          <button
            onClick={() => { toggleStereoLink(); window.playKnobTick && window.playKnobTick(0.6); }}
            title={stereoLinked ? 'STEREO LINKED — click to unlink (independent L/R)' : 'Click to link L/R sidechains'}
            aria-label="Stereo Link"
            style={{
              position: 'absolute',
              left: '50%', bottom: -22,
              transform: 'translateX(-50%)',
              width: 8, height: 8, borderRadius: '50%',
              border: 'none', padding: 0, cursor: 'pointer',
              background: stereoLinked
                ? `radial-gradient(circle at 35% 30%, #ffd380 0%, ${v.inkAccent} 55%, #6a2410 100%)`
                : 'radial-gradient(circle, #000 0%, #1a1612 100%)',
              boxShadow: stereoLinked
                ? `0 0 6px ${v.inkAccent}cc, 0 0 12px ${v.inkAccent}66`
                : 'inset 0 1px 1px rgba(0,0,0,0.9)',
              transition: 'background 120ms, box-shadow 120ms',
            }}
          />
        </div>

        {/* ── RIGHT BLOCK: PEAK REDUCTION + GAIN REDUCTION switch + POWER ── */}
        {/* PEAK REDUCTION - mirror of GAIN, lower position */}
        <div style={{
          position: 'absolute',
          right: 240, top: 90,
        }}>
          <ScaleKnob
            value={peakRed}
            onChange={setPeakRed}
            defaultValue={16/44}                                                 /* PR=40 = step 16/44 (40/110 = 16/44 exactly) */
            label="PEAK REDUCTION"
            v={v}
            knobStyle={knobStyle}
            labels={[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 'MAX']}         /* 12 labels including MAX=110 */
            pivotFrac={5/11}                                                      /* "50" at 12 o'clock = index 5 of 11 */
          />
        </div>

        {/* Far-right cluster: METER MODE knob + POWER switch */}
        <div style={{
          position: 'absolute',
          right: 50, top: 28,
          display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
        }}>
          {/* Meter mode as a small rotary knob notching GAIN / PEAK REDUCTION */}
          <MeterModeKnob
            mode={meterMode}
            onChange={setMeterMode}
            v={v}
            knobStyle={knobStyle}
          />

          {/* POWER rocker toggle — regular switch */}
          <div style={{ marginTop: 22, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 }}>
            <div style={{
              fontFamily: 'var(--font-engrave)', fontSize: 9,
              letterSpacing: '0.22em', color: v.ink, fontWeight: 600,
            }}>ON</div>
            <BatToggle value={power} onChange={setPower} />
            <div style={{
              fontFamily: 'var(--font-engrave)', fontSize: 9,
              letterSpacing: '0.22em', color: v.ink, fontWeight: 600,
            }}>POWER</div>
          </div>

          {/* SCF — sidechain filter knob, right side */}
          <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3 }}>
            <MiniKnobSCF value={scFilter} onChange={setScFilter} v={v} />
          </div>

          {/* EMPH — R37 LIM/RESP HF emphasis (de-essing), tiny screw-trim style */}
          <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
            <ScrewTrimEmph value={emphasis} onChange={setEmphasis} v={v} />
          </div>

          {/* OUT TRIM — final output level adjust ±12 dB. Doesn't touch
              compression or saturation — pure post-everything trim for
              gain-staging into next plugin. */}
          <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3 }}>
            <MiniKnobOutTrim value={outTrim} onChange={setOutTrim} v={v} />
          </div>
        </div>

        {/* bottom vent strip */}
        <div style={{
          position: 'absolute',
          left: 40, right: 40, bottom: 4,
          height: 12,
          backgroundImage: `repeating-linear-gradient(90deg,
            rgba(0,0,0,0.55) 0px, rgba(0,0,0,0.55) 2px,
            transparent 2px, transparent 6px)`,
          opacity: 0.45,
          pointerEvents: 'none',
        }}/>
      </div>

      <RackEar side="right" variant={v} />
    </div>
  );
}

// ── MiniKnobSCF — tiny sidechain filter knob with Hz readout
function MiniKnobSCF({ value, onChange, v, defaultValue = 0 }) {
  const size = 44;
  const minA = -135, maxA = 135;
  const angle = minA + value * (maxA - minA);
  // Map 0..1 to frequency: 0..400Hz linear
  const hz = Math.round(value * 400);
  const hzLabel = `${hz}`;

  const onDown = (e) => {
    e.preventDefault();
    const startY = e.clientY, startVal = value;
    const steps = 40;
    let last = Math.round(startVal * steps);
    const onMove = (ev) => {
      const dy = startY - ev.clientY;
      const raw = Math.max(0, Math.min(1, startVal + dy / 300));
      const s = Math.round(raw * steps);
      if (s !== last) { last = s; }
      onChange(s / steps);
    };
    const onUp = () => { window.removeEventListener('pointermove', onMove); window.removeEventListener('pointerup', onUp); };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  };
  const onWheel = (e) => {
    e.preventDefault();
    const steps = 40;
    const dir = e.deltaY > 0 ? -1 : 1;
    onChange(Math.max(0, Math.min(1, Math.round((value + dir/steps) * steps) / steps)));
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', userSelect: 'none' }}>
      <div onPointerDown={onDown} onWheel={onWheel}
        onDoubleClick={() => onChange && onChange(defaultValue)}
        style={{ width: size, height: size, cursor: 'ns-resize', position: 'relative' }}>
        {/* small arc ticks */}
        <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}
          style={{ position: 'absolute', inset: 0, overflow: 'visible' }}>
          {[-135, -45, 45, 135].map((a, i) => {
            const rad = (a - 90) * Math.PI / 180;
            const r = size / 2 + 3;
            const cx = size / 2, cy = size / 2;
            return (
              <line key={i}
                x1={cx + Math.cos(rad) * (r - 4)} y1={cy + Math.sin(rad) * (r - 4)}
                x2={cx + Math.cos(rad) * r} y2={cy + Math.sin(rad) * r}
                stroke={v.ink} strokeWidth="0.8" opacity="0.55" />
            );
          })}
        </svg>
        <KnobBody size={size} angle={angle} style="bakelite" />
      </div>
      <div style={{
        fontFamily: 'var(--font-engrave)', fontSize: 9, fontWeight: 700,
        letterSpacing: '0.22em', color: v.ink, marginTop: 2,
      }}>SCF</div>
      <div style={{
        fontFamily: 'var(--font-mono)', fontSize: 7,
        color: v.ink, opacity: 0.65,
      }}>{value < 0.01 ? 'OFF' : `${hzLabel}Hz`}</div>
    </div>
  );
}

// ── MiniKnobOutTrim — ±12 dB final output trim, post-saturation
function MiniKnobOutTrim({ value, onChange, v, defaultValue = 0.5 }) {
  const size = 44;
  const minA = -135, maxA = 135;
  const angle = minA + value * (maxA - minA);
  // 0..1 maps to -12..+12 dB linearly; 0.5 = 0 dB unity
  const dB = ((value - 0.5) * 24).toFixed(1);
  const dBLabel = value === 0.5 ? '0' : (dB > 0 ? `+${dB}` : `${dB}`);

  const onDown = (e) => {
    e.preventDefault();
    const startY = e.clientY, startVal = value;
    const steps = 48; // 0.5 dB per step
    let last = Math.round(startVal * steps);
    const onMove = (ev) => {
      const dy = startY - ev.clientY;
      const raw = Math.max(0, Math.min(1, startVal + dy / 300));
      const s = Math.round(raw * steps);
      if (s !== last) { last = s; }
      onChange(s / steps);
    };
    const onUp = () => { window.removeEventListener('pointermove', onMove); window.removeEventListener('pointerup', onUp); };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  };
  const onWheel = (e) => {
    e.preventDefault();
    const steps = 48;
    const dir = e.deltaY > 0 ? -1 : 1;
    onChange(Math.max(0, Math.min(1, Math.round((value + dir/steps) * steps) / steps)));
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', userSelect: 'none' }}>
      <div onPointerDown={onDown} onWheel={onWheel}
        onDoubleClick={() => onChange && onChange(defaultValue)}
        style={{ width: size, height: size, cursor: 'ns-resize', position: 'relative' }}
        title={`Output Trim: ${dBLabel} dB (double-click to reset)`}>
        <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}
          style={{ position: 'absolute', inset: 0, overflow: 'visible' }}>
          {/* tick marks at -135, -45, 0 (top), 45, 135 */}
          {[-135, -45, 0, 45, 135].map((a, i) => {
            const rad = (a - 90) * Math.PI / 180;
            const r = size / 2 + 3;
            const cx = size / 2, cy = size / 2;
            const isCenter = a === 0;
            return (
              <line key={i}
                x1={cx + Math.cos(rad) * (r - (isCenter ? 5 : 4))}
                y1={cy + Math.sin(rad) * (r - (isCenter ? 5 : 4))}
                x2={cx + Math.cos(rad) * r} y2={cy + Math.sin(rad) * r}
                stroke={v.ink} strokeWidth={isCenter ? '1.0' : '0.8'}
                opacity={isCenter ? '0.85' : '0.55'} />
            );
          })}
        </svg>
        <KnobBody size={size} angle={angle} style="bakelite" />
      </div>
      <div style={{
        fontFamily: 'var(--font-engrave)', fontSize: 9, fontWeight: 700,
        letterSpacing: '0.22em', color: v.ink, marginTop: 2,
      }}>TRIM</div>
      <div style={{
        fontFamily: 'var(--font-mono)', fontSize: 7,
        color: v.ink, opacity: 0.65,
      }}>{dBLabel}dB</div>
    </div>
  );
}

// ── ScrewTrimEmph — R37 LIM/RESP HF emphasis. Models the rear-panel trim
// pot on real LA-2A (screwdriver-adjustable). Small slot-head, drag to
// rotate, tiny "EMPH" label underneath. 0 = factory flat (default).
function ScrewTrimEmph({ value, onChange, v, defaultValue = 0 }) {
  const size = 16;
  const minA = -135, maxA = 135;
  const angle = minA + value * (maxA - minA);
  const dB = (value * 12).toFixed(1);

  const onDown = (e) => {
    e.preventDefault();
    const startY = e.clientY, startVal = value;
    const steps = 40;
    let last = Math.round(startVal * steps);
    const onMove = (ev) => {
      const dy = startY - ev.clientY;
      const raw = Math.max(0, Math.min(1, startVal + dy / 300));
      const s = Math.round(raw * steps);
      if (s !== last) { last = s; }
      onChange(s / steps);
    };
    const onUp = () => { window.removeEventListener('pointermove', onMove); window.removeEventListener('pointerup', onUp); };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  };
  const onWheel = (e) => {
    e.preventDefault();
    const steps = 40;
    const dir = e.deltaY > 0 ? -1 : 1;
    onChange(Math.max(0, Math.min(1, Math.round((value + dir/steps) * steps) / steps)));
  };

  const cx = size / 2, cy = size / 2;
  const slotLen = size * 0.7;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', userSelect: 'none' }}
         title={`Emphasis (R37): ${value < 0.01 ? 'FLAT' : '+' + dB + ' dB'}`}>
      <div onPointerDown={onDown} onWheel={onWheel}
        onDoubleClick={() => onChange && onChange(defaultValue)}
        style={{ width: size, height: size, cursor: 'ns-resize', position: 'relative' }}>
        <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}
          style={{ position: 'absolute', inset: 0 }}>
          {/* recessed screw head — radial gradient circle */}
          <defs>
            <radialGradient id="screwGrad" cx="35%" cy="35%" r="65%">
              <stop offset="0%" stopColor="#9a9080" />
              <stop offset="60%" stopColor="#6b6358" />
              <stop offset="100%" stopColor="#3a3530" />
            </radialGradient>
          </defs>
          <circle cx={cx} cy={cy} r={size / 2 - 0.5}
                  fill="url(#screwGrad)"
                  stroke="rgba(0,0,0,0.55)" strokeWidth="0.4" />
          {/* rotating slot */}
          <g transform={`rotate(${angle} ${cx} ${cy})`}>
            <line
              x1={cx - slotLen / 2} y1={cy}
              x2={cx + slotLen / 2} y2={cy}
              stroke="#1a1612" strokeWidth="1.2" strokeLinecap="round" />
            <line
              x1={cx - slotLen / 2} y1={cy - 0.5}
              x2={cx + slotLen / 2} y2={cy - 0.5}
              stroke="rgba(255,255,255,0.18)" strokeWidth="0.5" strokeLinecap="round" />
          </g>
        </svg>
      </div>
      <div style={{
        fontFamily: 'var(--font-engrave)', fontSize: 6, fontWeight: 700,
        letterSpacing: '0.18em', color: v.ink, marginTop: 1, opacity: 0.7,
      }}>EMPH</div>
    </div>
  );
}

// ── MixFader — small vertical fader for MIX control
function MixFader({ value, onChange, v, defaultValue = 1.0 }) {
  const H = 70;
  const onDown = (e) => {
    e.preventDefault();
    const startY = e.clientY, startVal = value;
    const onMove = (ev) => {
      const dy = startY - ev.clientY;
      const next = Math.max(0, Math.min(1, startVal + dy / H));
      onChange(Math.round(next * 40) / 40);
      window.playKnobTick && window.playKnobTick(0.4);
    };
    const onUp = () => { window.removeEventListener('pointermove', onMove); window.removeEventListener('pointerup', onUp); };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  };
  const onWheel = (e) => {
    e.preventDefault();
    const dir = e.deltaY > 0 ? -1 : 1;
    onChange(Math.max(0, Math.min(1, Math.round((value + dir / 40) * 40) / 40)));
  };
  return (
    <div onPointerDown={onDown} onWheel={onWheel}
      onDoubleClick={() => onChange && onChange(defaultValue)}
      style={{ position: 'relative', width: 20, height: H, cursor: 'ns-resize' }}>
      <div style={{
        position: 'absolute', left: '50%', top: 4, bottom: 4,
        transform: 'translateX(-50%)', width: 3,
        background: 'linear-gradient(180deg, #000 0%, #2a2620 100%)',
        borderRadius: 2, boxShadow: 'inset 0 0 2px rgba(0,0,0,0.9)',
      }}/>
      {[0, 0.25, 0.5, 0.75, 1].map((t, i) => (
        <div key={i} style={{
          position: 'absolute', left: i === 2 ? 0 : 3, right: i === 2 ? 0 : 3,
          top: `${(1 - t) * (H - 8) + 4}px`, height: 1,
          background: i === 2 ? 'rgba(214,160,85,0.55)' : 'rgba(255,255,255,0.18)',
        }}/>
      ))}
      <div style={{
        position: 'absolute', left: -5, right: -5,
        top: `${(1 - value) * (H - 14) + 2}px`, height: 12,
        background: 'linear-gradient(180deg, #d8d2c5 0%, #8a8478 45%, #3a3530 100%)',
        borderRadius: 2,
        boxShadow: '0 2px 4px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.4)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <div style={{ width: '60%', height: 2, background: '#d6a055', boxShadow: '0 0 3px #d6a055' }}/>
      </div>
    </div>
  );
}

// ── MeterModeKnob: 3-position rotary with GR / +4 / +10 labels
// Notches the chrome knob body of ScaleKnob at small scale.
function MeterModeKnob({ mode, onChange, v, knobStyle }) {
  const positions = ['GR', '+4', '+10'];
  const idx = Math.max(0, positions.indexOf(mode));
  const size = 58;
  // 3 positions at -120°, 0°, +120°
  const posAngles = [-120, 0, 120];
  const angle = posAngles[idx];

  const step = (dir) => onChange(positions[(idx + dir + positions.length) % positions.length]);
  const onPointerDown = (e) => {
    e.preventDefault();
    const startY = e.clientY;
    let lastIdx = idx;
    const onMove = (ev) => {
      const steps = Math.round((startY - ev.clientY) / 28);
      const next = Math.max(0, Math.min(2, idx + steps));
      if (next !== lastIdx) { onChange(positions[next]); lastIdx = next; }
    };
    const onUp = () => { window.removeEventListener('pointermove', onMove); window.removeEventListener('pointerup', onUp); };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  };

  // Draw labels + ticks on SVG arc around the knob
  const W = 120, H = 110;
  const cx = W / 2, cy = H / 2;
  const labelR = 48; // radius to label centers
  const tickR = 36;  // radius to tick outer edge
  const tickInnerR = 30;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <div style={{ position: 'relative', width: W, height: H }}>
        {/* SVG arc labels */}
        <svg width={W} height={H} style={{ position: 'absolute', inset: 0, overflow: 'visible' }}>
          {posAngles.map((a, i) => {
            const rad = (a - 90) * Math.PI / 180;
            const lx = cx + Math.cos(rad) * labelR;
            const ly = cy + Math.sin(rad) * labelR;
            const tx1 = cx + Math.cos(rad) * tickInnerR;
            const ty1 = cy + Math.sin(rad) * tickInnerR;
            const tx2 = cx + Math.cos(rad) * tickR;
            const ty2 = cy + Math.sin(rad) * tickR;
            const isActive = i === idx;
            return (
              <g key={i}>
                <line x1={tx1} y1={ty1} x2={tx2} y2={ty2}
                  stroke={v.ink} strokeWidth={isActive ? 1.8 : 1} opacity={isActive ? 1 : 0.5} />
                <text x={lx} y={ly + 3.5} textAnchor="middle"
                  fontSize="10" fontFamily="var(--font-engrave)"
                  fontWeight={isActive ? '700' : '500'}
                  fill={v.ink} opacity={isActive ? 1 : 0.6}>{positions[i]}</text>
              </g>
            );
          })}
        </svg>

        {/* Knob body centered */}
        <div
          onPointerDown={onPointerDown}
          onWheel={(e) => { e.preventDefault(); step(e.deltaY > 0 ? 1 : -1); }}
          onClick={() => step(1)}
          style={{
            position: 'absolute', left: '50%', top: '50%',
            transform: 'translate(-50%, -50%)',
            width: size, height: size, borderRadius: '50%',
            background: 'radial-gradient(circle at 32% 30%, #2a2a2a 0%, #1a1a1a 45%, #0a0a0a 100%)',
            boxShadow: '0 2px 4px rgba(0,0,0,0.6), inset 0 1px 1px rgba(255,255,255,0.25), inset 0 -2px 3px rgba(0,0,0,0.6)',
            cursor: 'ns-resize', userSelect: 'none',
          }}>
          <div style={{
            position: 'absolute', inset: 4, borderRadius: '50%',
            background: 'linear-gradient(145deg, #4a4a4a 0%, #1a1a1a 100%)',
            boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.2)',
          }}/>
          <div style={{
            position: 'absolute', left: '50%', top: '50%',
            width: 3, height: size / 2 - 6,
            transformOrigin: '50% 100%',
            transform: `translate(-50%, -100%) rotate(${angle}deg)`,
            background: 'linear-gradient(180deg, #e8dcb8 0%, #b89a60 100%)',
            borderRadius: 1, pointerEvents: 'none',
          }}/>
        </div>
      </div>
      <div style={{
        fontFamily: 'var(--font-engrave)', fontSize: 10, fontWeight: 700,
        letterSpacing: '0.22em', color: v.ink, marginTop: 2,
      }}>METER</div>
    </div>
  );
}

// ── Curved scale around a knob: ring with 0-100 numbers like the LA-2A
// Calibrated knob arc, locked 2026-05-03:
//   value 0           → -135° (7:30, lower-left)
//   value pivotFrac   →    0° (12:00, straight up)        — "50" label
//   value 1.0         → +180° (6:00, straight down, MAX)
// Two linear segments hinged at the pivot — operator-locked LA-2A convention.
//
// Per-knob calibration via pivotFrac:
//   PR knob   (range 0..110, 12 labels 0..MAX): pivotFrac = 5/11 ≈ 0.4545
//                                               (50 sits at index 5 of 11)
//   Gain knob (range 0..100, 11 labels 0..100): pivotFrac = 5/10 = 0.50
//                                               (50 sits at index 5 of 10)
// Without per-knob pivot, Gain default 0.40 (= Gain 40) landed on PR's
// dial calibration, drawing the indicator between the "40" and "50" marks
// instead of on "40".  Reported 2026-05-03 ("Gain knob still not on 40").
const KNOB_START_DEG = -135;       // 7:30
const KNOB_PIVOT_DEG = 0;          // 12:00
const KNOB_END_DEG   = 180;        // 6:00
function knobAngle(v01, pivotFrac) {
  if (v01 <= pivotFrac)
    return KNOB_START_DEG + (v01 / pivotFrac) * (KNOB_PIVOT_DEG - KNOB_START_DEG);
  return KNOB_PIVOT_DEG + ((v01 - pivotFrac) / (1 - pivotFrac)) * (KNOB_END_DEG - KNOB_PIVOT_DEG);
}
function labelAngle(idx, totalSegments, pivotFrac) {
  return knobAngle(idx / totalSegments, pivotFrac);
}

function ScaleKnob({ value, onChange, label, v, knobStyle, defaultValue, labels, pivotFrac }) {
  const size = 110;
  const ringR = 78; // distance from center to numbers
  // labels prop drives both the printed numbers AND the calibration —
  // 12 entries [0..MAX] for PR (range 0-110), 11 entries [0..100] for
  // Gain (range 0-100).  pivotFrac places the "50" label at 12 o'clock.
  const totalSegments = labels.length - 1;
  const stepsPerSegment = 4;             // operator-locked: 4 substeps per 10-mark
  const steps = totalSegments * stepsPerSegment;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', position: 'relative' }}>
      <div style={{ position: 'relative', width: ringR * 2 + 24, height: ringR * 2 + 18 }}>
        {/* number arc */}
        <svg
          width={ringR * 2 + 24} height={ringR * 2 + 18}
          style={{ position: 'absolute', inset: 0, overflow: 'visible' }}
        >
          {labels.map((n, i) => {
            const a = labelAngle(i, totalSegments, pivotFrac);
            const rad = (a - 90) * Math.PI / 180;
            const cx = (ringR * 2 + 24) / 2 + Math.cos(rad) * ringR;
            const cy = (ringR * 2 + 18) / 2 + Math.sin(rad) * ringR;
            return (
              <text key={n}
                x={cx} y={cy + 3.5}
                textAnchor="middle"
                fontSize="10"
                fontFamily="var(--font-engrave)"
                fontWeight="700"
                fill={v.ink}
                opacity="0.95"
              >{n}</text>
            );
          })}
          {/* small tick marks just inside numbers — shortened so they don't touch labels */}
          {labels.map((n, i) => {
            const a = labelAngle(i, totalSegments, pivotFrac);
            const rad = (a - 90) * Math.PI / 180;
            const cx0 = (ringR * 2 + 24) / 2;
            const cy0 = (ringR * 2 + 18) / 2;
            const r1 = ringR - 18, r2 = ringR - 11; // stop 11px before edge — gap before label
            return (
              <line key={`tk${n}`}
                x1={cx0 + Math.cos(rad) * r1}
                y1={cy0 + Math.sin(rad) * r1}
                x2={cx0 + Math.cos(rad) * r2}
                y2={cy0 + Math.sin(rad) * r2}
                stroke={v.ink} strokeWidth="1.2" opacity="0.7"
              />
            );
          })}
          {/* Minor substep ticks — 3 per segment between labels (giving the
              4-step-per-10-mark feel: |·|·|·|·|·).  Operator-locked
              2026-05-03. */}
          {Array.from({ length: totalSegments * stepsPerSegment }).map((_, stepIdx) => {
            // Skip steps that coincide with a major label (every Nth step)
            if (stepIdx % stepsPerSegment === 0) return null;
            const a = labelAngle(stepIdx / stepsPerSegment, totalSegments, pivotFrac);
            const rad = (a - 90) * Math.PI / 180;
            const cx0 = (ringR * 2 + 24) / 2;
            const cy0 = (ringR * 2 + 18) / 2;
            const r1 = ringR - 15, r2 = ringR - 11;
            return (
              <line key={`mt${stepIdx}`}
                x1={cx0 + Math.cos(rad) * r1}
                y1={cy0 + Math.sin(rad) * r1}
                x2={cx0 + Math.cos(rad) * r2}
                y2={cy0 + Math.sin(rad) * r2}
                stroke={v.ink} strokeWidth="0.9" opacity="0.45"
              />
            );
          })}
        </svg>
        {/* knob in center, no built-in ticks */}
        <div style={{
          position: 'absolute',
          left: '50%', top: '50%',
          transform: 'translate(-50%, -50%)',
        }}>
          <BareKnob
            value={value}
            onChange={onChange}
            defaultValue={defaultValue}
            steps={steps}
            pivotFrac={pivotFrac}
            size={size}
            style={knobStyle}
          />
        </div>
      </div>
      <div style={{
        fontFamily: 'var(--font-engrave)',
        fontSize: 11, fontWeight: 700,
        letterSpacing: '0.22em', color: v.ink,
        marginTop: 2,
      }}>{label}</div>
    </div>
  );
}

// Knob without surrounding tick marks (scale lives outside on the faceplate ring)
function BareKnob({ value, onChange, size, style, defaultValue,
                    steps = 44, pivotFrac = 5 / 11 }) {
  const angle = knobAngle(value, pivotFrac);
  // Manual double-click detection (WKWebView's native dblclick fires
  // unreliably when sandwiched between drag pointerdown/move handlers).
  // 350ms window — comfortable double-click cadence without false positives
  // from rapid trim-tweaks.
  const lastClickRef = React.useRef(0);
  const onPointerDown = (e) => {
    e.preventDefault();
    const now = Date.now();
    if (defaultValue != null && (now - lastClickRef.current) < 350) {
      onChange && onChange(defaultValue);
      lastClickRef.current = 0;
      return;
    }
    lastClickRef.current = now;
    const startY = e.clientY;
    const startVal = value;
    let lastStep = Math.round(startVal * steps);
    const onMove = (ev) => {
      const dy = startY - ev.clientY;
      const raw = Math.max(0, Math.min(1, startVal + dy / 400));
      const stepIdx = Math.round(raw * steps);
      if (stepIdx !== lastStep) {
        lastStep = stepIdx;
        if (window.playKnobTick) window.playKnobTick();
      }
      onChange && onChange(stepIdx / steps);
    };
    const onUp = () => {
      window.removeEventListener('pointermove', onMove);
      window.removeEventListener('pointerup', onUp);
    };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  };
  const onWheel = (e) => {
    e.preventDefault();
    const dir = e.deltaY > 0 ? -1 : 1;
    const cur = Math.round(value * steps);
    const next = Math.max(0, Math.min(steps, cur + dir));
    if (next !== cur && window.playKnobTick) window.playKnobTick(0.6);
    onChange && onChange(next / steps);
  };
  return (
    <div onPointerDown={onPointerDown} onWheel={onWheel}
      onDoubleClick={defaultValue != null ? () => onChange && onChange(defaultValue) : undefined}
      style={{ width: size, height: size, cursor: 'ns-resize' }}>
      <KnobBody size={size} angle={angle} style={style} />
    </div>
  );
}

// Bat-style miniature toggle (single small bat sticking up/down)
function BatToggle({ value, onChange }) {
  // Authentic LA-2A style: chrome hex nut bezel + tall chrome bat lever
  // Use a unique id suffix so gradient defs don't collide across instances
  const uid = React.useId ? React.useId().replace(/:/g, '') : Math.random().toString(36).slice(2, 7);
  const click = () => {
    onChange(!value);
    if (window.playKnobTick) window.playKnobTick(0.85);
  };
  const W = 40, H = 62;
  return (
    <div onClick={click} style={{
      width: W, height: H, position: 'relative', cursor: 'pointer',
    }}>
      {/* hex chrome nut bezel — sits slightly raised, with faceted shading */}
      <svg viewBox="0 0 40 40" width={W} height={40} style={{
        position: 'absolute', left: 0, top: H / 2 - 20,
        filter: 'drop-shadow(0 2px 3px rgba(0,0,0,0.55))',
      }}>
        <defs>
          <radialGradient id={`nutBody-${uid}`} cx="38%" cy="32%" r="72%">
            <stop offset="0%" stopColor="#f8f4ea" />
            <stop offset="28%" stopColor="#d8d2c4" />
            <stop offset="55%" stopColor="#9a948a" />
            <stop offset="85%" stopColor="#4a443e" />
            <stop offset="100%" stopColor="#15110d" />
          </radialGradient>
          <linearGradient id={`nutRim-${uid}`} x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor="rgba(255,255,255,0.5)" />
            <stop offset="100%" stopColor="rgba(0,0,0,0.4)" />
          </linearGradient>
          <radialGradient id={`throat-${uid}`} cx="50%" cy="50%" r="65%">
            <stop offset="0%" stopColor="#050403" />
            <stop offset="65%" stopColor="#0c0a08" />
            <stop offset="100%" stopColor="#2a2420" />
          </radialGradient>
        </defs>
        {/* hex outer body */}
        <polygon points="20,2 36,10 36,30 20,38 4,30 4,10"
          fill={`url(#nutBody-${uid})`}
          stroke="#050403" strokeWidth="0.7" />
        {/* facet seams — subtle */}
        <g stroke="rgba(0,0,0,0.35)" strokeWidth="0.4" fill="none">
          <line x1="20" y1="2" x2="20" y2="38" opacity="0.25" />
          <line x1="4" y1="10" x2="36" y2="30" opacity="0.15" />
          <line x1="36" y1="10" x2="4" y2="30" opacity="0.15" />
        </g>
        {/* inner rim */}
        <circle cx="20" cy="20" r="10"
          fill="none" stroke={`url(#nutRim-${uid})`} strokeWidth="1.2" />
        {/* throat */}
        <circle cx="20" cy="20" r="9"
          fill={`url(#throat-${uid})`} />
        {/* thread ring inside throat */}
        <circle cx="20" cy="20" r="7.2"
          fill="none" stroke="#3a3530" strokeWidth="0.5" opacity="0.6" />
        <circle cx="20" cy="20" r="5.6"
          fill="none" stroke="#2a2620" strokeWidth="0.35" opacity="0.5" />
        {/* top chrome highlight on nut */}
        <ellipse cx="15" cy="9" rx="7" ry="2.6" fill="rgba(255,255,255,0.42)" />
        <ellipse cx="25" cy="33" rx="5" ry="1.6" fill="rgba(0,0,0,0.35)" />
      </svg>

      {/* tall chrome bat lever — points up if value=true (LIMIT/ON), down otherwise */}
      <svg viewBox={`0 0 ${W} ${H}`} width={W} height={H} style={{
        position: 'absolute', inset: 0,
      }}>
        <defs>
          <linearGradient id={`batShaft-${uid}`} x1="0" x2="1" y1="0" y2="0">
            <stop offset="0%" stopColor="#3a3530" />
            <stop offset="20%" stopColor="#b8b2a4" />
            <stop offset="45%" stopColor="#f4efe2" />
            <stop offset="58%" stopColor="#fbf8ee" />
            <stop offset="72%" stopColor="#a8a294" />
            <stop offset="100%" stopColor="#1a1612" />
          </linearGradient>
          <radialGradient id={`batTip-${uid}`} cx="38%" cy="32%" r="62%">
            <stop offset="0%" stopColor="#fefcf4" />
            <stop offset="35%" stopColor="#d8d2c4" />
            <stop offset="70%" stopColor="#6a645a" />
            <stop offset="100%" stopColor="#15110d" />
          </radialGradient>
          <filter id={`batShadow-${uid}`} x="-60%" y="-20%" width="220%" height="140%">
            <feGaussianBlur stdDeviation="0.9" />
          </filter>
        </defs>
        {/* soft drop shadow cast by the lever onto the bezel */}
        <g transform={value ? '' : `rotate(180 ${W/2} ${H/2})`}>
          <ellipse cx={W/2 + 1.5} cy={H/2 - 6} rx="6" ry="1.8"
            fill="rgba(0,0,0,0.35)" filter={`url(#batShadow-${uid})`} />
        </g>
        {/* lever — rotates 180° around center when off */}
        <g transform={value ? '' : `rotate(180 ${W/2} ${H/2})`}>
          {/* base ring (visible collar at exit of throat) */}
          <ellipse cx={W/2} cy={H/2} rx="4.5" ry="1.6"
            fill="#1a1612" stroke="#050403" strokeWidth="0.3" />
          {/* shaft */}
          <rect x={W/2 - 3.2} y={H/2 - 28} width="6.4" height="28"
            fill={`url(#batShaft-${uid})`} rx="1.8" />
          {/* shaft ferrule — flare at base */}
          <path
            d={`M ${W/2 - 4.2} ${H/2 - 2} L ${W/2 - 3.2} ${H/2 - 5} L ${W/2 + 3.2} ${H/2 - 5} L ${W/2 + 4.2} ${H/2 - 2} Z`}
            fill="#3a3530" stroke="#050403" strokeWidth="0.3"
          />
          {/* tip ball */}
          <circle cx={W/2} cy={H/2 - 30} r="4.8"
            fill={`url(#batTip-${uid})`}
            stroke="#050403" strokeWidth="0.4" />
          {/* tip specular */}
          <ellipse cx={W/2 - 1.6} cy={H/2 - 31.4} rx="2" ry="1" fill="rgba(255,255,255,0.75)" />
          <ellipse cx={W/2 + 1.4} cy={H/2 - 28} rx="1.3" ry="0.5" fill="rgba(0,0,0,0.35)" />
        </g>
      </svg>
    </div>
  );
}

// Chicken-head selector with N positions (used for GAIN REDUCTION OUTPUT +10 / +4)
function ChickenHeadSelector({ value, onChange, positions = 2 }) {
  const size = 50;
  const minA = -45, maxA = 45;
  const angle = positions <= 1 ? 0 : minA + (value / (positions - 1)) * (maxA - minA);
  const click = () => {
    onChange((value + 1) % positions);
    if (window.playKnobTick) window.playKnobTick(0.7);
  };
  return (
    <div onClick={click} style={{ cursor: 'pointer', width: size + 12, height: size + 12, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <svg width={size} height={size} viewBox="0 0 100 100">
        <defs>
          <radialGradient id="chrSel" cx="35%" cy="28%" r="75%">
            <stop offset="0%" stopColor="#3a3530" />
            <stop offset="50%" stopColor="#1a1612" />
            <stop offset="100%" stopColor="#050403" />
          </radialGradient>
        </defs>
        <ellipse cx="50" cy="54" rx="46" ry="44" fill="rgba(0,0,0,0.5)" />
        <circle cx="50" cy="50" r="44" fill="url(#chrSel)" />
        {/* knurl */}
        <g opacity="0.25">
          {Array.from({ length: 48 }).map((_, i) => {
            const a = (i / 48) * 360;
            const rad = (a - 90) * Math.PI / 180;
            return <line key={i}
              x1={50 + Math.cos(rad) * 40} y1={50 + Math.sin(rad) * 40}
              x2={50 + Math.cos(rad) * 44} y2={50 + Math.sin(rad) * 44}
              stroke="#000" strokeWidth="0.5" />;
          })}
        </g>
        <g transform={`rotate(${angle} 50 50)`}>
          {/* chicken-head pointer */}
          <path d="M50 18 L58 50 L50 56 L42 50 Z"
            fill="#1a1410" stroke="#000" strokeWidth="0.5" />
          <circle cx="50" cy="50" r="4" fill="#0a0806" />
        </g>
      </svg>
    </div>
  );
}

function MENTESH2A_NotImported() {} // placeholder to keep order tidy

// ── VU meter framed window — UA Classics 2014 style
// Chromed silver bezel, clean brushed metal surround, small screws in each corner,
// and subtle curved "glass" reflection across the face.
function VUFrame({ v, children }) {
  return (
    <div style={{
      padding: '8px 12px',
      background: `
        linear-gradient(180deg, rgba(255,255,255,0.18) 0%, transparent 18%, transparent 82%, rgba(0,0,0,0.22) 100%),
        repeating-linear-gradient(90deg,
          #d0ccc2 0px, #d0ccc2 1px,
          #b8b4a8 1px, #b8b4a8 2px,
          #c4c0b4 2px, #c4c0b4 3px),
        linear-gradient(180deg, #cac6ba 0%, #a09c90 50%, #86827a 100%)
      `,
      borderRadius: 3,
      boxShadow: `
        inset 0 1px 0 rgba(255,255,255,0.5),
        inset 0 -1px 0 rgba(0,0,0,0.35),
        inset 0 0 0 1px rgba(0,0,0,0.15),
        0 2px 4px rgba(0,0,0,0.4)
      `,
      border: '1px solid #3a352e',
      position: 'relative',
    }}>
      {/* inner chrome lip */}
      <div style={{
        position: 'absolute', inset: 4,
        borderRadius: 2,
        boxShadow: `
          inset 0 0 0 1px rgba(0,0,0,0.5),
          inset 0 1px 1px rgba(255,255,255,0.35)
        `,
        pointerEvents: 'none',
        zIndex: 1,
      }}/>
      {/* corner screws */}
      <div style={{ position: 'absolute', left: 3, top: 3, zIndex: 3 }}><Screw angle={22} size={7} /></div>
      <div style={{ position: 'absolute', right: 3, top: 3, zIndex: 3 }}><Screw angle={88} size={7} /></div>
      <div style={{ position: 'absolute', left: 3, bottom: 3, zIndex: 3 }}><Screw angle={134} size={7} /></div>
      <div style={{ position: 'absolute', right: 3, bottom: 3, zIndex: 3 }}><Screw angle={56} size={7} /></div>

      <div style={{ position: 'relative', borderRadius: 2, overflow: 'hidden' }}>
        {/* glass curvature reflection */}
        <div style={{
          position: 'absolute', inset: 0,
          background: `
            radial-gradient(ellipse 140% 60% at 30% -10%, rgba(255,255,255,0.28) 0%, transparent 55%),
            linear-gradient(180deg, rgba(255,255,255,0.10) 0%, transparent 30%, transparent 70%, rgba(0,0,0,0.08) 100%)
          `,
          pointerEvents: 'none',
          zIndex: 2,
          mixBlendMode: 'screen',
        }}/>
        {/* brand script removed — the meter face already carries VU + GAIN REDUCTION mode text */}
        {children}
      </div>
    </div>
  );
}

// ── Saturation insert strip: horizontal Drive fader + 3 EQ band buttons + Lift/Notch toggle
function SatStrip({ drive, setDrive, mode, setMode, bands, setBands, match, setMatch, v, width = 560 }) {
  const accent = mode === 'lift' ? '#d68a3a' : '#3a78c8';
  const faderW = width - 260; // reserve space for band cluster
  const onFaderDown = (e) => {
    e.preventDefault();
    // Jump-to-click: figure out current track bounds and snap drive to where
    // the user clicked (every DAW fader works this way). Then continue
    // dragging relative to that point. Much easier to hit than the old
    // relative-only mode where you had to land exactly on the cap.
    const trackEl = e.currentTarget;
    const rect = trackEl.getBoundingClientRect();
    const padding = 9; // half cap width — keeps cap centered on click point
    const usableW = rect.width - 2 * padding;
    const xToVal = (clientX) => {
      const x = clientX - rect.left - padding;
      return Math.max(0, Math.min(1, x / usableW));
    };
    const steps = 40;
    let lastStep = Math.round(xToVal(e.clientX) * steps);
    setDrive(lastStep / steps);
    if (window.playKnobTick) window.playKnobTick(0.5);
    const onMove = (ev) => {
      const raw = xToVal(ev.clientX);
      const stepIdx = Math.round(raw * steps);
      if (stepIdx !== lastStep) {
        lastStep = stepIdx;
        if (window.playKnobTick) window.playKnobTick(0.5);
      }
      setDrive(stepIdx / steps);
    };
    const onUp = () => {
      window.removeEventListener('pointermove', onMove);
      window.removeEventListener('pointerup', onUp);
    };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
  };
  const onFaderWheel = (e) => {
    e.preventDefault();
    const steps = 40;
    const dir = e.deltaY > 0 ? -1 : 1;
    const cur = Math.round(drive * steps);
    const next = Math.max(0, Math.min(steps, cur + dir));
    if (next !== cur && window.playKnobTick) window.playKnobTick(0.5);
    setDrive(next / steps);
  };
  const toggleBand = (k) => {
    setBands({ ...bands, [k]: !bands[k] });
    if (window.playKnobTick) window.playKnobTick(0.7);
  };

  const driveDb = Math.round(drive * 100);
  return (
    <div style={{
      width,
      display: 'flex', flexDirection: 'column', alignItems: 'stretch',
      gap: 4,
      padding: '6px 10px',
      background: 'linear-gradient(180deg, rgba(255,255,255,0.04) 0%, rgba(0,0,0,0.08) 100%)',
      border: `0.5px solid rgba(0,0,0,0.2)`,
      borderRadius: 3,
    }}>
      {/* Header row: label + drive readout */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
        <div style={{
          fontFamily: 'var(--font-engrave)', fontSize: 9,
          letterSpacing: '0.3em', color: v.ink, fontWeight: 700,
        }}>SATURATION</div>
        <div style={{
          fontFamily: 'var(--font-mono)', fontSize: 10,
          color: v.ink, opacity: 0.85, fontWeight: 700,
        }}>{driveDb}</div>
      </div>

      {/* Body row: horizontal fader + band cluster */}
      <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
        {/* Horizontal fader */}
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
          <div style={{
            position: 'relative', height: 32,
            background: 'linear-gradient(180deg, #1a1612 0%, #0a0806 50%, #1a1612 100%)',
            borderRadius: 3,
            boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.9), 0 1px 0 rgba(255,255,255,0.15)',
            cursor: 'ew-resize',
            touchAction: 'none',
          }} onPointerDown={onFaderDown} onWheel={onFaderWheel}
             onDoubleClick={() => setDrive && setDrive(0)}>
            {/* track slot */}
            <div style={{
              position: 'absolute',
              top: '50%', left: 4, right: 4,
              transform: 'translateY(-50%)',
              height: 2,
              background: '#000',
              boxShadow: '0 0 2px rgba(0,0,0,0.8)',
            }}/>
            {/* tick marks along top */}
            {Array.from({ length: 11 }).map((_, i) => (
              <div key={i} style={{
                position: 'absolute',
                top: i % 5 === 0 ? 0 : 4,
                bottom: i % 5 === 0 ? 0 : 4,
                left: `calc(${(i / 10) * 100}% - 0.5px)`,
                width: 1,
                background: 'rgba(255,255,255,0.18)',
              }}/>
            ))}
            {/* cap — wider + taller for easier visual reference; track itself
                 receives the pointer events (jump-to-click) */}
            <div style={{
              position: 'absolute',
              top: -5, bottom: -5,
              left: `calc(${drive * 100}% - 11px)`,
              width: 22,
              background: `linear-gradient(180deg, #d8d2c5 0%, #8a8478 45%, #3a3530 100%)`,
              borderRadius: 2,
              boxShadow: '0 2px 4px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.4)',
              pointerEvents: 'none',
            }}>
              <div style={{
                position: 'absolute',
                top: 2, bottom: 2, left: '50%',
                transform: 'translateX(-50%)',
                width: 2,
                background: accent,
                boxShadow: `0 0 4px ${accent}`,
              }}/>
            </div>
          </div>
          <div style={{
            display: 'flex', justifyContent: 'space-between',
            fontFamily: 'var(--font-engrave)', fontSize: 7,
            letterSpacing: '0.18em', color: v.ink, opacity: 0.6, fontWeight: 600,
          }}>
            <span>DRIVE</span>
            <span>0</span>
            <span>25</span>
            <span>50</span>
            <span>75</span>
            <span>100</span>
          </div>
        </div>

        {/* Band buttons — row */}
        <div style={{ display: 'flex', gap: 4 }}>
          {[
            { k: 'low',  label: 'LOW' },
            { k: 'mid',  label: 'MID' },
            { k: 'high', label: 'HIGH' },
          ].map(b => {
            const on = bands[b.k];
            return (
              <button key={b.k} onClick={() => toggleBand(b.k)} style={{
                width: 40, height: 24,
                background: on
                  ? `linear-gradient(180deg, ${accent} 0%, ${accent}cc 100%)`
                  : 'linear-gradient(180deg, #2a2620 0%, #0a0806 100%)',
                color: on ? '#1a1410' : '#e8e2d2',
                border: `1px solid ${on ? accent : '#000'}`,
                borderRadius: 3,
                fontFamily: 'var(--font-engrave)',
                fontSize: 8, fontWeight: 700,
                letterSpacing: '0.16em',
                cursor: 'pointer',
                boxShadow: on
                  ? `0 0 8px ${accent}aa, inset 0 1px 0 rgba(255,255,255,0.35)`
                  : 'inset 0 1px 2px rgba(0,0,0,0.6), 0 1px 0 rgba(255,255,255,0.15)',
                transition: 'all 80ms',
              }}>{b.label}</button>
            );
          })}
        </div>

        {/* Lift / Notch toggle — horizontal */}
        <div style={{
          width: 80,
          background: '#0a0806',
          border: '1px solid #000',
          borderRadius: 3,
          padding: 2,
          display: 'flex',
          position: 'relative',
          boxShadow: 'inset 0 1px 2px rgba(0,0,0,0.7)',
        }}>
          <div style={{
            position: 'absolute',
            top: 2, bottom: 2,
            left: mode === 'lift' ? 2 : '50%',
            width: 'calc(50% - 2px)',
            background: `linear-gradient(180deg, ${accent} 0%, ${accent}cc 100%)`,
            borderRadius: 2,
            transition: 'left 140ms',
            boxShadow: `0 0 6px ${accent}88`,
          }}/>
          <button onClick={() => { setMode('lift'); window.playKnobTick && window.playKnobTick(0.7); }} style={{
            flex: 1, position: 'relative', zIndex: 1,
            background: 'transparent', border: 0,
            fontFamily: 'var(--font-engrave)', fontSize: 8,
            fontWeight: 700, letterSpacing: '0.12em',
            color: mode === 'lift' ? '#1a1410' : '#8a8478',
            padding: '4px 0', cursor: 'pointer',
          }}>LIFT</button>
          <button onClick={() => { setMode('notch'); window.playKnobTick && window.playKnobTick(0.7); }} style={{
            flex: 1, position: 'relative', zIndex: 1,
            background: 'transparent', border: 0,
            fontFamily: 'var(--font-engrave)', fontSize: 8,
            fontWeight: 700, letterSpacing: '0.12em',
            color: mode === 'notch' ? '#1a1410' : '#8a8478',
            padding: '4px 0', cursor: 'pointer',
          }}>NOTCH</button>
        </div>

        {/* MATCH — level-match LIFT output to clean (operator-requested 2026-05-03).
            No-op in NOTCH mode (NOTCH already reduces level).  Sits next to the
            mode toggle so it's grouped with the saturation behavior controls. */}
        <button
          onClick={() => { setMatch && setMatch(!match); window.playKnobTick && window.playKnobTick(0.5); }}
          title={match ? 'Level match ON — sat output gain-compensated to match clean' : 'Click to match LIFT output level to clean'}
          disabled={mode === 'notch'}
          style={{
            marginLeft: 6,
            background: match ? `linear-gradient(180deg, ${v.inkAccent} 0%, ${v.inkAccent}aa 100%)` : '#1a1612',
            border: `1px solid ${v.ink}`,
            borderRadius: 3,
            fontFamily: 'var(--font-engrave)', fontSize: 8,
            fontWeight: 700, letterSpacing: '0.12em',
            color: match ? '#1a1410' : '#8a8478',
            padding: '4px 8px', cursor: mode === 'notch' ? 'not-allowed' : 'pointer',
            opacity: mode === 'notch' ? 0.35 : 1,
            transition: 'background 120ms, color 120ms',
            boxShadow: match
              ? `0 0 4px ${v.inkAccent}88, inset 0 1px 0 rgba(255,255,255,0.2)`
              : 'inset 0 1px 0 rgba(255,255,255,0.05)',
          }}>MATCH</button>
      </div>
    </div>
  );
}

// ── LicenseBanner — small status pill in the top-right of chassis ─────────
// Brand-spec copy (me-plugins-web/brand/MENTESH2A-BRAND.mdx §5 + §8):
// UK spelling "licence" (the noun); plain language; no exclamation marks.
function LicenseBanner({ state, onClick, v }) {
  const s = state.state;
  let label = '', bg = '', textColor = v.ink;
  if (s === 'licensed') {
    label = `LICENCED · ${state.scope.toUpperCase()}`;
    bg = 'rgba(40, 90, 40, 0.85)';
    textColor = '#cfeacf';
  } else if (s === 'trial') {
    label = `TRIAL · ${state.trialDaysLeft} DAY${state.trialDaysLeft === 1 ? '' : 'S'} REMAINING`;
    bg = 'rgba(180, 130, 40, 0.85)';
    textColor = '#fff2c8';
  } else if (s === 'demo') {
    label = 'DEMO · CLICK TO ENTER LICENCE';
    bg = 'rgba(180, 50, 30, 0.92)';
    textColor = '#ffd0c8';
  } else {
    label = 'NOT ACTIVATED · CLICK TO ENTER LICENCE';
    bg = 'rgba(80, 80, 80, 0.85)';
    textColor = '#e0e0e0';
  }
  return (
    <button
      onClick={onClick}
      title={s === 'licensed'
        ? `Licenced: ${state.maskedKey || ''} — active on this machine. Click to manage.`
        : 'Paste your licence key.'}
      style={{
        position: 'absolute', top: 6, right: 70,
        zIndex: 30,
        background: bg,
        color: textColor,
        border: '1px solid rgba(0,0,0,0.4)',
        borderRadius: 3,
        padding: '3px 9px',
        fontFamily: 'var(--font-engrave)', fontSize: 9,
        fontWeight: 700, letterSpacing: '0.18em',
        cursor: 'pointer',
        boxShadow: '0 1px 2px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.15)',
        textTransform: 'uppercase',
        whiteSpace: 'nowrap',
      }}
    >{label}</button>
  );
}

// ── LicenseDialog — modal for entering / managing license keys ────────────
function LicenseDialog({ state, onActivate, onDeactivate, onClose, v }) {
  const [keyInput, setKeyInput] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [msg, setMsg] = React.useState({ text: '', ok: null });
  const [confirmDeactivate, setConfirmDeactivate] = React.useState(false);

  const handleActivate = async () => {
    if (!keyInput.trim()) return;
    setBusy(true);
    setMsg({ text: 'Activating…', ok: null });
    const res = await onActivate(keyInput.trim().toUpperCase());
    setBusy(false);
    setMsg({ text: res.message || (res.ok ? 'Activated' : 'Failed'), ok: res.ok });
    if (res.ok) setKeyInput('');
  };

  const handleDeactivate = async () => {
    setBusy(true);
    setMsg({ text: 'Deactivating…', ok: null });
    const res = await onDeactivate();
    setBusy(false);
    setMsg({ text: res.message || 'Deactivated', ok: res.ok });
    setConfirmDeactivate(false);
  };

  const isLicensed = state.state === 'licensed';

  return (
    <div
      onClick={onClose}
      style={{
        position: 'absolute', inset: 0, zIndex: 100,
        background: 'rgba(0,0,0,0.65)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}
    >
      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          width: 460, maxWidth: '92%',
          background: '#1f1d1a',
          color: '#e8e4dc',
          border: '1px solid rgba(255,255,255,0.08)',
          borderRadius: 6,
          padding: '24px 28px',
          boxShadow: '0 12px 40px rgba(0,0,0,0.7)',
          fontFamily: 'system-ui, -apple-system, sans-serif',
        }}
      >
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 14 }}>
          <div style={{ fontSize: 16, fontWeight: 700, letterSpacing: '0.05em' }}>Licence</div>
          <button onClick={onClose} style={{
            background: 'transparent', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: 18, padding: 0, lineHeight: 1,
          }}>×</button>
        </div>

        {/* Status block */}
        <div style={{
          background: 'rgba(255,255,255,0.04)',
          border: '1px solid rgba(255,255,255,0.06)',
          borderRadius: 4,
          padding: '10px 12px',
          marginBottom: 14,
          fontSize: 12,
        }}>
          <div style={{ fontSize: 10, color: '#888', letterSpacing: '0.15em', marginBottom: 4 }}>STATUS</div>
          {state.state === 'licensed' && (
            <div>
              <div style={{ color: '#9fd29f', fontWeight: 600 }}>Licenced — {state.scope || 'paid'}</div>
              <div style={{ color: '#bbb', fontSize: 11, marginTop: 2 }}>Active on this machine. Key: {state.maskedKey}</div>
            </div>
          )}
          {state.state === 'trial' && (
            <div>
              <div style={{ color: '#f0c878', fontWeight: 600 }}>Trial — {state.trialDaysLeft} day{state.trialDaysLeft === 1 ? '' : 's'} remaining.</div>
              <div style={{ color: '#bbb', fontSize: 11, marginTop: 2 }}>Full features. Buy a licence below to keep using after the trial.</div>
            </div>
          )}
          {state.state === 'demo' && (
            <div>
              <div style={{ color: '#f08878', fontWeight: 600 }}>Trial expired — demo mode.</div>
              <div style={{ color: '#bbb', fontSize: 11, marginTop: 2 }}>Audio fades to silence every 30 seconds. Paste your licence key below to unlock.</div>
            </div>
          )}
          {state.state === 'inactive' && (
            <div>
              <div style={{ color: '#aaa', fontWeight: 600 }}>Not activated.</div>
              <div style={{ color: '#bbb', fontSize: 11, marginTop: 2 }}>Paste your licence key below.</div>
            </div>
          )}
        </div>

        {/* Activation input — show unless Licensed */}
        {!isLicensed && (
          <>
            <div style={{ fontSize: 11, color: '#aaa', marginBottom: 6, letterSpacing: '0.1em' }}>LICENCE KEY</div>
            <input
              type="text"
              value={keyInput}
              onChange={(e) => setKeyInput(e.target.value.toUpperCase())}
              placeholder="MNTSH-XXXX-XXXX-XXXX-XXXX-XXXX"
              autoFocus
              spellCheck={false}
              style={{
                width: '100%',
                background: '#0e0d0b',
                color: '#fff',
                border: '1px solid rgba(255,255,255,0.12)',
                borderRadius: 3,
                padding: '8px 10px',
                fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
                fontSize: 13,
                letterSpacing: '0.05em',
                outline: 'none',
                boxSizing: 'border-box',
              }}
              onKeyDown={(e) => { if (e.key === 'Enter' && !busy) handleActivate(); }}
            />
            <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
              <button
                onClick={handleActivate}
                disabled={busy || !keyInput.trim()}
                style={{
                  flex: 1,
                  background: busy || !keyInput.trim() ? '#3a3530' : v.inkAccent || '#c66a30',
                  color: '#fff',
                  border: 'none', borderRadius: 3,
                  padding: '8px 14px',
                  fontSize: 12, fontWeight: 700, letterSpacing: '0.08em',
                  cursor: busy || !keyInput.trim() ? 'default' : 'pointer',
                }}
              >ACTIVATE</button>
              <button
                onClick={() => {
                  if (typeof window !== 'undefined' && window.juceInvoke)
                    window.juceInvoke('openExternalUrl', 'https://me-plugins.com/buy/mentesh2a');
                }}
                style={{
                  background: 'transparent', color: '#cfd8e0',
                  border: '1px solid rgba(255,255,255,0.18)', borderRadius: 3,
                  padding: '8px 14px',
                  fontSize: 12, fontWeight: 700, letterSpacing: '0.08em',
                  cursor: 'pointer',
                }}
              >BUY A LICENCE</button>
            </div>
          </>
        )}

        {/* Licensed view: deactivate */}
        {isLicensed && (
          <div style={{ marginTop: 4 }}>
            {!confirmDeactivate ? (
              <button
                onClick={() => setConfirmDeactivate(true)}
                style={{
                  background: 'transparent', color: '#e8a098',
                  border: '1px solid rgba(232,160,152,0.4)', borderRadius: 3,
                  padding: '8px 14px',
                  fontSize: 11, fontWeight: 700, letterSpacing: '0.1em',
                  cursor: 'pointer',
                }}
              >DEACTIVATE THIS MACHINE</button>
            ) : (
              <div>
                <div style={{ fontSize: 11, color: '#e8a098', marginBottom: 8 }}>
                  Frees one of your two activation slots. You'll need to re-enter the key to use the plug-in on this machine again.
                </div>
                <div style={{ display: 'flex', gap: 8 }}>
                  <button
                    onClick={handleDeactivate}
                    disabled={busy}
                    style={{
                      background: '#c83a30', color: '#fff',
                      border: 'none', borderRadius: 3,
                      padding: '8px 14px', fontSize: 11, fontWeight: 700, letterSpacing: '0.08em',
                      cursor: busy ? 'default' : 'pointer',
                    }}
                  >YES, DEACTIVATE</button>
                  <button
                    onClick={() => setConfirmDeactivate(false)}
                    style={{
                      background: 'transparent', color: '#aaa',
                      border: '1px solid rgba(255,255,255,0.18)', borderRadius: 3,
                      padding: '8px 14px', fontSize: 11, fontWeight: 700, letterSpacing: '0.08em',
                      cursor: 'pointer',
                    }}
                  >CANCEL</button>
                </div>
              </div>
            )}
          </div>
        )}

        {/* Status message */}
        {msg.text && (
          <div style={{
            marginTop: 14, padding: '8px 10px', borderRadius: 3,
            background: msg.ok === true ? 'rgba(80,160,80,0.18)' :
                        msg.ok === false ? 'rgba(200,80,80,0.18)' :
                        'rgba(255,255,255,0.05)',
            color: msg.ok === true ? '#9fd29f' : msg.ok === false ? '#e8a098' : '#cfd0d2',
            fontSize: 11, lineHeight: 1.4,
          }}>{msg.text}</div>
        )}

        <div style={{
          marginTop: 16, fontSize: 10, color: '#777', textAlign: 'center', lineHeight: 1.5,
        }}>
          Need help? <span
              onClick={() => window.juceInvoke && window.juceInvoke('openExternalUrl', 'mailto:support@me-plugins.com')}
              style={{ color: '#9ab', cursor: 'pointer', textDecoration: 'underline' }}
            >support@me-plugins.com</span>
          &nbsp;·&nbsp; <span
              onClick={() => window.juceInvoke && window.juceInvoke('openExternalUrl', 'https://me-plugins.com/account/licenses')}
              style={{ color: '#9ab', cursor: 'pointer', textDecoration: 'underline' }}
            >Manage licences</span>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { MENTESH2A, VARIANTS, VARIANT_DSP_PROFILES });
