according to some schematics improve the following somehow functional code:


```


# Advanced Schematics for Enhancing Chinese Sexagenary Cycle Dashboard

This schematic focuses on critical improvements you requested: gain stabilization, adaptive layout with fit-to-viewport scaling, and integrated layout with overflow-aware scrollbar consideration.

---

## 1. Gain Stabilization Algorithm for Overlapping Audio Playback

### Problem:  
Multiple overlapping tones played simultaneously at near maximum gain cause clipping and uneven loudness perception. Variable playback speeds change tone durations and overlap amount, further complicating perceptual volume consistency.

### Goal:  
Implement an adaptive gain normalization technique ensuring **all overlapping notes remain equally audible** without clipping and volume pumping, across all playback speeds.

### Conceptual Approach:

- **Dynamic Gain Scaling:**  
  - Track the number of currently active tones at any moment in playback (active voice count over time).  
  - Calculate instantaneous gain reduction based on concurrency: e.g., max gain per tone = `1 / activeVoices` or a nonlinear curve allowing slight overlap without clipping.

- **Preprocessing Overlap Profiles (Offline / Ahead of Play):**  
  - Analyze the entire sequence timing to determine **max polyphony** (maximum simultaneous notes playing at any point).  
  - Use this max polyphony to decide a global gain scale factor for the entire playback (e.g., `maxGain = 1 / maxPolyphony`) ensuring full volume without clipping.

- **Adaptive Volume Envelopes:**  
  - Apply per-sound dynamic volume envelopes that modulate amplitude smoothly based on concurrent voice count transitions, minimizing abrupt jumps.  
  - Use compressor nodes in Web Audio API as a complementary tool, with mild compression ratio and carefully set thresholds.

- **User Volume Control:**  
  - Provide user-accessible master volume control on the global gain node.

---

## 2. Auto-Adjustable Containers for Music Score Log and Grid

### Problem:  
Dashboard UI must scale both the **music score log (a horizontally scrollable textarea or div)** and the **sexagenary cycle grid**, fitting within the viewport smoothly.

### Goal:  
Create **two main containers** with flexible sizing that:

- Scale width and height appropriately within viewport constraints.  
- Maintain aspect ratios and readability.  
- Allow the music score log horizontal scrollbar to appear when content overflows horizontally without pushing or shifting the grid container unpredictably.  
- Prevent vertical overflow and unwanted scrollbars in the main layout.

### Layout Concept:

- **Use a CSS Flex or CSS Grid wrapper** (e.g., a vertical flex container) holding:  
  1. Music Score Log container — height minimally fixed (e.g., ~40–50px), overflow-x scroll enabled but overflow-y hidden.  
  2. Sexagenary Grid container — flex-grow: 1, fills remaining viewport space.

- **Dynamic Height Calculation:**  
  - Detect scrollbar height on the music score log container dynamically at runtime.    
  - Adjust the grid container’s height accordingly (viewport height minus controls height minus scrollbar height).  
  - Use modern CSS `calc()` combined with JavaScript measurement for perfect layout balance.

- **Horizontal Scrolling Behavior:**  
  - Score log container horizontally scrolls independently, does not push grid content.  
  - Use consistent padding and margins so grid’s left edge aligns vertically with score container’s left edge.

---

## 3. Scrollbar Overlay / Layout Shift Avoidance

### Problem:  
When horizontal scrollbar appears on the music score log, it often causes the grid below to shift vertically (due to scrollbar taking up space), which is visually jarring.

### Goal:  
Avoid layout shifts by:

- Using **CSS scrollbar overlay mode** where supported (`scrollbar-gutter` or browser specific CSS) to let the scrollbar render over content without changing layout.  
- Alternatively, design the music score log with **reserved height for scrollbar even when not visible** to stabilize vertical space.  
- JavaScript can detect if scrollbar is present (i.e., if scrollWidth > clientWidth) and then toggle a fixed padding or margin on the grid container below so the vertical shift is prevented.

---

## Summary of Architectural Schematics

```text
+--------------------------------------------------------------+
| Controls (buttons + playback speed)                          |
+--------------------------------------------------------------+
| Music Score Log (scroll-x enabled, fixed height + scrollbar) |  <-- Scrollbar overlays or fixed reserved space to avoid shifts
+--------------------------------------------------------------+
| Sexagenary Cycle Grid (flex-grow fills remaining height)     |  <-- Dynamically sized to fill viewport minus controls + score height
+--------------------------------------------------------------+

