/**
 * CHEF MT-3 tuner visualization for the FeedBack tuner plugin.
 *
 * Inspired by classic chromatic pedal tuners:
 *   - Shiny black rectangular panel with chamfered edges and corner screws
 *   - 90° curved glass gauge arc spanning between the screw inner edges
 *   - 51 tick marks; glow fades in/out with audio signal presence
 *   - Red 7-segment display (bottom centre) with "#" symbol
 *   - Two rubber buttons flanked by panel labels: MODE (left), BRGHT. (right)
 *   - Standard mode: nearest tick cluster glows for current deviation
 *   - Strobe mode: groups of 3 bright ticks + lightspill drift with deviation
 *
 * Contract: window['_tunerViz_chef-mt3'](container) → { update(note, cents, freq, mode), destroy() }
 */
(function () {
    'use strict';

    // ── Module-level constants ─────────────────────────────────────────
    var _TUNER_MT3_IN_TUNE_THR        = 2;
    var _TUNER_MT3_GAUGE_CENTS        = 50;
    var _TUNER_MT3_TICK_COUNT         = 51;   // 0¢ centre + 25×2¢ per side
    var _TUNER_MT3_STROBE_GROUP_COUNT = 5;

    // ── Colours ────────────────────────────────────────────────────────
    var _MT3_COL_BG        = '#0a0a0a';
    var _MT3_COL_GAUGE_ARC = 'rgba(255,255,255,0.18)';
    var _MT3_COL_TICK_DIM  = 'rgba(255,255,255,0.45)';
    // Tick glow colours are computed per-tick at factory construction (orange→yellow gradient)
    var _MT3_COL_SEG_UNLIT = '#2a0000';

    // Brightness levels: [brightScale for tick glow, SVG filter flood-opacity, lit segment fill, segment drop-shadow]
    var _MT3_BRIGHTNESS = [
        { brightScale: 0.66, floodOpacity: '0.45', litFill: '#cc1500', glow: 'drop-shadow(0 0 2px #bb1100)' },  // low
        { brightScale: 1.0,  floodOpacity: '0.65', litFill: '#ff2200', glow: 'drop-shadow(0 0 5px #ff2200)' },  // medium
        { brightScale: 1.0,  floodOpacity: '1.0',  litFill: '#ff5533', glow: 'drop-shadow(0 0 9px #ff4400)' },  // high
    ];
    var _MT3_COL_BUTTON    = '#1a1a1a';
    var _MT3_COL_LABEL     = '#c8c8c8';

    // ── Gauge SVG geometry ─────────────────────────────────────────────
    // viewBox "0 0 200 66": ratio 3.03 matches SVG element (width:100% height:auto on 5:3 panel)
    // 90° arc: 225°(−50¢) → 270°(apex) → 315°(+50¢); R=124, cy=138 → apex at y=14
    var _MT3_cx        = 100;
    var _MT3_cy        = 138;
    var _MT3_ARC_R     = 124;
    var _MT3_ARC_START = 5 * Math.PI / 4;
    var _MT3_ARC_SPAN  = Math.PI / 2;

    var _MT3_ARC_SX = _MT3_cx + _MT3_ARC_R * Math.cos(_MT3_ARC_START);
    var _MT3_ARC_SY = _MT3_cy + _MT3_ARC_R * Math.sin(_MT3_ARC_START);
    var _MT3_ARC_EX = _MT3_cx + _MT3_ARC_R * Math.cos(_MT3_ARC_START + _MT3_ARC_SPAN);
    var _MT3_ARC_EY = _MT3_cy + _MT3_ARC_R * Math.sin(_MT3_ARC_START + _MT3_ARC_SPAN);

    var _SVG_NS = 'http://www.w3.org/2000/svg';

    // ── 8-segment lookup table ─────────────────────────────────────────
    var _TUNER_MT3_SEGMENTS = {
        //         a      b      c      d      e      f      g1     g2
        'A': [ true,  true,  true,  false, true,  true,  true,  true  ],
        'B': [ false, false, true,  true,  true,  true,  true,  true  ],
        'C': [ true,  false, false, true,  true,  true,  false, false ],
        'D': [ false, true,  true,  true,  true,  false, true,  true  ],
        'E': [ true,  false, false, true,  true,  true,  true,  false ],
        'F': [ true,  false, false, false, true,  true,  true,  false ],
        'G': [ true,  false, true,  true,  true,  true,  false, true  ],
        ' ': [ false, false, false, false, false, false, false, false ],
    };
    var _segKeys = ['a', 'b', 'c', 'd', 'e', 'f', 'g1', 'g2'];

    window['_tunerViz_chef-mt3'] = function (container) {
        'use strict';

        // ── Root panel ────────────────────────────────────────────────
        var panel = document.createElement('div');
        panel.style.position        = 'relative';
        panel.style.overflow        = 'hidden';
        panel.style.aspectRatio     = '5 / 3';
        panel.style.minHeight       = '120px';
        panel.style.backgroundColor = _MT3_COL_BG;
        panel.style.border          = '2px solid #505050';
        panel.style.borderRadius    = '6px';
        panel.style.userSelect      = 'none';
        panel.style.fontFamily      = 'monospace';

        // ── Corner screws ─────────────────────────────────────────────
        [['top','left'],['top','right'],['bottom','left'],['bottom','right']].forEach(function (pos) {
            var s = document.createElement('div');
            s.style.position      = 'absolute';
            s.style[pos[0]]       = '3%';
            s.style[pos[1]]       = '2%';
            s.style.width         = '4%';
            s.style.height        = '0';
            s.style.paddingBottom = '4%';
            s.style.borderRadius  = '50%';
            s.style.background    = 'radial-gradient(circle at 35% 35%, #666, #222)';
            s.style.boxShadow     = '0 1px 3px rgba(0,0,0,0.9), inset 0 1px 1px rgba(255,255,255,0.12)';
            s.style.zIndex        = '5';
            var slot = document.createElement('div');
            slot.style.position        = 'absolute';
            slot.style.top             = '45%';
            slot.style.left            = '15%';
            slot.style.right           = '15%';
            slot.style.height          = '10%';
            slot.style.backgroundColor = '#111';
            s.appendChild(slot);
            panel.appendChild(s);
        });

        // ── Gauge SVG ─────────────────────────────────────────────────
        var gaugeSvg = document.createElementNS(_SVG_NS, 'svg');
        gaugeSvg.setAttribute('viewBox', '0 0 200 66');
        gaugeSvg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
        gaugeSvg.style.position = 'absolute';
        gaugeSvg.style.top      = '5%';
        gaugeSvg.style.left     = '0';
        gaugeSvg.style.width    = '100%';
        gaugeSvg.style.height   = 'auto';
        gaugeSvg.style.overflow = 'visible';

        // SVG glow filter — applied to the glow tick group as a whole
        var _glowId     = 'mt3-glow-' + Math.random().toString(36).slice(2, 7);
        var _svgDefs    = document.createElementNS(_SVG_NS, 'defs');
        var _svgFilter  = document.createElementNS(_SVG_NS, 'filter');
        _svgFilter.setAttribute('id', _glowId);
        _svgFilter.setAttribute('x', '-60%'); _svgFilter.setAttribute('y', '-60%');
        _svgFilter.setAttribute('width', '220%'); _svgFilter.setAttribute('height', '220%');
        var _sfBlur  = document.createElementNS(_SVG_NS, 'feGaussianBlur');
        _sfBlur.setAttribute('stdDeviation', '1.8'); _sfBlur.setAttribute('result', 'blur');
        var _sfFlood = document.createElementNS(_SVG_NS, 'feFlood');
        _sfFlood.setAttribute('flood-color', '#ff8800'); _sfFlood.setAttribute('flood-opacity', '0.65');
        _sfFlood.setAttribute('result', 'col');
        var _sfComp  = document.createElementNS(_SVG_NS, 'feComposite');
        _sfComp.setAttribute('in', 'col'); _sfComp.setAttribute('in2', 'blur');
        _sfComp.setAttribute('operator', 'in'); _sfComp.setAttribute('result', 'glow');
        var _sfMerge = document.createElementNS(_SVG_NS, 'feMerge');
        var _sfMn1   = document.createElementNS(_SVG_NS, 'feMergeNode'); _sfMn1.setAttribute('in', 'glow');
        var _sfMn2   = document.createElementNS(_SVG_NS, 'feMergeNode'); _sfMn2.setAttribute('in', 'SourceGraphic');
        _sfMerge.appendChild(_sfMn1); _sfMerge.appendChild(_sfMn2);
        _svgFilter.appendChild(_sfBlur); _svgFilter.appendChild(_sfFlood);
        _svgFilter.appendChild(_sfComp); _svgFilter.appendChild(_sfMerge);
        // Shared blur filter for highlight and shadow arcs
        var _glassBlurId = 'mt3-gb-' + Math.random().toString(36).slice(2, 7);
        var _glassBlur   = document.createElementNS(_SVG_NS, 'filter');
        _glassBlur.setAttribute('id', _glassBlurId);
        _glassBlur.setAttribute('x', '-30%'); _glassBlur.setAttribute('y', '-30%');
        _glassBlur.setAttribute('width', '160%'); _glassBlur.setAttribute('height', '160%');
        var _gbFe = document.createElementNS(_SVG_NS, 'feGaussianBlur');
        _gbFe.setAttribute('stdDeviation', '1.8');
        _glassBlur.appendChild(_gbFe);
        _svgDefs.appendChild(_glassBlur);

        // Gradient for specular highlight: transparent at arc ends, white at centre
        var _hlGradId = 'mt3-hl-' + Math.random().toString(36).slice(2, 7);
        var _hlGrad   = document.createElementNS(_SVG_NS, 'linearGradient');
        _hlGrad.setAttribute('id', _hlGradId);
        _hlGrad.setAttribute('gradientUnits', 'userSpaceOnUse');
        _hlGrad.setAttribute('x1', _MT3_ARC_SX.toFixed(2)); _hlGrad.setAttribute('y1', '0');
        _hlGrad.setAttribute('x2', _MT3_ARC_EX.toFixed(2)); _hlGrad.setAttribute('y2', '0');
        [['0%','rgba(255,255,255,0.65)'],['4%','rgba(255,255,255,0)'],['20%','rgba(255,255,255,0)'],['35%','rgba(255,255,255,0.68)'],['65%','rgba(255,255,255,0.68)'],['80%','rgba(255,255,255,0)'],['96%','rgba(255,255,255,0)'],['100%','rgba(255,255,255,0.65)']]
        .forEach(function(s){var st=document.createElementNS(_SVG_NS,'stop');st.setAttribute('offset',s[0]);st.setAttribute('stop-color',s[1]);_hlGrad.appendChild(st);});
        _svgDefs.appendChild(_hlGrad);

        // Gradient for outer shadow: transparent at arc ends, dark at centre
        var _shGradId = 'mt3-sh-' + Math.random().toString(36).slice(2, 7);
        var _shGrad   = document.createElementNS(_SVG_NS, 'linearGradient');
        _shGrad.setAttribute('id', _shGradId);
        _shGrad.setAttribute('gradientUnits', 'userSpaceOnUse');
        _shGrad.setAttribute('x1', _MT3_ARC_SX.toFixed(2)); _shGrad.setAttribute('y1', '0');
        _shGrad.setAttribute('x2', _MT3_ARC_EX.toFixed(2)); _shGrad.setAttribute('y2', '0');
        [['0%','rgba(0,0,0,0.65)'],['4%','rgba(0,0,0,0)'],['20%','rgba(0,0,0,0.68)'],['80%','rgba(0,0,0,0.68)'],['96%','rgba(0,0,0,0)'],['100%','rgba(0,0,0,0.65)']]
        .forEach(function(s){var st=document.createElementNS(_SVG_NS,'stop');st.setAttribute('offset',s[0]);st.setAttribute('stop-color',s[1]);_shGrad.appendChild(st);});
        _svgDefs.appendChild(_shGrad);

        _svgDefs.appendChild(_svgFilter);
        gaugeSvg.appendChild(_svgDefs);

        var _arcD = 'M ' + _MT3_ARC_SX.toFixed(2) + ' ' + _MT3_ARC_SY.toFixed(2) +
            ' A ' + _MT3_ARC_R + ' ' + _MT3_ARC_R + ' 0 0 1 ' +
            _MT3_ARC_EX.toFixed(2) + ' ' + _MT3_ARC_EY.toFixed(2);

        // Glass arc body
        var arcBody = document.createElementNS(_SVG_NS, 'path');
        arcBody.setAttribute('d', _arcD);
        arcBody.setAttribute('fill', 'none');
        arcBody.setAttribute('stroke', _MT3_COL_GAUGE_ARC);
        arcBody.setAttribute('stroke-width', '16');
        arcBody.setAttribute('stroke-linecap', 'round');
        gaugeSvg.appendChild(arcBody);

        // Inner shadow — dark stroke on the inner-lower edge, simulates less light reaching far side
        var _shadowR  = _MT3_ARC_R - 6;
        // Align shadow gradient vector to shadow arc's own endpoints (not the main arc's)
        _shGrad.setAttribute('x1', (_MT3_cx + _shadowR * Math.cos(_MT3_ARC_START)).toFixed(2));
        _shGrad.setAttribute('x2', (_MT3_cx + _shadowR * Math.cos(_MT3_ARC_START + _MT3_ARC_SPAN)).toFixed(2));
        var arcShadow = document.createElementNS(_SVG_NS, 'path');
        arcShadow.setAttribute('d',
            'M ' + (_MT3_cx + _shadowR * Math.cos(_MT3_ARC_START)).toFixed(2) + ' ' +
                   (_MT3_cy + _shadowR * Math.sin(_MT3_ARC_START)).toFixed(2) +
            ' A ' + _shadowR + ' ' + _shadowR + ' 0 0 1 ' +
                   (_MT3_cx + _shadowR * Math.cos(_MT3_ARC_START + _MT3_ARC_SPAN)).toFixed(2) + ' ' +
                   (_MT3_cy + _shadowR * Math.sin(_MT3_ARC_START + _MT3_ARC_SPAN)).toFixed(2));
        arcShadow.setAttribute('fill', 'none');
        arcShadow.setAttribute('stroke', 'url(#' + _shGradId + ')');
        arcShadow.setAttribute('stroke-width', '3.5');
        arcShadow.setAttribute('stroke-linecap', 'round');
        arcShadow.setAttribute('filter', 'url(#' + _glassBlurId + ')');

        // Specular highlight — bright white arc fading to transparent at ends
        var _hlR        = _MT3_ARC_R + 2;
        var arcHighlight = document.createElementNS(_SVG_NS, 'path');
        arcHighlight.setAttribute('d',
            'M ' + (_MT3_cx + _hlR * Math.cos(_MT3_ARC_START)).toFixed(2) + ' ' +
                   (_MT3_cy + _hlR * Math.sin(_MT3_ARC_START)).toFixed(2) +
            ' A ' + _hlR + ' ' + _hlR + ' 0 0 1 ' +
                   (_MT3_cx + _hlR * Math.cos(_MT3_ARC_START + _MT3_ARC_SPAN)).toFixed(2) + ' ' +
                   (_MT3_cy + _hlR * Math.sin(_MT3_ARC_START + _MT3_ARC_SPAN)).toFixed(2));
        arcHighlight.setAttribute('fill', 'none');
        arcHighlight.setAttribute('stroke', 'url(#' + _hlGradId + ')');
        arcHighlight.setAttribute('stroke-width', '2.5');
        arcHighlight.setAttribute('stroke-linecap', 'round');
        arcHighlight.setAttribute('filter', 'url(#' + _glassBlurId + ')');

        // dimGroup: base tick lines always shown at dim colour — constructed once, never updated
        var _dimGroup = document.createElementNS(_SVG_NS, 'g');

        // glowGroup: lit tick overlay with shared glow filter, opacity animated 0→1
        var _glowGroup = document.createElementNS(_SVG_NS, 'g');
        _glowGroup.setAttribute('filter', 'url(#' + _glowId + ')');
        _glowGroup.setAttribute('opacity', '0');

        // Z-order: arcBody → dimGroup (unlit ticks) → arcShadow → glowGroup (lit ticks) → arcHighlight
        gaugeSvg.appendChild(_dimGroup);
        gaugeSvg.appendChild(arcShadow);
        gaugeSvg.appendChild(_glowGroup);

        var _mt3GlowTickEls = [];

        for (var i = 0; i < _TUNER_MT3_TICK_COUNT; i++) {
            var isMajor = (i % 5 === 0);
            var halfLen = isMajor ? 5 : 3;
            var a       = _MT3_ARC_START + (_MT3_ARC_SPAN / (_TUNER_MT3_TICK_COUNT - 1)) * i;
            var cosA    = Math.cos(a), sinA = Math.sin(a);
            var x1 = _MT3_cx + (_MT3_ARC_R - halfLen) * cosA;
            var y1 = _MT3_cy + (_MT3_ARC_R - halfLen) * sinA;
            var x2 = _MT3_cx + (_MT3_ARC_R + halfLen) * cosA;
            var y2 = _MT3_cy + (_MT3_ARC_R + halfLen) * sinA;

            var dimTick = document.createElementNS(_SVG_NS, 'line');
            dimTick.setAttribute('x1', String(x1)); dimTick.setAttribute('y1', String(y1));
            dimTick.setAttribute('x2', String(x2)); dimTick.setAttribute('y2', String(y2));
            dimTick.setAttribute('stroke', _MT3_COL_TICK_DIM);
            dimTick.setAttribute('stroke-width', '1');
            dimTick.setAttribute('stroke-linecap', 'round');
            _dimGroup.appendChild(dimTick);

            var glowTick = document.createElementNS(_SVG_NS, 'line');
            glowTick.setAttribute('x1', String(x1)); glowTick.setAttribute('y1', String(y1));
            glowTick.setAttribute('x2', String(x2)); glowTick.setAttribute('y2', String(y2));
            glowTick.setAttribute('stroke', 'none');
            glowTick.setAttribute('stroke-width', '1');
            glowTick.setAttribute('stroke-linecap', 'round');
            _glowGroup.appendChild(glowTick);
            _mt3GlowTickEls.push(glowTick);
        }

        // Per-tick gradient colours: orange (#ff7700) at arc edges → yellow (#ffee00) at centre
        // d = distance from centre (0 = centre tick, 1 = end ticks)
        var _mt3TickColors     = [];
        var _mt3TickSpillColors = [];
        for (var tc = 0; tc < _TUNER_MT3_TICK_COUNT; tc++) {
            var d  = Math.abs((tc / (_TUNER_MT3_TICK_COUNT - 1)) - 0.5) * 2;
            var tg = Math.round(238 * (1 - d) + 119 * d);   // G channel: 238 (yellow) → 119 (orange)
            _mt3TickColors.push('rgb(255,' + tg + ',0)');
            _mt3TickSpillColors.push('rgba(255,' + tg + ',0,0.52)');
        }

        // Arc-following labels — inside the arc at R−18 (below the tube's inner edge with a gap)
        // Tube inner edge at R−8; labels at R−18 give ~10 SVG-unit gap at apex, ~7 at ends
        [
            { t: 0.0, text: '-50' },
            { t: 0.5, text:  '0'  },
            { t: 1.0, text: '+50' },
        ].forEach(function (lbl) {
            var ang = _MT3_ARC_START + lbl.t * _MT3_ARC_SPAN;
            var r   = _MT3_ARC_R - 18;
            var lx  = _MT3_cx + r * Math.cos(ang);
            var ly  = _MT3_cy + r * Math.sin(ang);
            var el  = document.createElementNS(_SVG_NS, 'text');
            el.setAttribute('x', String(lx));
            el.setAttribute('y', String(ly));
            el.setAttribute('text-anchor', 'middle');
            el.setAttribute('dominant-baseline', 'middle');
            el.setAttribute('font-size', '7');
            el.setAttribute('fill', 'rgba(255,255,255,0.50)');
            el.textContent = lbl.text;
            gaugeSvg.appendChild(el);
        });

        // arcHighlight is topmost — appended last so it renders above ticks
        gaugeSvg.appendChild(arcHighlight);

        panel.appendChild(gaugeSvg);

        // ── 7-segment display ─────────────────────────────────────────
        var displayWrap = document.createElement('div');
        displayWrap.style.position       = 'absolute';
        displayWrap.style.bottom         = '5%';
        displayWrap.style.left           = '50%';
        displayWrap.style.transform      = 'translateX(-50%)';
        displayWrap.style.width          = '18%';
        displayWrap.style.height         = '45%';
        displayWrap.style.background     = '#0d0000';
        displayWrap.style.borderRadius   = '3px';
        displayWrap.style.border         = '1px solid #2a0000';
        displayWrap.style.display        = 'flex';
        displayWrap.style.alignItems     = 'center';
        displayWrap.style.justifyContent = 'center';
        displayWrap.style.padding        = '4%';
        displayWrap.style.boxSizing      = 'border-box';
        displayWrap.style.boxShadow      = 'inset 0 0 8px #000';
        panel.appendChild(displayWrap);

        var segSvg = document.createElementNS(_SVG_NS, 'svg');
        segSvg.setAttribute('viewBox', '0 0 100 200');
        segSvg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
        segSvg.style.width       = '90%';
        segSvg.style.aspectRatio = '1 / 2';
        segSvg.style.flexShrink  = '0';
        segSvg.style.overflow    = 'visible';
        displayWrap.appendChild(segSvg);

        var _mt3SegEls = {};
        function _makeSegPoly(key, points) {
            var el = document.createElementNS(_SVG_NS, 'polygon');
            el.setAttribute('points', points);
            el.setAttribute('fill', _MT3_COL_SEG_UNLIT);
            el.setAttribute('shape-rendering', 'crispEdges');
            segSvg.appendChild(el);
            _mt3SegEls[key] = el;
        }
        _makeSegPoly('a',  '11,5    89,5    95,13   89,21   11,21   5,13');
        _makeSegPoly('b',  '87,26   95,32   95,81   87,87   79,81   79,32');
        _makeSegPoly('c',  '87,113  95,119  95,168  87,174  79,168  79,119');
        _makeSegPoly('d',  '11,179  89,179  95,187  89,195  11,195  5,187');
        _makeSegPoly('e',  '13,113  21,119  21,168  13,174  5,168   5,119');
        _makeSegPoly('f',  '13,26   21,32   21,81   13,87   5,81    5,32');
        _makeSegPoly('g1', '11,92   42.5,92 48.5,100 42.5,108 11,108 5,100');
        _makeSegPoly('g2', '57.5,92 89,92   95,100  89,108  57.5,108 51.5,100');

        var sharpSvg = document.createElementNS(_SVG_NS, 'svg');
        sharpSvg.setAttribute('viewBox', '0 0 90 90');
        sharpSvg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
        sharpSvg.style.position      = 'absolute';
        sharpSvg.style.top           = '57%';
        sharpSvg.style.right         = '3%';
        sharpSvg.style.width         = '23%';
        sharpSvg.style.aspectRatio   = '1 / 1';
        sharpSvg.style.overflow      = 'visible';
        sharpSvg.style.pointerEvents = 'none';
        displayWrap.appendChild(sharpSvg);

        var _mt3SharpParts = [];
        function _makeSharpPoly(pts) {
            var el = document.createElementNS(_SVG_NS, 'polygon');
            el.setAttribute('points', pts);
            el.setAttribute('fill', _MT3_COL_SEG_UNLIT);
            el.setAttribute('shape-rendering', 'crispEdges');
            sharpSvg.appendChild(el);
            _mt3SharpParts.push(el);
        }
        _makeSharpPoly('28.3,0   33.3,4   33.3,86  28.3,90  23.3,86  23.3,4');
        _makeSharpPoly('61.7,0   66.7,4   66.7,86  61.7,90  56.7,86  56.7,4');
        _makeSharpPoly('4,23.3   86,23.3  90,28.3  86,33.3  4,33.3   0,28.3');
        _makeSharpPoly('4,56.7   86,56.7  90,61.7  86,66.7  4,66.7   0,61.7');

        // "♭" symbol — same position as sharpSvg, shown in place of it for flat notes
        var flatSvg = document.createElementNS(_SVG_NS, 'svg');
        flatSvg.setAttribute('viewBox', '0 0 90 90');
        flatSvg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
        flatSvg.style.position     = 'absolute';
        flatSvg.style.top          = '57%';
        flatSvg.style.right        = '3%';
        flatSvg.style.width        = '23%';
        flatSvg.style.aspectRatio  = '1 / 1';
        flatSvg.style.overflow     = 'visible';
        flatSvg.style.pointerEvents = 'none';
        flatSvg.style.display      = 'none';
        displayWrap.appendChild(flatSvg);
        var _mt3FlatText = document.createElementNS(_SVG_NS, 'text');
        _mt3FlatText.setAttribute('x', '45');
        _mt3FlatText.setAttribute('y', '82');
        _mt3FlatText.setAttribute('text-anchor', 'middle');
        _mt3FlatText.setAttribute('font-size', '85');
        _mt3FlatText.setAttribute('font-family', 'Georgia, serif');
        _mt3FlatText.setAttribute('fill', _MT3_COL_SEG_UNLIT);
        _mt3FlatText.textContent = '♭';
        flatSvg.appendChild(_mt3FlatText);

        // ── Buttons ───────────────────────────────────────────────────
        // Strobe indicator LED — centred above the MODE button, lit when strobe mode is active
        // Button: bottom:24%, height:8% → top edge at bottom:32%; LED sits at bottom:33.5%
        // Button left:calc(50%-22%), width:10% → centre at calc(50%-17%); LED width:2.5% → left:calc(50%-18.25%)
        var _mt3StrobeLed = document.createElement('div');
        _mt3StrobeLed.style.position      = 'absolute';
        _mt3StrobeLed.style.bottom        = '33.5%';
        _mt3StrobeLed.style.left          = 'calc(50% - 18.25%)';
        _mt3StrobeLed.style.width         = '2.5%';
        _mt3StrobeLed.style.height        = '0';
        _mt3StrobeLed.style.paddingBottom = '2.5%';
        _mt3StrobeLed.style.borderRadius  = '50%';
        _mt3StrobeLed.style.background    = 'radial-gradient(circle at 35% 35%, #2a0000, #0a0000)';
        _mt3StrobeLed.style.boxShadow     = 'none';
        _mt3StrobeLed.style.pointerEvents = 'none';
        panel.appendChild(_mt3StrobeLed);

        var _mt3ModeBtn = document.createElement('div');
        _mt3ModeBtn.style.position        = 'absolute';
        _mt3ModeBtn.style.bottom          = '24%';
        _mt3ModeBtn.style.left            = 'calc(50% - 22%)';
        _mt3ModeBtn.style.width           = '10%';
        _mt3ModeBtn.style.height          = '8%';
        _mt3ModeBtn.style.backgroundColor = _MT3_COL_BUTTON;
        _mt3ModeBtn.style.borderRadius    = '3px';
        _mt3ModeBtn.style.border          = '1px solid #333';
        _mt3ModeBtn.style.boxShadow       = 'inset 0 1px 2px rgba(255,255,255,0.08), 0 2px 3px rgba(0,0,0,0.7)';
        _mt3ModeBtn.style.cursor          = 'pointer';
        panel.appendChild(_mt3ModeBtn);

        var brightBtn = document.createElement('div');
        brightBtn.style.position        = 'absolute';
        brightBtn.style.bottom          = '24%';
        brightBtn.style.left            = 'calc(50% + 12%)';
        brightBtn.style.width           = '10%';
        brightBtn.style.height          = '8%';
        brightBtn.style.backgroundColor = _MT3_COL_BUTTON;
        brightBtn.style.borderRadius    = '3px';
        brightBtn.style.border          = '1px solid #333';
        brightBtn.style.boxShadow       = 'inset 0 1px 2px rgba(255,255,255,0.08), 0 2px 3px rgba(0,0,0,0.7)';
        brightBtn.style.cursor          = 'pointer';
        panel.appendChild(brightBtn);

        [{text: 'MODE', left: 'calc(50% - 22%)'}, {text: 'BRGHT', left: 'calc(50% + 12%)'}]
        .forEach(function (lbl) {
            var el = document.createElement('div');
            el.style.position      = 'absolute';
            el.style.bottom        = '14%';
            el.style.left          = lbl.left;
            el.style.width         = '10%';
            el.style.textAlign     = 'center';
            el.style.color         = _MT3_COL_LABEL;
            el.style.fontSize      = '0.75em';
            el.style.letterSpacing = '0.05em';
            el.style.pointerEvents = 'none';
            el.textContent         = lbl.text;
            panel.appendChild(el);
        });

        var brandLbl = document.createElement('div');
        brandLbl.style.position      = 'absolute';
        brandLbl.style.top           = '2%';
        brandLbl.style.right         = '8%';
        brandLbl.style.color         = _MT3_COL_LABEL;
        brandLbl.style.fontSize      = '0.75em';
        brandLbl.style.fontWeight    = '600';
        brandLbl.style.letterSpacing = '0.06em';
        brandLbl.textContent         = 'CHEF MT-3';
        panel.appendChild(brandLbl);

        container.appendChild(panel);

        // ── Animation / glow state ────────────────────────────────────
        var _mt3Mode            = 'standard';
        var _mt3CurrentCents    = 0;
        var _mt3SmoothedCents   = 0;
        var _mt3HasSignal       = false;
        var _mt3GlowOpacity     = 0;
        var _mt3RafId           = null;
        var _mt3BrightnessIdx   = 1;   // 0=low, 1=medium, 2=high
        var _mt3LastLetter      = ' '; // for re-render on brightness change
        var _mt3LastSharp       = false;
        var _mt3LastFlat        = false;
        var _mt3LastTime      = null;
        var _mt3StrobeOffset  = 0;

        // Tick state: 0=dim, 1=spill, 2=bright  (computed each frame, never persisted between calls)
        var _mt3TickState     = [];
        var _mt3LastTickState = [];
        for (var ti = 0; ti < _TUNER_MT3_TICK_COUNT; ti++) {
            _mt3TickState.push(0);
            _mt3LastTickState.push(-1);   // -1 = unrendered, forces first paint
        }

        // ── Segment helpers ───────────────────────────────────────────
        function _setSegment(el, lit) {
            var brt = _MT3_BRIGHTNESS[_mt3BrightnessIdx];
            el.setAttribute('fill', lit ? brt.litFill : _MT3_COL_SEG_UNLIT);
            el.style.filter = lit ? brt.glow : 'drop-shadow(0 0 0px transparent)';
        }
        function _renderNote(letter) {
            var map = _TUNER_MT3_SEGMENTS[letter] || _TUNER_MT3_SEGMENTS[' '];
            for (var k = 0; k < _segKeys.length; k++) { _setSegment(_mt3SegEls[_segKeys[k]], map[k]); }
        }
        function _setSharp(lit) {
            var brt = _MT3_BRIGHTNESS[_mt3BrightnessIdx];
            for (var p = 0; p < _mt3SharpParts.length; p++) {
                _mt3SharpParts[p].setAttribute('fill', lit ? brt.litFill : _MT3_COL_SEG_UNLIT);
            }
            sharpSvg.style.filter = lit ? brt.glow : 'none';
        }
        function _setFlat(lit) {
            flatSvg.style.display = lit ? '' : 'none';
            if (lit) {
                var brt = _MT3_BRIGHTNESS[_mt3BrightnessIdx];
                _mt3FlatText.setAttribute('fill', brt.litFill);
                flatSvg.style.filter = brt.glow;
            } else {
                flatSvg.style.filter = 'none';
            }
        }
        function _applyAccidental() {
            if (_mt3LastFlat) {
                sharpSvg.style.display = 'none';
                _setFlat(true);
            } else {
                sharpSvg.style.display = '';
                _setSharp(_mt3LastSharp);
                _setFlat(false);
            }
        }

        // ── Tick state helpers ────────────────────────────────────────
        function _clearTickStates() {
            for (var ti = 0; ti < _TUNER_MT3_TICK_COUNT; ti++) { _mt3TickState[ti] = 0; }
        }

        // Never downgrade: a bright centre tick won't be overwritten by a spill from another group
        function _setTickState(idx, level) {
            if (idx < 0 || idx >= _TUNER_MT3_TICK_COUNT) { return; }
            if (level > _mt3TickState[idx]) { _mt3TickState[idx] = level; }
        }

        // Standard mode: 1 bright + ±1 spill
        function _computeStandardStates(cents, hasSignal) {
            _clearTickStates();
            if (!hasSignal) { return; }
            var clamped   = Math.max(-_TUNER_MT3_GAUGE_CENTS, Math.min(_TUNER_MT3_GAUGE_CENTS, cents));
            var targetIdx = Math.round((clamped + _TUNER_MT3_GAUGE_CENTS) /
                (2 * _TUNER_MT3_GAUGE_CENTS / (_TUNER_MT3_TICK_COUNT - 1)));
            targetIdx = Math.max(0, Math.min(_TUNER_MT3_TICK_COUNT - 1, targetIdx));
            _setTickState(targetIdx,     2);
            _setTickState(targetIdx - 1, 1);
            _setTickState(targetIdx + 1, 1);
        }

        // Strobe mode: 3 bright ticks per group + ±1 spill on each outer edge
        function _computeStrobeStates() {
            _clearTickStates();
            for (var g = 0; g < _TUNER_MT3_STROBE_GROUP_COUNT; g++) {
                var baseAngle = _MT3_ARC_START + (g / _TUNER_MT3_STROBE_GROUP_COUNT) * _MT3_ARC_SPAN + _mt3StrobeOffset;
                var relAngle  = ((baseAngle - _MT3_ARC_START) % _MT3_ARC_SPAN + _MT3_ARC_SPAN) % _MT3_ARC_SPAN;
                var nearIdx   = Math.max(0, Math.min(_TUNER_MT3_TICK_COUNT - 1,
                    Math.round(relAngle / _MT3_ARC_SPAN * (_TUNER_MT3_TICK_COUNT - 1))));
                // 3 bright ticks centred at nearIdx
                _setTickState(nearIdx - 1, 2);
                _setTickState(nearIdx,     2);
                _setTickState(nearIdx + 1, 2);
                // Lightspill beyond the cluster
                _setTickState(nearIdx - 2, 1);
                _setTickState(nearIdx + 2, 1);
            }
        }

        // Apply current tick states to the glowGroup DOM — only write changed ticks
        function _applyTickStates() {
            for (var ti = 0; ti < _TUNER_MT3_TICK_COUNT; ti++) {
                var state = _mt3TickState[ti];
                if (state === _mt3LastTickState[ti]) { continue; }
                _mt3LastTickState[ti] = state;
                var el = _mt3GlowTickEls[ti];
                if (state === 2) {
                    el.setAttribute('stroke', _mt3TickColors[ti]);
                    el.setAttribute('stroke-width', '3');
                } else if (state === 1) {
                    el.setAttribute('stroke', _mt3TickSpillColors[ti]);
                    el.setAttribute('stroke-width', '2');
                } else {
                    el.setAttribute('stroke', 'none');
                    el.setAttribute('stroke-width', '1');
                }
            }
        }

        // ── RAF loop ──────────────────────────────────────────────────
        function _animateStrobe(now) {
            if (_mt3LastTime === null) { _mt3LastTime = now; }
            var dt = Math.min((now - _mt3LastTime) / 1000, 0.1);
            _mt3LastTime = now;

            // Smooth cents for strobe drift
            var lerpFactor = 1 - Math.exp(-10 * dt);
            _mt3SmoothedCents += (_mt3CurrentCents - _mt3SmoothedCents) * lerpFactor;

            // Animate glow opacity: fast fade-in (~120ms), slow fade-out (~400ms)
            var opacityTarget = _mt3HasSignal ? 1.0 : 0.0;
            var opacityRate   = _mt3HasSignal ? 8.0 : 2.5;
            _mt3GlowOpacity  += (opacityTarget - _mt3GlowOpacity) * (1 - Math.exp(-opacityRate * dt));
            var _scaledOpacity = Math.min(1, _mt3GlowOpacity * _MT3_BRIGHTNESS[_mt3BrightnessIdx].brightScale);
            _glowGroup.setAttribute('opacity', _scaledOpacity.toFixed(3));

            // Advance strobe offset
            if (_mt3Mode === 'strobe' && Math.abs(_mt3SmoothedCents) > 0.1) {
                var absCents   = Math.min(_TUNER_MT3_GAUGE_CENTS, Math.abs(_mt3SmoothedCents));
                var normalized = Math.max(0, absCents - _TUNER_MT3_IN_TUNE_THR) / (_TUNER_MT3_GAUGE_CENTS - _TUNER_MT3_IN_TUNE_THR);
                var speed      = _MT3_ARC_SPAN * Math.pow(normalized, 0.9);
                if (_mt3SmoothedCents < 0) { speed = -speed; }
                _mt3StrobeOffset = ((_mt3StrobeOffset + speed * dt) % _MT3_ARC_SPAN + _MT3_ARC_SPAN) % _MT3_ARC_SPAN;
            }

            // Recompute and apply strobe tick states each frame
            if (_mt3Mode === 'strobe') { _computeStrobeStates(); }

            _applyTickStates();

            // No signal and both the glow and strobe drift have fully settled →
            // idle the loop. update() re-kicks it on the next note.
            if (!_mt3HasSignal && _mt3GlowOpacity < 0.004
                    && Math.abs(_mt3SmoothedCents) <= 0.1) {
                _mt3RafId = null;
                _mt3LastTime = null;
                return;
            }
            _mt3RafId = requestAnimationFrame(_animateStrobe);
        }
        function _kick() {
            if (_mt3RafId === null) { _mt3LastTime = null; _mt3RafId = requestAnimationFrame(_animateStrobe); }
        }
        _mt3RafId = requestAnimationFrame(_animateStrobe);

        // ── MODE button ───────────────────────────────────────────────
        _mt3ModeBtn.addEventListener('click', function () {
            _mt3ModeBtn.style.boxShadow = 'inset 0 2px 4px rgba(0,0,0,0.9)';
            setTimeout(function () {
                _mt3ModeBtn.style.boxShadow = 'inset 0 1px 2px rgba(255,255,255,0.08), 0 2px 3px rgba(0,0,0,0.7)';
            }, 120);
            if (_mt3Mode === 'standard') {
                _mt3Mode = 'strobe';
                _mt3StrobeOffset = 0;
                _clearTickStates();
                _mt3StrobeLed.style.background = 'radial-gradient(circle at 35% 35%, #ff4444, #cc0000)';
                _mt3StrobeLed.style.boxShadow  = '0 0 4px 2px #ff2200, 0 0 8px 3px #880000';
            } else {
                _mt3Mode = 'standard';
                _computeStandardStates(_mt3CurrentCents, _mt3HasSignal);
                _mt3StrobeLed.style.background = 'radial-gradient(circle at 35% 35%, #2a0000, #0a0000)';
                _mt3StrobeLed.style.boxShadow  = 'none';
            }
        });

        // ── BRGHT button — cycle low/medium/high brightness ──────────
        brightBtn.addEventListener('click', function () {
            brightBtn.style.boxShadow = 'inset 0 2px 4px rgba(0,0,0,0.9)';
            setTimeout(function () {
                brightBtn.style.boxShadow = 'inset 0 1px 2px rgba(255,255,255,0.08), 0 2px 3px rgba(0,0,0,0.7)';
            }, 120);
            _mt3BrightnessIdx = (_mt3BrightnessIdx + 1) % _MT3_BRIGHTNESS.length;
            // Update SVG tick glow filter intensity
            _sfFlood.setAttribute('flood-opacity', _MT3_BRIGHTNESS[_mt3BrightnessIdx].floodOpacity);
            // Re-render segment display with new brightness
            _renderNote(_mt3LastLetter);
            _applyAccidental();
        });

        // ── Public: update ────────────────────────────────────────────
        function update(note, cents) {
            var hasNote = (note !== null && note !== undefined);
            _mt3HasSignal    = hasNote;
            _mt3CurrentCents = hasNote ? (cents || 0) : 0;
            if (_mt3Mode === 'standard') { _computeStandardStates(_mt3CurrentCents, hasNote); }
            if (hasNote) {
                _mt3LastLetter = note.charAt(0);
                _mt3LastSharp  = note.charAt(1) === '#';
                _mt3LastFlat   = note.charAt(1) === 'b';
                _renderNote(_mt3LastLetter);
                _applyAccidental();
            } else {
                _mt3LastLetter = ' ';
                _mt3LastSharp  = false;
                _mt3LastFlat   = false;
                _renderNote(' ');
                _applyAccidental();
            }
            if (hasNote) { _kick(); }   // new signal → restart the strobe loop if idled
        }

        // ── Public: destroy ───────────────────────────────────────────
        function destroy() {
            if (_mt3RafId) { cancelAnimationFrame(_mt3RafId); _mt3RafId = null; }
            panel.remove();
        }

        return { update: update, destroy: destroy };
    };
})();
