<script lang="ts">
  import { onMount } from "svelte";
  import WaveformDisplay from "./lib/components/WaveformDisplay.svelte";
  import VUMeter from "./lib/components/VUMeter.svelte";
  import WelcomeModal from "./lib/components/WelcomeModal.svelte";
  import ProcessorPopup from "./lib/components/ProcessorPopup.svelte";
  import { engines } from "./lib/audio/engines/registry";
  import type {
    SynthEngine,
    PitchLock,
  } from "./lib/audio/engines/base/SynthEngine";
  import type { EngineType } from "./lib/audio/engines/base/SynthEngine";
  import { AudioService } from "./lib/audio/services/AudioService";
  import { downloadWAV } from "./lib/audio/utils/WAVEncoder";
  import {
    loadVolume,
    saveVolume,
    loadDuration,
    saveDuration,
    loadPitchLockEnabled,
    savePitchLockEnabled,
    loadPitchLockFrequency,
    savePitchLockFrequency,
    loadExpandedCategories,
    saveExpandedCategories,
    loadSelectedProcessorCategory,
    saveSelectedProcessorCategory,
  } from "./lib/utils/settings";
  import {
    cropAudio,
    cutAudio,
    processSelection,
  } from "./lib/audio/utils/AudioEdit";
  import { generateRandomColor } from "./lib/utils/colors";
  import { getRandomProcessor } from "./lib/audio/processors/registry";
  import type { AudioProcessor, ProcessorCategory } from "./lib/audio/processors/AudioProcessor";
  import { Sample } from "./lib/audio/engines/Sample";
  import { Input } from "./lib/audio/engines/Input";
  import { createKeyboardHandler } from "./lib/utils/keyboard";
  import { parseFrequencyInput, formatFrequency } from "./lib/utils/pitch";
  import { UndoManager, type AudioState } from "./lib/utils/UndoManager";
  import type { EngineCategory } from "./lib/audio/engines/base/SynthEngine";

  let currentEngineIndex = $state(0);
  const engine = $derived(engines[currentEngineIndex]);
  const engineType = $derived(engine.getType());
  const audioService = new AudioService();
  const undoManager = new UndoManager(20);

  let currentParams = $state<any>(null);
  let currentBuffer = $state<AudioBuffer | null>(null);
  let duration = $state(loadDuration());
  let volume = $state(loadVolume());
  let playbackPosition = $state(-1);
  let cuePoint = $state(0);
  let waveformColor = $state(generateRandomColor());
  let showModal = $state(true);
  let isProcessed = $state(false);
  let showProcessorPopup = $state(false);
  let popupTimeout = $state<ReturnType<typeof setTimeout> | null>(null);
  let isRecording = $state(false);
  let isDragOver = $state(false);
  let pitchLockEnabled = $state(loadPitchLockEnabled());
  let pitchLockFrequency = $state(loadPitchLockFrequency());
  let pitchLockInput = $state(formatFrequency(loadPitchLockFrequency()));
  let pitchLockInputValid = $state(true);
  let selectionStart = $state<number | null>(null);
  let selectionEnd = $state<number | null>(null);
  let canUndo = $state(false);
  let sidebarOpen = $state(false);
  let expandedCategories = $state<Set<string>>(loadExpandedCategories());
  let selectedProcessorCategory = $state<ProcessorCategory | 'All'>(
    loadSelectedProcessorCategory()
  );

  const showDuration = $derived(engineType !== "sample");
  const showRandomButton = $derived(engineType === "generative");
  const showRecordButton = $derived(engineType === "input");
  const showFileDropZone = $derived(engineType === "sample" && !currentBuffer);
  const showMutateButton = $derived(
    engineType === "generative" && !isProcessed && currentBuffer,
  );
  const showPitchLock = $derived(engineType === "generative");
  const pitchLock = $derived<PitchLock>({
    enabled: pitchLockEnabled,
    frequency: pitchLockFrequency,
  });
  const hasSelection = $derived(
    selectionStart !== null && selectionEnd !== null && currentBuffer !== null,
  );
  const showEditButtons = $derived(hasSelection);

  $effect(() => {
    audioService.setVolume(volume);
    saveVolume(volume);
  });

  $effect(() => {
    saveDuration(duration);
  });

  $effect(() => {
    savePitchLockEnabled(pitchLockEnabled);
  });

  $effect(() => {
    savePitchLockFrequency(pitchLockFrequency);
  });

  $effect(() => {
    saveExpandedCategories(expandedCategories);
  });

  $effect(() => {
    saveSelectedProcessorCategory(selectedProcessorCategory);
  });

  // Group engines by category
  const enginesByCategory = $derived.by(() => {
    const grouped = new Map<EngineCategory, typeof engines>();
    for (const engine of engines) {
      const category = engine.getCategory();
      if (!grouped.has(category)) {
        grouped.set(category, []);
      }
      grouped.get(category)!.push(engine);
    }
    return grouped;
  });

  function toggleCategory(category: string) {
    const newSet = new Set(expandedCategories);
    if (newSet.has(category)) {
      newSet.delete(category);
    } else {
      newSet.add(category);
    }
    expandedCategories = newSet;
  }

  function selectProcessorCategory(category: ProcessorCategory | 'All') {
    selectedProcessorCategory = category;
  }

  onMount(() => {
    audioService.setPlaybackUpdateCallback((position) => {
      playbackPosition = position;
    });
    generateRandom();
  });

  function captureCurrentState(): AudioState | null {
    return UndoManager.captureState(
      currentBuffer,
      currentParams,
      isProcessed,
      waveformColor,
      currentEngineIndex,
    );
  }

  function pushState(): void {
    const state = captureCurrentState();
    if (state) {
      undoManager.pushState(state);
      canUndo = true;
    }
  }

  function restoreState(state: AudioState): void {
    currentBuffer = audioService.createAudioBuffer([
      state.leftChannel,
      state.rightChannel,
    ]);
    currentParams = state.params;
    isProcessed = state.isProcessed;
    waveformColor = state.waveformColor;
    if (state.engineIndex !== currentEngineIndex) {
      currentEngineIndex = state.engineIndex;
    }
    clearSelection();
    audioService.play(currentBuffer);
  }

  function undo(): void {
    const previousState = undoManager.undo();
    if (previousState) {
      restoreState(previousState);
      canUndo = undoManager.canUndo();
    }
  }

  const keyboardHandler = createKeyboardHandler({
    onMutate: mutate,
    onRandom: generateRandom,
    onProcess: processSound,
    onDownload: download,
    onUndo: undo,
    onPlayFromStart: togglePlayback,
    onDurationDecrease: (large) => {
      duration = Math.max(0.05, duration - (large ? 1 : 0.05));
    },
    onDurationIncrease: (large) => {
      duration = Math.min(32, duration + (large ? 1 : 0.05));
    },
    onVolumeDecrease: (large) => {
      volume = Math.max(0, volume - (large ? 0.2 : 0.05));
    },
    onVolumeIncrease: (large) => {
      volume = Math.min(1, volume + (large ? 0.2 : 0.05));
    },
    onEscape: () => {
      if (hasSelection) {
        clearSelection();
      } else if (showModal) {
        closeModal();
      }
    },
  });

  function generateRandom() {
    pushState();

    currentParams = engine.randomParams(pitchLock);
    waveformColor = generateRandomColor();
    isProcessed = false;
    clearSelection();
    regenerateBuffer();
  }

  function mutate() {
    if (!currentParams) {
      generateRandom();
      return;
    }
    pushState();

    currentParams = engine.mutateParams(currentParams, 0.15, pitchLock);
    waveformColor = generateRandomColor();
    regenerateBuffer();
  }

  function handlePitchLockInput(event: Event) {
    const input = event.target as HTMLInputElement;
    pitchLockInput = input.value;

    const parsed = parseFrequencyInput(pitchLockInput);
    if (parsed !== null) {
      pitchLockFrequency = parsed;
      pitchLockInputValid = true;
    } else {
      pitchLockInputValid = false;
    }
  }

  function togglePitchLock() {
    pitchLockEnabled = !pitchLockEnabled;
  }

  async function regenerateBuffer() {
    if (!currentParams) return;

    const sampleRate = audioService.getSampleRate();
    const data = await engine.generate(
      currentParams,
      sampleRate,
      duration,
      pitchLock,
    );
    currentBuffer = audioService.createAudioBuffer(data);
    cuePoint = 0;
    audioService.play(currentBuffer);
  }

  function replaySound() {
    if (currentBuffer) {
      audioService.play(currentBuffer);
    }
  }

  function setCuePoint(offset: number) {
    cuePoint = offset;
  }

  function togglePlayback() {
    if (playbackPosition >= 0) {
      audioService.stop();
    } else if (currentBuffer) {
      audioService.play(currentBuffer, cuePoint);
    }
  }

  function download() {
    if (!currentBuffer) return;
    downloadWAV(currentBuffer, "synth-sound.wav");
  }

  function processSound() {
    if (!currentBuffer) return;
    applyProcessor(getRandomProcessor());
  }

  async function applyProcessor(processor: AudioProcessor) {
    if (!currentBuffer) return;

    pushState();

    let processedLeft: Float32Array;
    let processedRight: Float32Array;

    if (hasSelection) {
      const start = Math.min(selectionStart!, selectionEnd!);
      const end = Math.max(selectionStart!, selectionEnd!);
      const sampleRate = audioService.getSampleRate();

      [processedLeft, processedRight] = await processSelection(
        currentBuffer,
        start,
        end,
        processor,
        sampleRate,
      );
    } else {
      const leftChannel = currentBuffer.getChannelData(0);
      const rightChannel = currentBuffer.getChannelData(1);
      [processedLeft, processedRight] = await processor.process(
        leftChannel,
        rightChannel,
      );
    }

    currentBuffer = audioService.createAudioBuffer([
      processedLeft,
      processedRight,
    ]);
    isProcessed = true;
    audioService.play(currentBuffer);
    hideProcessorPopup();
  }

  function switchEngine(index: number) {
    pushState();

    currentEngineIndex = index;
    currentBuffer = null;
    currentParams = null;
    isProcessed = false;
    clearSelection();
    sidebarOpen = false;

    if (engineType === "generative") {
      generateRandom();
    }
  }

  async function handleFileInput(event: Event) {
    const input = event.target as HTMLInputElement;
    if (!input.files?.length) return;
    await loadAudioFile(input.files[0]);
  }

  async function loadAudioFile(file: File) {
    if (!(engine instanceof Sample)) return;

    pushState();

    try {
      await engine.loadFile(file);
      currentParams = engine.randomParams(pitchLock);
      waveformColor = generateRandomColor();
      isProcessed = false;
      regenerateBuffer();
    } catch (error) {
      console.error("Failed to load audio file:", error);
      alert(`Failed to load audio file: ${error}`);
    }
  }

  async function recordAudio() {
    if (!(engine instanceof Input) || isRecording) return;

    pushState();

    try {
      isRecording = true;
      await engine.record(duration);
      currentParams = engine.randomParams(pitchLock);
      waveformColor = generateRandomColor();
      isProcessed = false;
      regenerateBuffer();
    } catch (error) {
      console.error("Failed to record audio:", error);
      alert(`Failed to record audio: ${error}`);
    } finally {
      isRecording = false;
    }
  }

  function handleDrop(event: DragEvent) {
    event.preventDefault();
    isDragOver = false;

    const files = event.dataTransfer?.files;
    if (!files?.length) return;

    const file = files[0];
    if (!file.type.startsWith("audio/")) {
      alert("Please drop an audio file");
      return;
    }

    loadAudioFile(file);
  }

  function handleDragOver(event: DragEvent) {
    event.preventDefault();
    isDragOver = true;
  }

  function handleDragLeave(event: DragEvent) {
    event.preventDefault();
    isDragOver = false;
  }

  function showPopup() {
    if (popupTimeout) clearTimeout(popupTimeout);
    showProcessorPopup = true;
    popupTimeout = setTimeout(() => (showProcessorPopup = false), 2000);
  }

  function keepPopupOpen() {
    if (popupTimeout) clearTimeout(popupTimeout);
    showProcessorPopup = true;
  }

  function scheduleHidePopup() {
    if (popupTimeout) clearTimeout(popupTimeout);
    popupTimeout = setTimeout(() => (showProcessorPopup = false), 200);
  }

  function hideProcessorPopup() {
    if (popupTimeout) clearTimeout(popupTimeout);
    showProcessorPopup = false;
  }

  function handleSelectionChange(start: number | null, end: number | null) {
    selectionStart = start;
    selectionEnd = end;
  }

  function clearSelection() {
    selectionStart = null;
    selectionEnd = null;
  }

  function cropSelection() {
    if (!currentBuffer || selectionStart === null || selectionEnd === null)
      return;

    pushState();

    const start = Math.min(selectionStart, selectionEnd);
    const end = Math.max(selectionStart, selectionEnd);

    const sampleRate = audioService.getSampleRate();
    const [newLeft, newRight] = cropAudio(
      currentBuffer,
      start,
      end,
      sampleRate,
    );

    currentBuffer = audioService.createAudioBuffer([newLeft, newRight]);
    clearSelection();
    audioService.play(currentBuffer);
  }

  function cutSelection() {
    if (!currentBuffer || selectionStart === null || selectionEnd === null)
      return;

    pushState();

    const start = Math.min(selectionStart, selectionEnd);
    const end = Math.max(selectionStart, selectionEnd);

    const sampleRate = audioService.getSampleRate();
    const [newLeft, newRight] = cutAudio(currentBuffer, start, end, sampleRate);

    currentBuffer = audioService.createAudioBuffer([newLeft, newRight]);
    clearSelection();
    audioService.play(currentBuffer);
  }

  async function closeModal() {
    showModal = false;
    await audioService.initialize();
  }

  function toggleSidebar() {
    sidebarOpen = !sidebarOpen;
  }