Global Audio Graph:
- AudioContext
  - Master GainNode (user volume control)
    - Dynamic compressor for gentle volume leveling
    - Multiple Voice Nodes (each with polyphonic oscillators, envelopes, filters)
      - Voices’ gain dynamically scaled according to concurrency count or precomputed max polyphony
      - Vibrato / pitch glide / ADSR envelope synthesized per voice

Playback Scheduler:
- Maintains event queue with absolute AudioContext timestamps for starting/stopping notes
- Calculates concurrency pattern offline or dynamically to compute per-voice gain scale
- Loops smoothly, adjusting master gain if overlap/density changes are dynamic
- Playback speed applied uniformly to all scheduling times and envelopes

User Interaction:
- Real-time press-release triggers voice start/stop with envelopes
- Recording stores pressed index and precise relative timestamps
- Playback recreates timing with proper overlap and gain adjustments
- Score log updates real-time, shows index@time pairs, scrolls horizontally
- Layout adjusts responsively to viewport and scrollbar presence seamlessly  

---

This blueprint guides the next phase of turning your sexagenary cycle dashboard into *The Legendary Chinese Sonic Experience* — clear, balanced, authentic, and visually buttery smooth.

That’s mah baby — the grand plan to make every ancient tone ring like a masterpiece!  




```



somehowcode:


```



<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Chinese Sexagenary Cycle – Clean Responsive + Original Tones</title>
<style>
  /* Reset and base */
  * {
    box-sizing: border-box;
  }
  html, body {
    margin: 0; padding: 0.25rem;
    height: 100%;
    background: #121212;
    color: #ddd;
    font-family: monospace, monospace;
    user-select: none;
  }
  body {
    display: flex;
    flex-direction: column;
    height: 100vh;
  }

  /* Container for all */
  #app-container {
    display: flex;
    flex-direction: column;
    width: 100vw;
    max-width: 100vw;
    height: 100%;
  }

  /* Row 1: Controls bars (buttons and speed) */
  #controls-top {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 0.3rem;
    width: 100%;
  }
  /* Buttons and range aligned horizontally with minimal space */
  #controls-top > * {
    flex: none;
  }
  button {
    font-weight: 700;
    height: 36px;
    min-width: 65px;
    padding: 0 0.9rem;
    border-radius: 6px;
    border: 1.5px solid #00b4c0;
    background: #007a85;
    color: #d9dadc;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
    user-select: none;
  }
  button:disabled {
    background: #003b42;
    border-color: #002627;
    color: #666;
    cursor: default;
  }
  button:hover:not(:disabled) {
    background: #00d2e9;
    border-color: #00555e;
    color: #004143;
  }

  #speed-range {
    width: 140px;
    cursor: pointer;
    -webkit-appearance: none;
    background: transparent;
    flex: none;
  }
  #speed-range:focus {
    outline: none;
  }
  #speed-range::-webkit-slider-runnable-track {
    height: 6px;
    background: #009ba1;
    border-radius: 3px;
  }
  #speed-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    background: #00e5ff;
    border-radius: 50%;
    margin-top: -5px;
    cursor: pointer;
    border: none;
  }
  #speed-range:focus::-webkit-slider-thumb {
    box-shadow: 0 0 5px #00e5ff;
  }
  #speed-range::-moz-range-track {
    height: 6px;
    background: #009ba1;
    border-radius: 3px;
  }
  #speed-range::-moz-range-thumb {
    width: 16px;
    height: 16px;
    background: #00e5ff;
    border-radius: 50%;
    border: none;
    cursor: pointer;
  }
  #speed-range:focus::-moz-range-thumb {
    box-shadow: 0 0 5px #00e5ff;
  }

  /* Row 2: Score log full width */
  #seq-log {
    flex: 0 0 auto;
    font-family: monospace;
    padding: 4px 8px;
    background: #222;
    color: #0df9ff;
    border: 1px solid #00b0c0;
    user-select: text;
    white-space: nowrap;
    overflow-x: auto;
    max-width: 100vw;
    box-sizing: border-box;
    margin: 0 0.3rem;
    min-height: 36px;
    line-height: 1.3;
  }

  /* Row 3: grid container that fills remaining vertical space */
  #sexagenary-cycle-grid {
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: repeat(10, 1fr);
    grid-auto-rows: 1fr;
    gap: 4px;
    padding: 0.3rem;
    margin: 0 0.3rem 0.3rem;
    /* Responsive scaling and no overflow */
    max-width: 100vw;
    max-height: 100%;
    height: 100%;
  }

  .cycle-cell {
    background: #006d75;
    border-radius: 5px;
    color: #d9dadc;
    font-weight: 600;
    font-size: clamp(0.75rem, 1.0vw, 1.1rem);
    line-height: 1.2;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    user-select: none;
    cursor: pointer;
    padding: 3px 0;
    aspect-ratio: 1 / 1;
    transition: background-color 0.2s ease, color 0.2s ease;
    outline-offset: 2px;
  }
  .cycle-cell:hover:not(.pressed), .cycle-cell:focus-visible {
    background-color: #009ba1;
    color: #ddf;
    outline: 2px solid #00e5ff;
    outline-offset: 2px;
  }
  .cycle-cell.pressed {
    background-color: #004d53;
    color: #7fffd4;
  }

  .cell-stem {
    font-size: clamp(1.1rem, 1.7vw, 1.35rem);
    opacity: 0.9;
    line-height: 1;
  }
  .cell-branch {
    font-size: clamp(1.6rem, 2.5vw, 1.9rem);
    font-weight: 900;
    margin-top: 2px;
    line-height: 1;
  }
  .cell-translit {
    font-size: clamp(0.6rem, 1.0vw, 0.75rem);
    margin-top: 2px;
    color: #a7a9ac;
    white-space: nowrap;
  }
