(() => {
  'use strict';

  // -------- helpers ----------
  const log  = (...a) => console.log('[Classic]', ...a);
  const fail = (msg) => {
    console.error('[Classic ERROR]', msg);
    const d = document.getElementById('classic-display');
    if (d) d.innerHTML = `<div style="padding:1rem;color:#b91c1c;font-family:system-ui,Arial">
      <strong>Classic failed:</strong> ${String(msg)}
    </div>`;
  };
  const normHex = (h) => (String(h || '')).trim().toLowerCase();

  // -------- persistent color selection (now supports image textures) ----------
  const PALETTE_KEY = 'classic:colors:v2';
  const TOPPER_COLOR_KEY = 'classic:topperColor:v2';

  const MAX_SLOTS = 20;
  const SLOT_COUNT_KEY = 'classic:slotCount:v1';
  const defaultColors = () => [
    { hex: '#d92e3a', image: null }, { hex: '#ffffff', image: null },
    { hex: '#0055a4', image: null }, { hex: '#40e0d0', image: null },
    { hex: '#fcd34d', image: null }
  ];
  const defaultTopper = () => ({ hex: '#a18b67', image: 'images/chrome-gold.webp' });

  function getClassicColors() {
    let arr = defaultColors();
    try {
      const savedJSON = localStorage.getItem(PALETTE_KEY);
      if (!savedJSON) return arr;
      const saved = JSON.parse(savedJSON);
      if (Array.isArray(saved) && saved.length > 0) {
        if (typeof saved[0] === 'string') {
          arr = saved.slice(0, MAX_SLOTS).map(hex => ({ hex: normHex(hex), image: null }));
        } else if (typeof saved[0] === 'object' && saved[0] !== null) {
          arr = saved.slice(0, MAX_SLOTS);
        }
      }
      while (arr.length < 5) arr.push({ hex: '#ffffff', image: null });
      if (arr.length > MAX_SLOTS) arr = arr.slice(0, MAX_SLOTS);
    } catch (e) { console.error('Failed to parse classic colors:', e); }
    return arr;
  }

  function setClassicColors(arr) {
    const clean = (arr || []).slice(0, MAX_SLOTS).map(c => ({
      hex: normHex(c.hex), image: c.image || null
    }));
    while (clean.length < 5) clean.push({ hex: '#ffffff', image: null });
    try { localStorage.setItem(PALETTE_KEY, JSON.stringify(clean)); } catch {}
    return clean;
  }

  function getTopperColor() {
    try {
      const saved = JSON.parse(localStorage.getItem(TOPPER_COLOR_KEY));
      return (saved && saved.hex) ? saved : defaultTopper();
    } catch { return defaultTopper(); }
  }

  function setTopperColor(colorObj) {
    const clean = { hex: normHex(colorObj.hex), image: colorObj.image || null };
    try { localStorage.setItem(TOPPER_COLOR_KEY, JSON.stringify(clean)); } catch {}
  }

  function buildClassicPalette() {
    const colors = getClassicColors();
    const palette = { 0: { colour: '#FFFFFF', name: 'No Colour', image: null } };
    colors.forEach((c, i) => {
      palette[i + 1] = { colour: c.hex, image: c.image };
    });
    return palette;
  }

  function flattenPalette() {
    const out = [];
    if (Array.isArray(window.PALETTE)) {
      window.PALETTE.forEach(group => {
        (group.colors || []).forEach(c => {
          if (!c?.hex) return;
          out.push({
            hex: normHex(c.hex), name: c.name || c.hex,
            family: group.family || '', image: c.image || null
          });
        });
      });
    }
    const seen = new Set();
    return out.filter(c => (seen.has(c.hex) ? false : (seen.add(c.hex), true)));
  }

  // -------- tiny grid engine (Mithril) ----------
  function GridCalculator() {
    if (typeof window.m === 'undefined') throw new Error('Mithril (m) not loaded');

    let pxUnit = 10;
    let clusters = 10;
    let reverse  = false;
    let topperEnabled = false;
    let topperType = 'round';
    let topperOffsetX_Px = 0;
    let topperOffsetY_Px = 0;
    let topperSizeMultiplier = 1;
    let shineEnabled = true;

    const patterns = {};
    const api = {
      patterns,
      initialPattern: 'Arch 4',
      controller: (el) => makeController(el),
      setClusters(n) { clusters = Math.max(1, (Number(n)|0) || 10); },
      setReverse(on){ reverse = !!on; },
      setTopperEnabled(on) { topperEnabled = !!on; },
      setTopperType(type) { topperType = type || 'round'; },
      setTopperOffsetX(val) { topperOffsetX_Px = (Number(val) || 0) * 5; },
      setTopperOffsetY(val) { topperOffsetY_Px = (Number(val) || 0) * -5; },
      setTopperSize(multiplier) { topperSizeMultiplier = Number(multiplier) || 1; },
      setShineEnabled(on) { shineEnabled = !!on; }
    };

    const svg = (tag, attrs, children) => m(tag, attrs, children);

    function extend(p){
      const parentName = p.deriveFrom; if (!parentName) return;
      const base = patterns[parentName]; if (!base) return;
      if (base.deriveFrom) extend(base);
      Object.keys(base).forEach(k => { if (!(k in p)) p[k] = base[k]; });
      p.parent = base;
    }

    function BBox(){ this.min={x:Infinity,y:Infinity}; this.max={x:-Infinity,y:-Infinity}; }
    BBox.prototype.add = function(x,y){ if(isNaN(x)||isNaN(y)) return this;
      this.min.x=Math.min(this.min.x,x); this.min.y=Math.min(this.min.y,y);
      this.max.x=Math.max(this.max.x,x); this.max.y=Math.max(this.max.y,y); return this; };
    BBox.prototype.w=function(){return this.max.x-this.min.x;};
    BBox.prototype.h=function(){return this.max.y-this.min.y;};

    const balloonSize = (cell)=> (cell.shape.size ?? 1);
    const cellScale   = (cell)=> balloonSize(cell) * pxUnit;

    function cellView(cell, id, explicitFill, model){
        const shape = cell.shape;
        const scale = cellScale(cell);
        const transform = [(shape.base.transform||''), `scale(${scale})`].join(' ');
        const commonAttrs = {
            'vector-effect': 'non-scaling-stroke', stroke: '#111827',
            'stroke-width': 2, 'paint-order': 'stroke fill', class: 'balloon',
            fill: explicitFill || '#cccccc'
        };
        if (cell.isTopper) {
            commonAttrs['data-is-topper'] = true;
        } else {
            commonAttrs['data-color-code'] = cell.colorCode || 0;
            commonAttrs['data-quad-number'] = cell.y + 1;
        }

        let shapeEl;
        if (shape.base.type === 'path') shapeEl = svg('path', { ...commonAttrs, d: shape.base.d });
        else shapeEl = svg('ellipse', { ...commonAttrs, cx:0, cy:0, rx:0.5, ry:0.5 });

        const kids = [shapeEl];
        const applyShine = model.shineEnabled && (!cell.isTopper || (cell.isTopper && model.topperType === 'round'));
        if (applyShine) {
            kids.push(svg('ellipse', {
                class: 'shine', cx: -0.15, cy: -0.15, rx: 0.22, ry: 0.13,
                fill: '#ffffff', opacity: 0.45, transform: 'rotate(-25)', 'pointer-events': 'none'
            }));
        }
        return svg('g', { id, transform }, kids);
    }

    function gridPos(x,y,z,inflate,pattern,model){
      const base = patterns[model.patternName].parent || patterns[model.patternName];
      const rel  = (pattern.baseBalloonSize && base.baseBalloonSize) ? pattern.baseBalloonSize/base.baseBalloonSize : 1;
      let p = { x: pattern.gridX(model.pattern.cellsPerRow > 1 ? y : x, x), y: pattern.gridY(y,x) };
      if (pattern.transform) p = pattern.transform(p,x,y,model);
      return { x: p.x * rel * pxUnit, y: p.y * rel * pxUnit };
    }

    // === Spiral coloring helpers (shared by 4- and 5-balloon clusters) ===
function distinctPaletteSlots(palette) {
  // Collapse visually identical slots so 3-color spirals work even if you filled 5 slots.
  const seen = new Set(), out = [];
  for (let s = 1; s <= 5; s++) {
    const c = palette[s];
    if (!c) continue;
    const key = (c.image || '') + '|' + String(c.colour || '').toLowerCase();
    if (!seen.has(key)) { seen.add(key); out.push(s); }
  }
  return out.length ? out : [1,2,3,4,5];
}



    function newGrid(pattern, cells, container, model){
      const kids = [], layers = [], bbox = new BBox();
      const balloonsPerCluster = pattern.balloonsPerCluster || 4;
      const reversed = !!(pattern._reverse || (pattern.parent && pattern.parent._reverse));
      const rowColorPatterns = {};
      const stackedSlots = (() => {
        const slots = distinctPaletteSlots(model.palette);
        const limit = Math.max(1, Math.min(slots.length, balloonsPerCluster));
        return slots.slice(0, limit);
      })();

      const colorBlock4 = [[1, 2, 3, 4], [3, 1, 4, 2], [4, 3, 2, 1], [2, 4, 1, 3]];
      const colorBlock5 = 
      [
        [5, 2, 3, 4, 1], 
        [2, 3, 4, 5, 1], 
        [2, 4, 5, 1, 3], 
        [4, 5, 1, 2, 3], 
        [4, 1, 2, 3, 5],
      ];

      for (let cell of cells) {
          let c, fill;
          if (cell.isTopper) {
              const topRowYIndex = 0, topClusterY = pattern.gridY(topRowYIndex, 0) * pxUnit;
              const regularBalloonRadius = (pattern.balloonShapes['front'] || pattern.balloonShapes['penta'] || pattern.balloonShapes['middle']).size * pxUnit * 0.5;
              const highestPoint = topClusterY - regularBalloonRadius;
              const topperRadius = cell.shape.size * pxUnit * cell.shape.base.radius;
              const topperY = highestPoint - topperRadius - (pxUnit * 0.5) + topperOffsetY_Px;
              c = { x: topperOffsetX_Px, y: topperY };
              fill = model.topperColor.image ? `url(#classic-pattern-topper)` : model.topperColor.hex;
          } else {
              c = gridPos(cell.x, cell.y, cell.shape.zIndex, cell.inflate, pattern, model);
              
              const rowIndex = cell.y;
              if (!rowColorPatterns[rowIndex]) {
                  const totalRows = model.rowCount * (pattern.cellsPerRow || 1);
                  const isRightHalf = false; // mirror mode removed
                  const baseRow = rowIndex;
                  const qEff = baseRow + 1;
                  let pat;

                  if (pattern.colorMode === 'stacked') {
                      const slot = stackedSlots[(rowIndex) % stackedSlots.length] || stackedSlots[0] || 1;
                      pat = new Array(balloonsPerCluster).fill(slot);
                  } else if (balloonsPerCluster === 5) {
                      const base = (qEff - 1) % 5;
                      pat = colorBlock5[base].slice();
                  } else {
                      const base = Math.floor((qEff - 1) / 2);
                      pat = colorBlock4[base % 4].slice();
                      if (qEff % 2 === 0) {
                          pat = [pat[0], pat[2], pat[1], pat[3]];
                      }
                  }
                  
                  // Swap left/right emphasis every 5 clusters to break repetition (per template override)
                  if (balloonsPerCluster === 5) {
                    const SWAP_EVERY = 5;
                    const blockIndex = Math.floor(rowIndex / SWAP_EVERY);
                    if (blockIndex % 2 === 1) {
                      [pat[0], pat[4]] = [pat[4], pat[0]];
                    }
                  }

                  if (pat.length > 1) {
                      let shouldReverse;
                      shouldReverse = reversed;
                      if (shouldReverse) pat.reverse();
                  }

                  rowColorPatterns[rowIndex] = pat;
              }

              const colorCode = rowColorPatterns[rowIndex][cell.balloonIndexInCluster];
              cell.colorCode = colorCode;
              const colorInfo = model.palette[colorCode];
              fill = colorInfo ? (colorInfo.image ? `url(#classic-pattern-slot-${colorCode})` : colorInfo.colour) : 'transparent';
          }

          const scale = cellScale(cell), shapeRadius = cell.shape.base.radius || 0.5, size = shapeRadius * scale;
          bbox.add(c.x - size, c.y - size);
          bbox.add(c.x + size, c.y + size);
          const v = cellView(cell, `balloon_${cell.x}_${cell.y}`, fill, model);
          v.attrs.transform = `translate(${c.x},${c.y}) ${v.attrs.transform || ''}`;
          const zi = cell.isTopper ? 100 + 2 : (100 + (cell.shape.zIndex || 0));
          (layers[zi] ||= []).push(v);
      };

      layers.forEach(layer => layer && layer.forEach(v => kids.push(v)));
      const margin = 20;
      const vb = [ bbox.min.x - margin, bbox.min.y - margin, Math.max(1,bbox.w()) + margin*2, Math.max(1,bbox.h()) + margin*2 ].join(' ');
      
      const patternsDefs = [];
      const SVG_PATTERN_ZOOM = 2.5;
      const offset = (1 - SVG_PATTERN_ZOOM) / 2;

      Object.entries(model.palette).forEach(([slot, colorInfo]) => {
          if (colorInfo.image) {
              patternsDefs.push(svg('pattern', {id: `classic-pattern-slot-${slot}`, patternContentUnits: 'objectBoundingBox', width: 1, height: 1}, 
                [ svg('image', { href: colorInfo.image, x: offset, y: offset, width: SVG_PATTERN_ZOOM, height: SVG_PATTERN_ZOOM, preserveAspectRatio: 'xMidYMid slice' }) ]
              ));
          }
      });
      if (model.topperColor.image) {
          patternsDefs.push(svg('pattern', {id: 'classic-pattern-topper', patternContentUnits: 'objectBoundingBox', width: 1, height: 1}, 
            [ svg('image', { href: model.topperColor.image, x: offset, y: offset, width: SVG_PATTERN_ZOOM, height: SVG_PATTERN_ZOOM, preserveAspectRatio: 'xMidYMid slice' }) ]
          ));
      }
      const svgDefs = svg('defs', {}, patternsDefs);

      const mainGroup = svg('g', null, kids);
      m.render(container, svg('svg', { xmlns: 'http://www.w3.org/2000/svg', width:'100%', height:'100%', viewBox: vb, preserveAspectRatio:'xMidYMid meet', style: 'isolation:isolate' }, [svgDefs, mainGroup]));
    }

    function makeController(displayEl){
      const models = [];
      function buildModel(name){
        const pattern = patterns[name];
        if (patterns['Column 4']) patterns['Column 4']._reverse = reverse;
        if (patterns['Arch 4'])   patterns['Arch 4']._reverse   = reverse;
        if (patterns['Column 5']) patterns['Column 5']._reverse = reverse;
        if (patterns['Arch 5'])   patterns['Arch 5']._reverse   = reverse;

        const model = { patternName: name, pattern, cells: [], rowCount: clusters, palette: buildClassicPalette(), topperColor: getTopperColor(), topperType, shineEnabled };
        const rows = pattern.cellsPerRow * model.rowCount, cols = pattern.cellsPerColumn;
        for (let y=0; y<rows; y++){
          let balloonIndexInCluster = 0;
          for (let x=0; x<cols; x++) {
              const cellData = pattern.createCell(x,y);
              if (cellData) model.cells.push({ ...cellData, x, y, balloonIndexInCluster: balloonIndexInCluster++ });
          }
        }
        if (name.toLowerCase().includes('column') && topperEnabled) {
            const shapeName = `topper-${topperType}`;
            const originalShape = pattern.balloonShapes[shapeName];
            if (originalShape) {
                const shape = {...originalShape};
                shape.size *= topperSizeMultiplier;
                model.cells.push({ isTopper: true, shape, inflate: 0, x:0, y:rows });
            }
        }
        return model;
      }
      function selectPattern(name){
        const m = buildModel(name); models.push(m);
        newGrid(m.pattern, m.cells, displayEl, m); return m;
      }
      return { selectPattern };
    }

    function roundedStarPath({ points = 5, outerR = 0.5, innerR = 0.22, round = 0.28, rotate = -90 }) {
      const toRad = Math.PI / 180; const rot = rotate * toRad; const verts = [];
      for (let i = 0; i < points * 2; i++) { const ang = rot + i * Math.PI / points; const R = (i % 2 === 0) ? outerR : innerR; verts.push([Math.cos(ang) * R, Math.sin(ang) * R]); }
      const t = Math.max(0, Math.min(0.49, round));
      const lerp = (a, b, u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u];
      let v0 = verts[0], v1 = verts[1]; let p0 = lerp(v0, v1, t);
      let d = `M ${p0[0].toFixed(4)} ${p0[1].toFixed(4)}`;
      for (let i = 0; i < verts.length; i++) { const v = verts[(i + 1) % verts.length], vNext = verts[(i + 2) % verts.length]; const p = lerp(v, vNext, t); d += ` Q ${v[0].toFixed(4)} ${v[1].toFixed(4)} ${p[0].toFixed(4)} ${p[1].toFixed(4)}`; }
      return d + ' Z';
    }

    // --- Column 4: This is the existing logic from classic.js, which matches your template file ---
    patterns['Column 4'] = {
      baseBalloonSize: 25, _reverse: false, balloonsPerCluster: 4,
      balloonShapes: {
        'front':{zIndex:4, base:{radius:0.5}, size:3}, 'front-inner':{zIndex:3, base:{radius:0.5}, size:3}, 'back-inner':{zIndex:2, base:{radius:0.5}, size:3}, 'back':{zIndex:1, base:{radius:0.5}, size:3},
        'topper-round':{base:{type:'ellipse', radius:0.5}, size:8}, 'topper-star':{base:{type:'path', d:roundedStarPath({}), radius:0.5}, size:8}, 'topper-heart':{base:{type:'path', d:'M0,0.35 C-0.5,0, -0.14,-0.35, 0,-0.14 C0.14,-0.35, 0.5,0, 0,0.35 Z', radius:0.5}, size:20}
      },
      tile: { size:{x:5,y:1} }, cellsPerRow: 1, cellsPerColumn: 5,
      gridX(row, col){ return col + [0, -0.12, -0.24, -0.36, -0.48][col % 5]; },
      gridY(row, col){ return 2.2 * (1 - 1/5) * (Math.floor(row/2) + Math.floor((row+1)/2)); },
      createCell(x, y) {
        const odd = !!(y % 2);
        const A = ['front-inner','back','', 'front','back-inner'], B = ['back-inner', 'front','', 'back', 'front-inner'];
        const arr = this._reverse ? (odd ? B : A) : (odd ? A : B);
        const shapeName = arr[x % 5];
        const shape = this.balloonShapes[shapeName];
        return shape ? { shape:{...shape} } : null;
      }
    };
    
    // --- Arch 4: This is the existing logic from classic.js, which matches your template file ---
    patterns['Arch 4'] = { 
      deriveFrom: 'Column 4', 
      transform(point, col, row, model){ 
        const len = this.gridY(model.rowCount*this.tile.size.y, 0) - this.gridY(0, 0); 
        const r = (len / Math.PI) + point.x;
        const y = point.y - this.gridY(0, 0);
        const a = Math.PI * (y / len); 
        return { x: -r*Math.cos(a), y: -r*Math.sin(a) }; 
      } 
    };
    // --- Column 5 (template geometry) ---
    patterns['Column 5'] = {
      baseBalloonSize: 25,
      _reverse: false,
      balloonsPerCluster: 5,
      tile: { size: { x: 5, y: 1 } },
      cellsPerRow: 1,
      cellsPerColumn: 5,
      balloonShapes: {
        "front":   { zIndex:5, base:{radius:0.5}, size:3.0 },
        "front2":  { zIndex:4, base:{radius:0.5}, size:3.0 },
        "middle":  { zIndex:3, base:{radius:0.5}, size:3.0 },
        "middle2": { zIndex:2, base:{radius:0.5}, size:3.0 },
        "back":    { zIndex:1, base:{radius:0.5}, size:3.0 },
        "back2":   { zIndex:0, base:{radius:0.5}, size:3.0 },
        'topper-round':{base:{type:'ellipse', radius:0.5}, size:8},
        'topper-star':{base:{type:'path', d:roundedStarPath({}), radius:0.5}, size:8},
        'topper-heart':{base:{type:'path', d:'M0,0.35 C-0.5,0, -0.14,-0.35, 0,-0.14 C0.14,-0.35, 0.5,0, 0,0.35 Z', radius:0.5}, size:20}
      },
      gridX(row, col) {
        var mid = 0.6;
        return (0.9) * (col + (0 === col % 5 && -0.5) + (1 === col % 5 && -mid) + (3 === col % 5 && mid) + (4 === col % 5 && 0.5) - 0.5);
      },
      gridY(row, col){ 
        return 2.2 * (1 - 1/5) * (Math.floor(row/2) + Math.floor((row+1)/2)); 
      },
      createCell(x, y) {
        var yOdd = !!(y % 2);
        const shapePattern = yOdd
          ? ['middle', 'back', 'front', 'back', 'middle']
          : ['middle2', 'front2', 'back2', 'front2', 'middle2'];
        var shapeName = shapePattern[x % 5];
        var shape = this.balloonShapes[shapeName];
        return shape ? { shape: {...shape} } : null;
      }
    };
    
    // Arch 5 derives from Column 5
    patterns['Arch 5'] = {
      deriveFrom: 'Column 5',
      transform(point, col, row, model){
        const len = this.gridY(model.rowCount * this.tile.size.y, 0) - this.gridY(0, 0);
        const r   = (len / Math.PI) + point.x;
        const y   = point.y - this.gridY(0, 0);
        const a   = Math.PI * (y / len);
        return { x: -r * Math.cos(a), y: -r * Math.sin(a) };
      }
    };
    // --- END: MODIFIED SECTION ---

    // --- Stacked variants (same geometry, single-color clusters alternating rows) ---
    patterns['Arch 4 Stacked'] = { deriveFrom: 'Arch 4', colorMode: 'stacked' };
    patterns['Arch 5 Stacked'] = { deriveFrom: 'Arch 5', colorMode: 'stacked' };
    patterns['Column 4 Stacked'] = { deriveFrom: 'Column 4', colorMode: 'stacked' };
    patterns['Column 5 Stacked'] = { deriveFrom: 'Column 5', colorMode: 'stacked' };


    Object.keys(patterns).forEach(n => extend(patterns[n]));
    return api;
  }

  const patternSlotCount = (name) => ((name || '').includes('5') ? 5 : 4);
  function getStoredSlotCount() {
      try {
        const saved = parseInt(localStorage.getItem(SLOT_COUNT_KEY), 10);
        if (Number.isFinite(saved) && saved > 0) return Math.min(saved, MAX_SLOTS);
      } catch {}
      return 5;
  }
  function setStoredSlotCount(n) {
      const v = Math.max(1, Math.min(MAX_SLOTS, n|0));
      try { localStorage.setItem(SLOT_COUNT_KEY, String(v)); } catch {}
      return v;
  }

  function initClassicColorPicker(onColorChange) {
      const slotsContainer = document.getElementById('classic-slots'), topperSwatch = document.getElementById('classic-topper-color-swatch'), swatchGrid = document.getElementById('classic-swatch-grid'), activeLabel = document.getElementById('classic-active-label'), randomizeBtn = document.getElementById('classic-randomize-colors'), addSlotBtn = document.getElementById('classic-add-slot');
      if (!slotsContainer || !topperSwatch || !swatchGrid || !activeLabel) return;
      topperSwatch.classList.add('tab-btn');
      let classicColors = getClassicColors(), activeTarget = '1', slotCount = getStoredSlotCount();

      function visibleSlotCount() {
        const patSelect = document.getElementById('classic-pattern');
        const name = patSelect?.value || 'Arch 4';
        const baseCount = patternSlotCount(name);
        const isStacked = (name || '').toLowerCase().includes('stacked');
        if (!isStacked) return baseCount;
        const lengthInp = document.getElementById('classic-length-ft');
        const clusters = Math.max(1, Math.round((parseFloat(lengthInp?.value) || 0) * 2));
        const maxSlots = Math.min(MAX_SLOTS, clusters);
        return Math.min(Math.max(baseCount, slotCount), maxSlots);
      }

      function renderSlots() {
        slotsContainer.innerHTML = '';
        const count = visibleSlotCount();
        for (let i = 1; i <= count; i++) {
          const btn = document.createElement('button');
          btn.type = 'button';
          btn.className = 'slot-btn tab-btn';
          btn.dataset.slot = String(i);
          btn.textContent = `#${i}`;
          btn.addEventListener('click', () => { activeTarget = String(i); updateUI(); });
          slotsContainer.appendChild(btn);
        }
      }

      function enforceSlotVisibility() {
        const count = visibleSlotCount();
        if (parseInt(activeTarget, 10) > count) activeTarget = '1';
        renderSlots();
      }

      function updateUI() {
          enforceSlotVisibility();
          const buttons = Array.from(slotsContainer.querySelectorAll('.slot-btn'));
          [...buttons, topperSwatch].forEach(el => { const id = el.dataset.slot || 'T'; el.classList.toggle('tab-active', activeTarget === id); el.classList.toggle('tab-idle', activeTarget !== id); });
          
          buttons.forEach((slot, i) => { 
            const color = classicColors[i]; 
            if (!color) return; // Safeguard against errors
            slot.style.backgroundImage = color.image ? `url("${color.image}")` : 'none';
            slot.style.backgroundColor = color.hex;
            slot.style.backgroundSize = '200%';
            slot.style.backgroundPosition = 'center';
          });
          
          const topperColor = getTopperColor(); 
          topperSwatch.style.backgroundImage = topperColor.image ? `url("${topperColor.image}")` : 'none';
          topperSwatch.style.backgroundColor = topperColor.hex;
          topperSwatch.style.backgroundSize = '200%';
          topperSwatch.style.backgroundPosition = 'center';

          const patSelect = document.getElementById('classic-pattern');
          const isStacked = (patSelect?.value || '').toLowerCase().includes('stacked');
          if (addSlotBtn) {
            const lengthInp = document.getElementById('classic-length-ft');
            const clusters = Math.max(1, Math.round((parseFloat(lengthInp?.value) || 0) * 2));
            const maxSlots = Math.min(MAX_SLOTS, clusters);
            addSlotBtn.classList.toggle('hidden', !isStacked);
            addSlotBtn.disabled = !isStacked || slotCount >= maxSlots;
          }

          activeLabel.textContent = activeTarget === 'T' ? 'Topper' : `Slot #${activeTarget}`;
      }

      const allPaletteColors = flattenPalette(); swatchGrid.innerHTML = '';
      (window.PALETTE || []).forEach(group => {
          const title = document.createElement('div'); title.className = 'family-title'; title.textContent = group.family; swatchGrid.appendChild(title);
          const row = document.createElement('div'); row.className = 'swatch-row';
          (group.colors || []).forEach(colorItem => {
              const sw = document.createElement('button'); sw.type = 'button'; sw.className = 'swatch'; sw.title = colorItem.name;
              sw.setAttribute('aria-label', colorItem.name);
              
              sw.style.backgroundImage = colorItem.image ? `url("${colorItem.image}")` : 'none';
              sw.style.backgroundColor = colorItem.hex;
              sw.style.backgroundSize = '500%';
              sw.style.backgroundPosition = 'center';

              sw.addEventListener('click', () => {
                  const selectedColor = { hex: colorItem.hex, image: colorItem.image };
                  if (activeTarget === 'T') setTopperColor(selectedColor);
                  else {
                      const index = parseInt(activeTarget, 10) - 1;
                      if (index >= 0 && index < MAX_SLOTS) { classicColors[index] = selectedColor; setClassicColors(classicColors); }
                  }
                  updateUI(); onColorChange();
                  if (window.updateExportButtonVisibility) window.updateExportButtonVisibility();
              });
              row.appendChild(sw);
          });
          swatchGrid.appendChild(row);
      });
      topperSwatch.addEventListener('click', () => { activeTarget = 'T'; updateUI(); });
      randomizeBtn?.addEventListener('click', () => {
          const pool = allPaletteColors.slice(); const picks = [];
          const colorCount = visibleSlotCount();
          for (let i = 0; i < colorCount && pool.length; i++) { picks.push(pool.splice(Math.floor(Math.random() * pool.length), 1)[0]); }
          classicColors = setClassicColors(picks.map(c => ({ hex: c.hex, image: c.image })));
          updateUI(); onColorChange();
          if (window.updateExportButtonVisibility) window.updateExportButtonVisibility();
      });
      addSlotBtn?.addEventListener('click', () => {
          const patSelect = document.getElementById('classic-pattern');
          const name = patSelect?.value || '';
          const isStacked = name.toLowerCase().includes('stacked');
          if (!isStacked) return;
          const lengthInp = document.getElementById('classic-length-ft');
          const clusters = Math.max(1, Math.round((parseFloat(lengthInp?.value) || 0) * 2));
          const maxSlots = Math.min(MAX_SLOTS, clusters);
          if (slotCount >= maxSlots) return;
          slotCount = setStoredSlotCount(slotCount + 1);
          while (classicColors.length < slotCount) {
            const fallback = allPaletteColors[Math.floor(Math.random() * allPaletteColors.length)] || { hex: '#ffffff', image: null };
            classicColors.push({ hex: fallback.hex, image: fallback.image });
          }
          setClassicColors(classicColors);
          updateUI(); onColorChange();
          if (window.updateExportButtonVisibility) window.updateExportButtonVisibility();
      });
      updateUI();
  }

  function initClassic() {
    try {
      if (typeof window.m === 'undefined') return fail('Mithril not loaded');
      const display = document.getElementById('classic-display'), patSel = document.getElementById('classic-pattern'), lengthInp = document.getElementById('classic-length-ft'), clusterHint = document.getElementById('classic-cluster-hint'), reverseCb = document.getElementById('classic-reverse'), topperControls = document.getElementById('topper-controls'), topperToggleRow = document.getElementById('classic-topper-toggle-row'), topperEnabledCb = document.getElementById('classic-topper-enabled'), topperSizeInp = document.getElementById('classic-topper-size'), shineEnabledCb = document.getElementById('classic-shine-enabled');
      const patternShapeBtns = Array.from(document.querySelectorAll('[data-pattern-shape]'));
      const patternCountBtns = Array.from(document.querySelectorAll('[data-pattern-count]'));
      const patternLayoutBtns = Array.from(document.querySelectorAll('[data-pattern-layout]'));
      const topperNudgeBtns = Array.from(document.querySelectorAll('.nudge-topper'));
      const topperTypeButtons = Array.from(document.querySelectorAll('.topper-type-btn'));
      const slotsContainer = document.getElementById('classic-slots');
      let topperOffsetX = 0, topperOffsetY = 0;
      let lastPresetKey = null; // 'custom' means user-tweaked; otherwise `${pattern}:${type}`
      const topperPresets = {
        'Column 4:heart': { enabled: true, offsetX: 3, offsetY: -10.5, size: 1.05 },
        'Column 4:star':  { enabled: true, offsetX: 3, offsetY: -7.5, size: 1.65 },
        'Column 4:round': { enabled: true, offsetX: 3, offsetY: -2, size: 1.25 },
        'Column 5:heart': { enabled: true, offsetX: 2, offsetY: -10, size: 1.15 },
        'Column 5:star':  { enabled: true, offsetX: 2.5, offsetY: -7.5, size: 1.75 },
        'Column 5:round': { enabled: true, offsetX: 2.5, offsetY: -2, size: 1.3 }
      };
      if (!display) return fail('#classic-display not found');
      const GC = GridCalculator(), ctrl = GC.controller(display);

      const getTopperType = () => topperTypeButtons.find(btn => btn.getAttribute('aria-pressed') === 'true')?.dataset.type || 'round';
      const setTopperType = (type) => {
        topperTypeButtons.forEach(btn => {
          const active = btn.dataset.type === type;
          btn.setAttribute('aria-pressed', String(active));
          btn.classList.toggle('tab-active', active);
          btn.classList.toggle('tab-idle', !active);
        });
      };

      function applyTopperPreset(patternName, type) {
        const key = `${patternName}:${type}`;
        const preset = topperPresets[key];
        if (!preset) return;
        if (lastPresetKey === key || lastPresetKey === 'custom') return;
        topperOffsetX = preset.offsetX;
        topperOffsetY = preset.offsetY;
        if (topperSizeInp) topperSizeInp.value = preset.size;
        if (topperEnabledCb) topperEnabledCb.checked = preset.enabled;
        setTopperType(type);
        lastPresetKey = key;
      }

      let patternShape = 'arch', patternCount = 4, patternLayout = 'spiral';
      const computePatternName = () => {
        const base = patternShape === 'column' ? 'Column' : 'Arch';
        const count = patternCount === 5 ? '5' : '4';
        const layout = patternLayout === 'stacked' ? ' Stacked' : '';
        return `${base} ${count}${layout}`;
      };
      const syncPatternStateFromSelect = () => {
        const val = (patSel?.value || '').toLowerCase();
        patternShape = val.includes('column') ? 'column' : 'arch';
        patternCount = val.includes('5') ? 5 : 4;
        patternLayout = val.includes('stacked') ? 'stacked' : 'spiral';
      };
      const applyPatternButtons = () => {
        const setActive = (btns, attr, val) => btns.forEach(b => {
          const active = b.dataset[attr] === val;
          b.classList.toggle('tab-active', active);
          b.classList.toggle('tab-idle', !active);
          b.setAttribute('aria-pressed', String(active));
        });
        setActive(patternShapeBtns, 'patternShape', patternShape);
        setActive(patternCountBtns, 'patternCount', String(patternCount));
        setActive(patternLayoutBtns, 'patternLayout', patternLayout);
      };
      syncPatternStateFromSelect();
      applyPatternButtons();

      function updateClassicDesign() {
        if (!lengthInp || !patSel) return;
        patSel.value = computePatternName();
        const patternName = patSel.value || 'Arch 4';
        const isColumn = patternName.toLowerCase().includes('column');
        const hasTopper = patternName.includes('4') || patternName.includes('5');
        const showToggle = isColumn && hasTopper;
        if (patternName.toLowerCase().includes('column')) {
          const baseName = patternName.includes('5') ? 'Column 5' : 'Column 4';
          applyTopperPreset(baseName, getTopperType());
        }
        if (topperToggleRow) topperToggleRow.classList.toggle('hidden', !showToggle);
        const showTopper = showToggle && topperEnabledCb?.checked;

        topperControls.classList.toggle('hidden', !showTopper);
        
        GC.setTopperEnabled(showTopper); 
        GC.setClusters(Math.round((parseFloat(lengthInp.value) || 0) * 2));
        GC.setReverse(!!reverseCb?.checked); 
        GC.setTopperType(getTopperType()); 
        GC.setTopperOffsetX(topperOffsetX); 
        GC.setTopperOffsetY(topperOffsetY); 
        GC.setTopperSize(topperSizeInp?.value); 
        GC.setShineEnabled(!!shineEnabledCb?.checked);
        if (document.body) {
          if (showTopper) document.body.dataset.topperOverlay = '1';
          else delete document.body.dataset.topperOverlay;
        }
        window.__updateFloatingNudge?.();
        if(clusterHint) clusterHint.textContent = `≈ ${Math.round((parseFloat(lengthInp.value) || 0) * 2)} clusters (rule: 2 clusters/ft)`;
        ctrl.selectPattern(patternName);
      }
      
      const setLengthForPattern = () => {
        if (!lengthInp || !patSel) return;
        const isArch = (computePatternName()).toLowerCase().includes('arch');
        lengthInp.value = isArch ? 20 : 5;
      };
      
      window.ClassicDesigner = window.ClassicDesigner || {};
      window.ClassicDesigner.api = GC;
      window.ClassicDesigner.redraw = updateClassicDesign;
      window.ClassicDesigner.getColors = getClassicColors;
      window.ClassicDesigner.getTopperColor = getTopperColor;

      document.querySelector('#mode-tabs')?.addEventListener('click', () => setTimeout(() => { if (window.updateExportButtonVisibility) window.updateExportButtonVisibility() }, 50));
      patSel?.addEventListener('change', () => { 
        lastPresetKey = null;
        syncPatternStateFromSelect();
        applyPatternButtons();
        setLengthForPattern();
        updateClassicDesign(); 
      });
      patternShapeBtns.forEach(btn => btn.addEventListener('click', () => { patternShape = btn.dataset.patternShape; lastPresetKey = null; applyPatternButtons(); setLengthForPattern(); updateClassicDesign(); }));
      patternCountBtns.forEach(btn => btn.addEventListener('click', () => { patternCount = Number(btn.dataset.patternCount) === 5 ? 5 : 4; lastPresetKey = null; applyPatternButtons(); setLengthForPattern(); updateClassicDesign(); }));
      patternLayoutBtns.forEach(btn => btn.addEventListener('click', () => { patternLayout = btn.dataset.patternLayout === 'stacked' ? 'stacked' : 'spiral'; lastPresetKey = null; applyPatternButtons(); updateClassicDesign(); }));
      topperNudgeBtns.forEach(btn => btn.addEventListener('click', () => {
        const dx = Number(btn.dataset.dx || 0);
        const dy = Number(btn.dataset.dy || 0);
        topperOffsetX += dx;
        topperOffsetY += dy;
        lastPresetKey = 'custom';
        GC.setTopperOffsetX(topperOffsetX);
        GC.setTopperOffsetY(topperOffsetY);
        updateClassicDesign();
      }));
      topperTypeButtons.forEach(btn => btn.addEventListener('click', () => {
        setTopperType(btn.dataset.type);
        lastPresetKey = null;
        updateClassicDesign();
      }));
      [lengthInp, reverseCb, topperEnabledCb, topperSizeInp]
      .forEach(el => { if (!el) return; const eventType = (el.type === 'range' || el.type === 'number') ? 'input' : 'change'; el.addEventListener(eventType, () => { if (el === topperSizeInp || el === topperEnabledCb) lastPresetKey = 'custom'; updateClassicDesign(); }); });
      topperEnabledCb?.addEventListener('change', updateClassicDesign);
      shineEnabledCb?.addEventListener('change', (e) => { const on = !!e.target.checked; GC.setShineEnabled(on); updateClassicDesign(); window.syncAppShine?.(on); });
      initClassicColorPicker(updateClassicDesign);
      try { const saved = localStorage.getItem('app:shineEnabled:v1'); if (saved !== null && shineEnabledCb) shineEnabledCb.checked = JSON.parse(saved); } catch {}
      setLengthForPattern();
      updateClassicDesign();
      if (window.updateExportButtonVisibility) window.updateExportButtonVisibility();
      log('Classic ready');
    } catch (e) { fail(e.message || e); }
  }

  window.ClassicDesigner = window.ClassicDesigner || { init: initClassic, api: null, redraw: null };
  document.addEventListener('DOMContentLoaded', () => { if (document.getElementById('classic-display') && !window.__classicInit) { window.__classicInit = true; initClassic(); } });
})();