</script>

<svelte:window onkeydown={keyboardHandler} />

<div class="container">
  {#if sidebarOpen}
    <div
      class="sidebar-overlay"
      onclick={toggleSidebar}
      onkeydown={(e) => e.key === "Escape" && toggleSidebar()}
      role="button"
      tabindex="-1"
      aria-label="Close sidebar"
    ></div>
  {/if}
  <div class="top-bar">
    <button
      class="hamburger"
      onclick={toggleSidebar}
      aria-label="Toggle engine menu"
    >
      <svg
        width="24"
        height="24"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
      >
        <line x1="3" y1="6" x2="21" y2="6" />
        <line x1="3" y1="12" x2="21" y2="12" />
        <line x1="3" y1="18" x2="21" y2="18" />
      </svg>
    </button>
    <h1 class="app-title">RSGP: Random Sample Generator and Processor</h1>
    <div class="controls-group">
      {#if showPitchLock}
        <div class="control-item pitch-lock-control">
          <div class="control-header">
            <label for="pitch">Pitch</label>
            <label
              class="custom-checkbox"
              title="Lock pitch across random/mutate"
            >
              <input type="checkbox" bind:checked={pitchLockEnabled} />
              <span class="checkbox-box" class:checked={pitchLockEnabled}>
                {#if pitchLockEnabled}
                  <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
                    <path
                      d="M1 5L4 8L9 2"
                      stroke="currentColor"
                      stroke-width="2"
                      stroke-linecap="square"
                    />
                  </svg>
                {/if}
              </span>
              <span class="checkbox-text">Lock</span>
            </label>
          </div>
          <input
            id="pitch"
            type="text"
            class="pitch-input"
            class:locked={pitchLockEnabled}
            class:invalid={!pitchLockInputValid}
            value={pitchLockInput}
            oninput={handlePitchLockInput}
            placeholder="440 or A4"
            title="Enter frequency (Hz) or note (e.g., A4, C#3)"
          />
        </div>
      {/if}
      {#if showDuration}
        <div class="control-item">
          <div class="control-header">
            <label for="duration">Duration</label>
            <span class="control-value-display">{duration.toFixed(2)}s</span>
          </div>
          <input
            id="duration"
            type="range"
            min="0.05"
            max="32"
            step="0.01"
            bind:value={duration}
          />
        </div>
      {/if}
      <div class="control-item">
        <div class="control-header">
          <label for="volume">Volume</label>
          <span class="control-value-display">{Math.round(volume * 100)}%</span>
        </div>
        <input
          id="volume"
          type="range"
          min="0"
          max="1"
          step="0.01"
          bind:value={volume}
        />
      </div>
    </div>
  </div>

  <div class="content-wrapper">
    <div class="sidebar" class:open={sidebarOpen}>
      <div class="sidebar-content">
        {#each Array.from(enginesByCategory.entries()) as [category, categoryEngines]}
          <div class="category-section">
            <button
              class="category-header"
              class:collapsed={!expandedCategories.has(category)}
              onclick={() => toggleCategory(category)}
            >
              <svg
                class="category-arrow"
                width="12"
                height="12"
                viewBox="0 0 12 12"
                fill="none"
              >
                <path
                  d="M3 4.5L6 7.5L9 4.5"
                  stroke="currentColor"
                  stroke-width="1.5"
                  stroke-linecap="square"
                />
              </svg>
              <span>{category}</span>
            </button>
            {#if expandedCategories.has(category)}
              {#each categoryEngines as currentEngine}
                {@const index = engines.indexOf(currentEngine)}
                <button
                  class="engine-button"
                  class:active={currentEngineIndex === index}
                  data-description={currentEngine.getDescription()}
                  onclick={() => switchEngine(index)}
                >
                  {currentEngine.getName()}
                </button>
              {/each}
            {/if}
          </div>
        {/each}
      </div>
    </div>

    <div class="main-area">
      <div class="waveform-container">
        {#if showFileDropZone}
          <div
            class="file-drop-zone"
            class:drag-over={isDragOver}
            role="button"
            tabindex="0"
            ondrop={handleDrop}
            ondragover={handleDragOver}
            ondragleave={handleDragLeave}
          >
            <div class="drop-zone-content">
              <h2>Drop an audio file here</h2>
              <label for="file-input" class="file-input-label">
                <input
                  id="file-input"
                  type="file"
                  accept="audio/*"
                  onchange={handleFileInput}
                  style="display: none;"
                />
                Choose a file
              </label>
            </div>
          </div>
        {:else}
          <WaveformDisplay
            buffer={currentBuffer}
            color={waveformColor}
            {playbackPosition}
            {cuePoint}
            {selectionStart}
            {selectionEnd}
            onselectionchange={handleSelectionChange}
            onclick={setCuePoint}
          />
        {/if}

        <div class="bottom-controls">
          {#if showRandomButton}
            <button onclick={generateRandom}>Random (R)</button>
          {/if}
          {#if showRecordButton}
            <button onclick={recordAudio} disabled={isRecording}>
              {isRecording ? "Recording..." : "Record"}
            </button>
          {/if}
          {#if showMutateButton}
            <button onclick={mutate}>Mutate (M)</button>
          {/if}
          {#if showEditButtons}
            <button onclick={cropSelection}>Crop</button>
            <button onclick={cutSelection}>Cut</button>
          {/if}
          {#if currentBuffer}
            <div
              class="process-button-container"
              role="group"
              onmouseenter={showPopup}
              onmouseleave={scheduleHidePopup}
            >
              <button onclick={processSound}>Process (P)</button>
              {#if showProcessorPopup}
                <div role="menu" tabindex="-1" onmouseenter={keepPopupOpen} onmouseleave={scheduleHidePopup}>
                  <ProcessorPopup
                    onselect={applyProcessor}
                    selectedCategory={selectedProcessorCategory}
                    onselectcategory={selectProcessorCategory}
                  />
                </div>
              {/if}
            </div>
            <button onclick={download}>Download (D)</button>
            <button onclick={undo} disabled={!canUndo}>Undo (Z)</button>
          {/if}
        </div>
      </div>
      <div class="vu-meter-container">
        <VUMeter buffer={currentBuffer} {playbackPosition} />
      </div>
    </div>
  </div>

  {#if showModal}
    <WelcomeModal onclose={closeModal} />
  {/if}
</div>

<style>
  .container {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
  }

  .top-bar {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.5rem;
    background-color: #1a1a1a;
    border-bottom: 1px solid #333;
    width: 100%;
    box-sizing: border-box;
    overflow: hidden;
  }

  .content-wrapper {
    flex: 1;
    display: flex;
    flex-direction: row;
    overflow: hidden;
  }

  .sidebar {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: -100%;
    top: 0;
    width: 70%;
    max-width: 300px;
    height: 100%;
    background-color: #1a1a1a;
    border-right: 1px solid #333;
    z-index: 2000;
    transition: left 0.3s ease;
  }

  .sidebar.open {
    left: 0;
  }

  .sidebar-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 1999;
  }

  .hamburger {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.25rem;
    background-color: transparent;
    border: 1px solid #3a3a3a;
    color: #fff;
    cursor: pointer;
    transition:
      border-color 0.2s,
      background-color 0.2s;
  }

  .hamburger svg {
    width: 20px;
    height: 20px;
  }

  .hamburger:hover {
    border-color: #646cff;
    background-color: #0f0f0f;
  }

  .app-title {
    font-size: 0.8rem;
    font-weight: 600;
    color: #fff;
    margin: 0;
    padding: 0;
    letter-spacing: 0.05em;
    white-space: nowrap;
  }

  .sidebar-content {
    display: flex;
    flex-direction: column;
    gap: 0;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: #444 transparent;
    height: 100%;
  }

  .sidebar-content::-webkit-scrollbar {
    width: 4px;
  }

  .sidebar-content::-webkit-scrollbar-track {
    background: transparent;
  }

  .sidebar-content::-webkit-scrollbar-thumb {
    background: #444;
    border-radius: 0;
  }

  .category-section {
    display: flex;
    flex-direction: column;
  }

  .category-header {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.5rem 0.5rem;
    background-color: #1a1a1a;
    border: none;
    border-bottom: 1px solid #333;
    color: #999;
    text-align: left;
    cursor: pointer;
    transition:
      color 0.2s,
      background-color 0.2s;
    flex-shrink: 0;
  }

  .category-header:hover {
    color: #ccc;
    background-color: #222;
  }

  .category-arrow {
    transition: transform 0.2s;
    flex-shrink: 0;
  }

  .category-header.collapsed .category-arrow {
    transform: rotate(-90deg);
  }

  .engine-button {
    opacity: 0.7;
    position: relative;
    flex-shrink: 0;
    font-size: 0.75rem;
    padding: 0.5rem 0.5rem 0.5rem 1rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
    border: none;
    border-bottom: 1px solid #2a2a2a;
  }

  .engine-button:hover {
    z-index: 1001;
  }

  .engine-button.active {
    opacity: 1;
    border-color: #646cff;
  }

  .engine-button::after {
    content: attr(data-description);
    position: absolute;
    top: 0;
    left: 100%;
    margin-left: 0.5rem;
    padding: 0.5rem 0.75rem;
    background-color: #0a0a0a;
    border: 1px solid #444;
    color: #ccc;
    font-size: 0.75rem;
    width: 200px;
    white-space: normal;
    word-wrap: break-word;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.2s;
    z-index: 1000;
    display: none;
  }

  .controls-group {
    display: flex;
    flex-direction: row;
    gap: 0.25rem;
    align-items: center;
    margin-left: auto;
    flex-shrink: 1;
    min-width: 0;
  }

  .control-item {
    display: flex;
    flex-direction: row;
    gap: 0.5rem;
    align-items: center;
    background-color: #0f0f0f;
    padding: 0.25rem 0.5rem;
    border: 1px solid #2a2a2a;
    transition: border-color 0.2s;
    flex: 1 1 auto;
    min-width: 0;
  }

  .control-item:hover {
    border-color: #3a3a3a;
  }

  .control-header {
    display: flex;
    align-items: center;
    gap: 0.25rem;
  }

  .control-header label {
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #999;
    font-weight: 500;
    white-space: nowrap;
  }

  .control-value-display {
    font-size: 0.65rem;
    color: #fff;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    min-width: 2rem;
    text-align: right;
  }

  .control-item input[type="range"] {
    width: 100%;
    min-width: 80px;
    max-width: 120px;
    margin: 0;
  }

  .custom-checkbox {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    cursor: pointer;
    user-select: none;
  }

  .custom-checkbox input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
  }

  .checkbox-box {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid #3a3a3a;
    background-color: #1a1a1a;
    transition: all 0.2s;
    color: #0a0a0a;
  }

  .checkbox-box.checked {
    background-color: #646cff;
    border-color: #646cff;
    color: #fff;
  }

  .custom-checkbox:hover .checkbox-box {
    border-color: #4a4a4a;
  }

  .custom-checkbox:hover .checkbox-box.checked {
    background-color: #535bf2;
    border-color: #535bf2;
  }

  .checkbox-text {
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #999;
    font-weight: 500;
  }

  .checkbox-box {
    width: 11px;
    height: 11px;
  }

  .checkbox-box svg {
    width: 7px;
    height: 7px;
  }

  .custom-checkbox:hover .checkbox-text {
    color: #aaa;
  }

  .pitch-input {
    width: 50px;
    min-width: 0;
    padding: 0.2rem 0.3rem;
    background-color: #1a1a1a;
    border: 1px solid #3a3a3a;
    color: #fff;
    font-size: 0.7rem;
    font-weight: 600;
    transition:
      border-color 0.2s,
      background-color 0.2s,
      box-shadow 0.2s;
    font-variant-numeric: tabular-nums;
    box-sizing: border-box;
  }

  .pitch-input:focus {
    outline: none;
    border-color: #646cff;
    background-color: #0a0a0a;
  }

  .pitch-input.locked {
    border-color: #646cff;
    background-color: #0a0a0a;
    box-shadow: 0 0 0 1px rgba(100, 108, 255, 0.2);
  }

  .pitch-input.locked:focus {
    box-shadow: 0 0 0 1px rgba(100, 108, 255, 0.4);
  }

  .pitch-input.invalid {
    border-color: #ff4444;
    box-shadow: 0 0 0 1px rgba(255, 68, 68, 0.2);
  }

  .pitch-input::placeholder {
    color: #555;
    font-weight: 400;
  }

  @media (min-width: 768px) {
    .sidebar {
      position: static;
      left: 0;
      width: 12%;
      min-width: 120px;
      max-width: 200px;
      transition: none;
      height: auto;
    }

    .sidebar-overlay {
      display: none;
    }

    .hamburger {
      display: none;
    }

    .app-title {
      font-size: 1rem;
    }

    .engine-button::after {
      display: block;
    }

    .engine-button:hover::after {
      opacity: 1;
    }
  }

  @media (min-width: 1024px) {
    .sidebar {
      width: 10%;
      min-width: 140px;
      max-width: 180px;
    }
  }

  .main-area {
    flex: 1;
    display: flex;
    flex-direction: column;
    background-color: #0a0a0a;
    overflow: hidden;
  }

  .waveform-container {
    flex: 1;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    min-height: 200px;
  }

  .vu-meter-container {
    width: 100%;
    height: 60px;
    border-top: 1px solid #333;
  }

  .bottom-controls {
    position: absolute;
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
    padding: 0 0.5rem;
    max-width: 95%;
  }

  .bottom-controls button {
    font-size: 0.85rem;
    padding: 0.5rem 0.75rem;
    white-space: nowrap;
  }

  .process-button-container {
    position: relative;
  }

  @media (min-width: 768px) {
    .main-area {
      flex-direction: row;
    }

    .waveform-container {
      min-height: auto;
    }

    .vu-meter-container {
      width: 5%;
      min-width: 50px;
      max-width: 80px;
      height: auto;
      border-left: 1px solid #333;
      border-top: none;
    }

    .bottom-controls {
      gap: 0.75rem;
      bottom: 1.5rem;
    }

    .bottom-controls button {
      font-size: 0.9rem;
      padding: 0.6rem 1rem;
    }
  }

  @media (min-width: 1024px) {
    .bottom-controls {
      gap: 1rem;
      bottom: 2rem;
    }

    .bottom-controls button {
      font-size: 1rem;
      padding: 0.75rem 1.25rem;
    }
  }

  input[type="range"] {
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
    height: 1.5rem;
    border-radius: 0;
  }

  input[type="range"]::-webkit-slider-track {
    background: #333;
    height: 0.375rem;
    border: 1px solid #444;
    border-radius: 0;
  }

  input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 1rem;
    height: 1rem;
    background: #fff;
    border: 1px solid #000;
    border-radius: 0;
    cursor: pointer;
    margin-top: -0.375rem;
  }

  input[type="range"]::-webkit-slider-thumb:hover {
    background: #ddd;
  }

  input[type="range"]::-moz-range-track {
    background: #333;
    height: 0.375rem;
    border: 1px solid #444;
    border-radius: 0;
  }

  input[type="range"]::-moz-range-thumb {
    width: 1rem;
    height: 1rem;
    background: #fff;
    border: 1px solid #000;
    border-radius: 0;
    cursor: pointer;
  }

  input[type="range"]::-moz-range-thumb:hover {
    background: #ddd;
  }

  .file-drop-zone {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px dashed #444;
    background-color: #0a0a0a;
    transition: all 0.2s;
    padding: 1rem;
  }

  .file-drop-zone.drag-over {
    border-color: #646cff;
    background-color: #1a1a1a;
  }

  .drop-zone-content {
    text-align: center;
    color: #ccc;
  }

  .drop-zone-content h2 {
    font-size: 1.2rem;
    margin-bottom: 0.75rem;
    color: #fff;
  }

  .file-input-label {
    display: inline-block;
    padding: 0.65rem 1.25rem;
    background-color: #646cff;
    color: #fff;
    border: 1px solid #646cff;
    cursor: pointer;
    transition: background-color 0.2s;
    font-size: 0.95rem;
  }

  .file-input-label:hover {
    background-color: #535bf2;
  }

  @media (min-width: 768px) {
    .drop-zone-content h2 {
      font-size: 1.5rem;
      margin-bottom: 1rem;
    }

    .file-input-label {
      padding: 0.75rem 1.5rem;
      font-size: 1rem;
    }
  }

  button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
</style>