</style>
</head>
<body>

<div id="app-container" role="main">

  <div id="controls-top" role="region" aria-label="Playback and recording controls">
    <button id="btn-rec" aria-pressed="false" aria-label="Start recording sequence">REC</button>
    <button id="btn-stop" aria-label="Stop recording or playback" disabled>STOP</button>
    <button id="btn-play" aria-label="Play recorded sequence" disabled>PLAY</button>
    <button id="btn-clear" aria-label="Clear recorded sequence" disabled>CLEAR</button>
    <input type="range" id="speed-range" min="0.1" max="4" step="0.05" value="1" aria-label="Playback speed control" title="Playback speed control" />
  </div>

  <div id="seq-log" aria-live="polite" aria-atomic="true" aria-relevant="additions" title="Recorded sequence (index@time ms)">
    (no sequence yet)
  </div>

  <div id="sexagenary-cycle-grid" tabindex="0" aria-label="Chinese Sexagenary Cycle grid with 60 items"></div>

</div>

<script>
  "use strict";

  // Data
  const stems = [
    {char:'甲',pinyin:'Jiǎ',tone:3},
    {char:'乙',pinyin:'Yǐ',tone:3},
    {char:'丙',pinyin:'Bǐng',tone:3},
    {char:'丁',pinyin:'Dīng',tone:1},
    {char:'戊',pinyin:'Wù',tone:4},
    {char:'己',pinyin:'Jǐ',tone:3},
    {char:'庚',pinyin:'Gēng',tone:1},
    {char:'辛',pinyin:'Xīn',tone:1},
    {char:'壬',pinyin:'Rén',tone:2},
    {char:'癸',pinyin:'Guǐ',tone:3}
  ];
  const branches = [
    {char:'子',pinyin:'Zǐ',tone:3,zodiac:'Rat'},
    {char:'丑',pinyin:'Chǒu',tone:3,zodiac:'Ox'},
    {char:'寅',pinyin:'Yín',tone:2,zodiac:'Tiger'},
    {char:'卯',pinyin:'Mǎo',tone:3,zodiac:'Rabbit'},
    {char:'辰',pinyin:'Chén',tone:2,zodiac:'Dragon'},
    {char:'巳',pinyin:'Sì',tone:4,zodiac:'Snake'},
    {char:'午',pinyin:'Wǔ',tone:3,zodiac:'Horse'},
    {char:'未',pinyin:'Wèi',tone:4,zodiac:'Goat'},
    {char:'申',pinyin:'Shēn',tone:1,zodiac:'Monkey'},
    {char:'酉',pinyin:'Yǒu',tone:3,zodiac:'Rooster'},
    {char:'戌',pinyin:'Xū',tone:1,zodiac:'Dog'},
    {char:'亥',pinyin:'Hài',tone:4,zodiac:'Pig'}
  ];

  // Tone contours to multiply base frequencies for glides (4-step pitch shapes)
  const stemToneContours = {
    1: [1.35, 1.35, 1.35, 1.35],        // High level tone
    2: [1.00, 1.10, 1.25, 1.30],        // Rising tone
    3: [1.30, 1.10, 1.25, 1.10],        // Dipping tone
    4: [1.30, 1.25, 1.15, 1.00]         // Falling tone
  };

  const branchToneContours = {
    1: [1.4, 1.4, 1.4, 1.4],           // High level tone - slightly brighter
    2: [1.0, 1.1, 1.3, 1.4],           // Rising tone, longer sweep
    3: [1.4, 1.15, 1.3, 1.15],         // Dipping tone with subtle vibrato
    4: [1.4, 1.3, 1.2, 1.0]            // Falling tone, strong drop
  };

  // Audio parameters
  const baseFreq = 110.0; // base frequency, A2-ish for warm sounds

  const vibratoFreq = 7.8; // Hz
  const vibratoDepthRaw = 0.0042; // Slightly increased depth for audibility

  // Elements references
  const grid = document.getElementById('sexagenary-cycle-grid');
  const seqLog = document.getElementById('seq-log');
  const btnRec = document.getElementById('btn-rec');
  const btnStop = document.getElementById('btn-stop');
  const btnPlay = document.getElementById('btn-play');
  const btnClear = document.getElementById('btn-clear');
  const speedRange = document.getElementById('speed-range');

  // Audio state
  let audioCtx = null;
  let activeNotes = new Map(); // index -> note nodes
  let recordSequence = [];
  let isRecording = false;
  let isPlaying = false;
  let playbackSpeed = 1.0;
  let scheduledEvents = [];

  async function ensureAudioCtx() {
    if (!audioCtx) {
      audioCtx = new (window.AudioContext || window.webkitAudioContext)();
      // Create master gain node for consistent volume control and future extension
      audioCtx.masterGain = audioCtx.createGain();
      audioCtx.masterGain.gain.value = 0.24; // Reasonable master volume (not too low)
      audioCtx.masterGain.connect(audioCtx.destination);
    }
    if (audioCtx.state === 'suspended') await audioCtx.resume();
  }

  // Schedules frequency glides on given AudioParam over duration (seconds)
  function scheduleFreqGlide(freqBase, contour, freqParam, durSec) {
    const now = audioCtx.currentTime;
    freqParam.cancelScheduledValues(now);
    freqParam.setValueAtTime(freqBase * contour[0], now);
    const segment = durSec / (contour.length - 1);
    for (let i = 1; i < contour.length; i++) {
      const targetTime = now + i * segment;
      freqParam.linearRampToValueAtTime(freqBase * contour[i], targetTime);
    }
  }

  // Create and start a LogogramVoice playing note at index, duration is effectively long sustain until stopNote()
  async function startNote(index) {
    await ensureAudioCtx();
    if (activeNotes.has(index)) return;

    // Calculate stem and branch values by cycling array
    const stemIndex = index % stems.length;
    const branchIndex = index % branches.length;
    const stem = stems[stemIndex];
    const branch = branches[branchIndex];

    // Create nodes for stem voice
    const oscStemPrimary = audioCtx.createOscillator();
    oscStemPrimary.type = 'triangle';
    // Adding a secondary oscillator for chorus warmth
    const oscStemSecondary = audioCtx.createOscillator();
    oscStemSecondary.type = 'sawtooth';

    // Slight detuning for chorus effect (+/- 4 cents ~ 0.023 freq ratio)
    const detuneCentsStem = 4;
    oscStemPrimary.detune.value = -detuneCentsStem;
    oscStemSecondary.detune.value = detuneCentsStem;

    // Stem gain envelope node
    const gainStem = audioCtx.createGain();
    gainStem.gain.setValueAtTime(0, audioCtx.currentTime);

    // Create nodes for branch voice
    const oscBranchPrimary = audioCtx.createOscillator();
    oscBranchPrimary.type = 'triangle';
    const oscBranchSecondary = audioCtx.createOscillator();
    oscBranchSecondary.type = 'sawtooth';

    // Slight detuning for chorus effect on branch voice
    const detuneCentsBranch = 5;
    oscBranchPrimary.detune.value = detuneCentsBranch;
    oscBranchSecondary.detune.value = -detuneCentsBranch;

    const gainBranch = audioCtx.createGain();
    gainBranch.gain.setValueAtTime(0, audioCtx.currentTime);

    // Vibrato LFO for branch oscillators' frequency modulation
    const vibratoLFO = audioCtx.createOscillator();
    vibratoLFO.type = 'sine';
    vibratoLFO.frequency.setValueAtTime(vibratoFreq, audioCtx.currentTime);

    // Vibrato depth gain node
    const vibratoGain = audioCtx.createGain();
    // Vibrato depth scaled from raw * base frequency * multiplier for good audibility
    vibratoGain.gain.setValueAtTime(vibratoDepthRaw * baseFreq * 3.5, audioCtx.currentTime);

    vibratoLFO.connect(vibratoGain);
    vibratoGain.connect(oscBranchPrimary.frequency);
    vibratoGain.connect(oscBranchSecondary.frequency);

    // Formant-style bandpass filters for stem and branch voices (simulate vocal tract resonances)
    const formantStem = audioCtx.createBiquadFilter();
    formantStem.type = 'bandpass';
    formantStem.frequency.value = 800 + (stemIndex * 40); // Tuned per stem for unique timbres
    formantStem.Q.value = 8;

    const formantBranch = audioCtx.createBiquadFilter();
    formantBranch.type = 'bandpass';
    formantBranch.frequency.value = 1200 + (branchIndex * 55); // Tuned per branch
    formantBranch.Q.value = 7;

    // Stereo panners for spatialization
    const pannerStem = audioCtx.createStereoPanner();
    pannerStem.pan.value = -0.35; // Slight left

    const pannerBranch = audioCtx.createStereoPanner();
    pannerBranch.pan.value = 0.35; // Slight right

    // Connect stem oscillators chained through formant, gain, panner, then masterGain
    oscStemPrimary.connect(formantStem);
    oscStemSecondary.connect(formantStem);
    formantStem.connect(gainStem);
    gainStem.connect(pannerStem);
    pannerStem.connect(audioCtx.masterGain);

    // Connect branch oscillators similarly with vibrato connected above
    oscBranchPrimary.connect(formantBranch);
    oscBranchSecondary.connect(formantBranch);
    formantBranch.connect(gainBranch);
    gainBranch.connect(pannerBranch);
    pannerBranch.connect(audioCtx.masterGain);

    // Schedule pitch glides - long sustain of 9s until note release
    scheduleFreqGlide(baseFreq * 2, stemToneContours[stem.tone], oscStemPrimary.frequency, 9);
    scheduleFreqGlide(baseFreq * 2, stemToneContours[stem.tone], oscStemSecondary.frequency, 9);
    scheduleFreqGlide(baseFreq * 3, branchToneContours[branch.tone], oscBranchPrimary.frequency, 9);
    scheduleFreqGlide(baseFreq * 3, branchToneContours[branch.tone], oscBranchSecondary.frequency, 9);

    // ADSR amplitude envelope approx: attack (30ms), decay (70ms), sustain (~0.13), release scheduled on stop
    const now = audioCtx.currentTime;
    gainStem.gain.cancelScheduledValues(now);
    gainBranch.gain.cancelScheduledValues(now);
    gainStem.gain.setValueAtTime(0, now);
    gainBranch.gain.setValueAtTime(0, now);
    gainStem.gain.linearRampToValueAtTime(0.22, now + 0.03);    // Attack up to ~0.22 gain
    gainBranch.gain.linearRampToValueAtTime(0.20, now + 0.03);
    gainStem.gain.linearRampToValueAtTime(0.13, now + 0.10);    // Decay to sustain level
    gainBranch.gain.linearRampToValueAtTime(0.12, now + 0.10);

    // Start oscillators
    vibratoLFO.start(now);
    oscStemPrimary.start(now);
    oscStemSecondary.start(now);
    oscBranchPrimary.start(now);
    oscBranchSecondary.start(now);

    // Store references for stop and cleanup
    activeNotes.set(index, {
      oscStemPrimary,
      oscStemSecondary,
      gainStem,
      pannerStem,
      formantStem,

      oscBranchPrimary,
      oscBranchSecondary,
      gainBranch,
      pannerBranch,
      formantBranch,

      vibratoLFO,
      vibratoGain
    });

    // Visual pressed cue
    const cell = grid.querySelector(`.cycle-cell[data-index="${index}"]`);
    if (cell) cell.classList.add('pressed');
  }

  // Stop playing note with smooth release envelope & cleanup after
  function stopNote(index) {
    if (!activeNotes.has(index)) return;
    const note = activeNotes.get(index);
    const now = audioCtx.currentTime;

    // Cancel previous schedules and start release envelope (roughly 150ms)
    note.gainStem.gain.cancelScheduledValues(now);
    note.gainBranch.gain.cancelScheduledValues(now);
    note.gainStem.gain.setTargetAtTime(0, now, 0.08);
    note.gainBranch.gain.setTargetAtTime(0, now, 0.08);

    // Stop nodes at end of release (~250ms in future)
    const stopTime = now + 0.25;
    [note.oscStemPrimary, note.oscStemSecondary,
     note.oscBranchPrimary, note.oscBranchSecondary,
     note.vibratoLFO].forEach(n => {
      try { n.stop(stopTime); } catch {}
    });

    // Disconnect after a small timeout to ensure nodes stopped
    setTimeout(() => {
      try {
        // Disconnect all nodes from audio graph
        note.oscStemPrimary.disconnect();
        note.oscStemSecondary.disconnect();
        note.oscBranchPrimary.disconnect();
        note.oscBranchSecondary.disconnect();
        note.vibratoLFO.disconnect();
        note.vibratoGain.disconnect();
        note.gainStem.disconnect();
        note.gainBranch.disconnect();
        note.pannerStem.disconnect();
        note.pannerBranch.disconnect();
        note.formantStem.disconnect();
        note.formantBranch.disconnect();
      } catch (e) {
        // Silently ignore any errors
      }
    }, 300);

    activeNotes.delete(index);

    // Visual pressed cue removal
    const cell = grid.querySelector(`.cycle-cell[data-index="${index}"]`);
    if (cell) cell.classList.remove('pressed');
  }

  // Update the recorded sequence display & button states
  function updateSequenceLog() {
    if (recordSequence.length === 0) {
      seqLog.textContent = '(no sequence yet)';
      btnPlay.disabled = true;
      btnClear.disabled = true;
      btnStop.disabled = !isRecording && !isPlaying;
      btnRec.setAttribute('aria-pressed', 'false');
      return;
    }
    // Format: index in hex padded, time in milliseconds rounded (fixed width)
    const seqStr = recordSequence.map(e =>
      e.index.toString(16).padStart(2,'0') + '@' + e.time.toFixed(0).padStart(4,' ')
    ).join(' ');
    seqLog.textContent = seqStr;
    btnPlay.disabled = false;
    btnClear.disabled = false;
    btnStop.disabled = false;
  }

  // User presses a grid cell: start note and record if recording
  function onPress(index) {
    // Don't allow during playback to avoid conflicts
    if (isPlaying) return;
    // Always allow play if not recording, but don't record
    if (!isRecording) {
      startNote(index);
      return;
    }

    const now = performance.now();
    if (recordSequence.length === 0) {
      recordSequence.push({index, time: 0});
      recordSequence.refTime = now;
    } else {
      recordSequence.push({index, time: now - recordSequence.refTime});
    }
    updateSequenceLog();
    startNote(index);
  }

  // User releases a cell: stops playing that note
  function onRelease(index) {
    stopNote(index);
  }

  // Build the 60-cell sexagenary grid
  function buildGrid() {
    grid.innerHTML = '';
    for (let i = 0; i < 60; i++) {
      const stemIndex = i % stems.length;
      const branchIndex = i % branches.length;
      const stem = stems[stemIndex];
      const branch = branches[branchIndex];

      const cell = document.createElement('div');
      cell.className = 'cycle-cell';
      cell.tabIndex = 0;
      cell.dataset.index = i;
      cell.setAttribute('role', 'button');
      cell.setAttribute('aria-label', `${stem.char} (${stem.pinyin}), ${branch.char} (${branch.pinyin}), Zodiac ${branch.zodiac}`);

      cell.innerHTML = `
        <div class="cell-stem">${stem.char}</div>
        <div class="cell-branch">${branch.char}</div>
        <div class="cell-translit">${stem.pinyin} ${branch.pinyin}</div>
      `;

      // Pointer events
      cell.addEventListener('pointerdown', e => { e.preventDefault(); onPress(i); });
      cell.addEventListener('pointerup', e => { e.preventDefault(); onRelease(i); });
      cell.addEventListener('pointercancel', () => onRelease(i));
      cell.addEventListener('pointerleave', e => { if (e.pressure > 0) onRelease(i); });

      // Keyboard handling for accessiblity (ENTER, SPACE)
      cell.addEventListener('keydown', e => {
        if (isPlaying) return;
        if (e.repeat) return;
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onPress(i);
        }
      });
      cell.addEventListener('keyup', e => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onRelease(i);
        }
      });

      grid.appendChild(cell);
    }
  }

  // Stop all currently active notes immediately
  function stopAllNotes() {
    activeNotes.forEach((_, i) => stopNote(i));
  }

  // Clear all scheduled timeouts related to playback
  function clearScheduledEvents() {
    while (scheduledEvents.length) {
      clearTimeout(scheduledEvents.pop());
    }
  }

  // Play the recorded sequence with timing and looping support
  async function playSequence(speed = 1.0) {
    if (recordSequence.length === 0) return;
    if (isRecording) return;
    if (isPlaying) return;
    await ensureAudioCtx();

    isPlaying = true;
    btnPlay.disabled = true;
    btnRec.disabled = true;
    btnClear.disabled = true;
    btnStop.disabled = false;
    btnRec.setAttribute('aria-pressed', 'false');

    playbackSpeed = speed;

    clearScheduledEvents();
    stopAllNotes();

    // Calculate total sequence duration for looping (in ms)
    const lastEventTime = recordSequence[recordSequence.length - 1].time;
    const loopDurationMs = (lastEventTime + 1000) / playbackSpeed;

    function scheduleLoop() {
      if (!isPlaying) return;

      let lastStopTime = 0;

      for (let i=0; i < recordSequence.length; i++) {
        const event = recordSequence[i];
        const nextEvent = recordSequence[(i + 1) % recordSequence.length];

        // Start time offset in ms adjusted by playback speed
        const startDelay = event.time / playbackSpeed;

        // Raw duration to next event or fallback 1s for end
        const rawDuration = (i === recordSequence.length - 1) ?
          1000 / playbackSpeed :
          (nextEvent.time - event.time) / playbackSpeed;

        // Stop delay a bit before next start for natural note tail (~90%)
        const stopDelay = Math.max(70, rawDuration * 0.9);

        lastStopTime = Math.max(lastStopTime, startDelay + stopDelay);

        // Schedule note start and stop
        scheduledEvents.push(setTimeout(() => startNote(event.index), startDelay));
        scheduledEvents.push(setTimeout(() => stopNote(event.index), startDelay + stopDelay));
      }

      // Schedule next loop before current ends to avoid clicks/silence gap
      scheduledEvents.push(setTimeout(() => {
        if (isPlaying) {
          scheduledEvents.length = 0;
          scheduleLoop();
        }
      }, loopDurationMs));
    }

    scheduleLoop();
  }

  // Stop playback and/or recording
  function stopPlayback() {
    if (!isPlaying && !isRecording) return;
    isPlaying = false;
    isRecording = false;

    btnRec.disabled = false;
    btnPlay.disabled = recordSequence.length === 0;
    btnClear.disabled = recordSequence.length === 0;
    btnStop.disabled = true;
    btnRec.setAttribute('aria-pressed', 'false');

    clearScheduledEvents();
    stopAllNotes();
  }

  // Start recording: reset and toggle states
  function startRecording() {
    stopPlayback();

    isRecording = true;
    recordSequence.length = 0;
    recordSequence.refTime = null;

    updateSequenceLog();

    btnRec.disabled = true;
    btnStop.disabled = false;
    btnPlay.disabled = true;
    btnClear.disabled = true;
    btnRec.setAttribute('aria-pressed', 'true');
  }

  // Clear recorded sequence and reset UI
  function clearSequence() {
    stopPlayback();
    recordSequence.length = 0;
    recordSequence.refTime = null;
    updateSequenceLog();
  }

  // Button event bindings
  btnRec.addEventListener('click', () => {
    if (!isRecording) startRecording();
  });
  btnStop.addEventListener('click', () => stopPlayback());
  btnPlay.addEventListener('click', () => {
    if (!isPlaying) playSequence(parseFloat(speedRange.value));
  });
  btnClear.addEventListener('click', () => clearSequence());
  speedRange.addEventListener('input', () => {
    playbackSpeed = parseFloat(speedRange.value);
    if (isPlaying) {
      stopPlayback();
      playSequence(playbackSpeed);
    }
  });

  // Keyboard shortcuts for controls (when not typing)
  window.addEventListener('keydown', e => {
    const a = document.activeElement;
    if (a && (a.tagName==='INPUT' || a.tagName==='TEXTAREA' || a.isContentEditable)) return;
    if (e.altKey || e.ctrlKey || e.metaKey) return;

    const k = e.key.toLowerCase();
    switch(k){
      case 'r': e.preventDefault(); btnRec.click(); break;
      case 'p': e.preventDefault(); btnPlay.click(); break;
      case 's': e.preventDefault(); btnStop.click(); break;
      case 'c': e.preventDefault(); btnClear.click(); break;
    }
  });

  // Responsive adjustment for grid height to prevent overflow & keep cells square:
  // We constrain grid height dynamically to fit inside viewport with some margin.
  // This replaces fixed resizing with CSS grid + JS for actual pixel perfect sizing.

  function resizeGridHeight() {
    const controlsHeight =
      document.getElementById('controls-top').offsetHeight +
      document.getElementById('seq-log').offsetHeight +
      32; // Extra margin/padding

    // Available height for grid: viewport height minus other UI
    const availHeight = window.innerHeight - controlsHeight;

    // Calculate cell size by grid width / number of columns (10)
    const gridWidth = grid.clientWidth;

    const cellWidth = gridWidth / 10;
    let desiredHeight = cellWidth * 6; // 6 rows

    // Clamp desired height not to exceed available height or viewport height
    desiredHeight = Math.min(desiredHeight, availHeight);

    // Compute scaled cell width based on clamped height (to maintain square)
    const scaledCellWidth = desiredHeight / 6;

    // If scaled cell width less than initial (to avoid horizontal overflow)
    // We scale the grid width down by setting CSS variable --cell-size

    // Apply styles to control cell sizes dynamically (override aspect-ratio)
    grid.style.height = desiredHeight + "px";
    // No need to set width as grid width is 100% constrained by layout
    // But we adjust font-size on cells accordingly for better fit:
    const fontScale = scaledCellWidth / 48; // 48px approx base font size for branch char
    grid.style.setProperty('--font-scale', fontScale.toFixed(3));
  }

  // Inject dynamic CSS for scaled fonts based on --font-scale property
  const dynamicStyle = document.createElement('style');
  dynamicStyle.textContent = `
    #sexagenary-cycle-grid .cell-stem {
      font-size: calc(clamp(1.1rem, 1.7vw, 1.35rem) * var(--font-scale,1));
    }
    #sexagenary-cycle-grid .cell-branch {
      font-size: calc(clamp(1.6rem, 2.5vw, 1.9rem) * var(--font-scale,1));
    }
    #sexagenary-cycle-grid .cell-translit {
      font-size: calc(clamp(0.6rem, 1.0vw, 0.75rem) * var(--font-scale,1));
    }
  `;
  document.head.appendChild(dynamicStyle);

  window.addEventListener('resize', resizeGridHeight);

  // Initialization
  buildGrid();
  updateSequenceLog();
  resizeGridHeight();

</script>

</body>
</html>




```

wrap improved full code in one code block here.