<script>
  import { createEventDispatcher } from "svelte";

  export let selectedPreset = "cyanPink";
  export let logoUrl = "";
  export let title = "Audio Visualizer";
  export let subtitle = "";
  export let bgImage = "";
  export let bgColor = "#0a0a0a";
  export let vignette = true;
  export let audioSource = "mic";
  export let customPrimary = "#00d4ff";
  export let customSecondary = "#ff006e";
  export let useCustomColors = false;
  export let isListening = false;
  export let colorPresets = {};
  export let onToggle = () => {};

  export let audioUrl = "";

  // File playback state
  export let fileCurrentTime = 0;
  export let fileDuration = 0;
  export let filePlaying = false;
  export let fileName = "";

  function formatTime(seconds) {
    if (!seconds || !isFinite(seconds)) return "0:00";
    const m = Math.floor(seconds / 60);
    const s = Math.floor(seconds % 60);
    return `${m}:${s.toString().padStart(2, "0")}`;
  }

  function handleSeek(e) {
    dispatch("fileseek", parseFloat(e.target.value));
  }

  function toggleFilePlayback() {
    if (filePlaying) {
      dispatch("filepause");
    } else {
      dispatch("fileresume");
    }
  }

  // New props
  export let vizMode = "circular";
  export let barCount = 48;
  export let sensitivity = 1.0;
  export let smoothing = 0.82;
  export let bassEmphasis = 0.25;
  export let particlesEnabled = true;
  export let particleDensity = 1.0;
  export let shakeAmount = 1.0;
  export let zoomIntensity = 1.0;
  export let glowIntensity = 0.6;
  export let logoSpin = false;
  export let logoSpinSpeed = 5;
  export let titlePosition = "top";

  const dispatch = createEventDispatcher();

  let fileInput;
  let bgFileInput;
  let audioFileInput;
  let dragOver = false;

  // Detect if browser supports tab audio capture (Chrome/Edge only)
  const isChromium =
    /Chrome/.test(navigator.userAgent) && !/Edg/.test(navigator.userAgent);
  const isEdge = /Edg/.test(navigator.userAgent);
  const supportsTabAudio = isChromium || isEdge;

  const vizModes = [
    { key: "circular", name: "Circular" },
    { key: "bars", name: "Bars" },
    { key: "wave", name: "Wave" },
    { key: "blob", name: "Blob" },
  ];

  function readFileAsDataUrl(file, callback) {
    const reader = new FileReader();
    reader.onload = (e) => callback(e.target.result);
    reader.readAsDataURL(file);
  }

  function handleFileUpload(e) {
    const file = e.target.files[0];
    if (file) readFileAsDataUrl(file, (url) => (logoUrl = url));
  }

  function handleBgUpload(e) {
    const file = e.target.files[0];
    if (file) readFileAsDataUrl(file, (url) => (bgImage = url));
  }

  function handleAudioFile(e) {
    const file = e.target.files[0];
    if (file && file.type.startsWith("audio/")) {
      audioSource = "file";
      dispatch("audiofile", file);
    }
  }

  function handleDrop(e) {
    e.preventDefault();
    dragOver = false;
    const file = e.dataTransfer.files[0];
    if (file && file.type.startsWith("audio/")) {
      audioSource = "file";
      dispatch("audiofile", file);
    }
  }

  function handleDragOver(e) {
    e.preventDefault();
    dragOver = true;
  }

  function clearLogo() {
    logoUrl = "";
    if (fileInput) fileInput.value = "";
  }

  function clearBg() {
    bgImage = "";
    if (bgFileInput) bgFileInput.value = "";
  }

  function selectPreset(key) {
    selectedPreset = key;
    useCustomColors = false;
  }

  function activateCustom() {
    useCustomColors = true;
  }

  function getEmbedUrl(url) {
    if (!url) return "";
    try {
      // YouTube
      let match = url.match(
        /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)/,
      );
      if (match) return `https://www.youtube.com/embed/${match[1]}?autoplay=1`;

      // Spotify
      match = url.match(/open\.spotify\.com\/(track|album|playlist)\/([\w]+)/);
      if (match)
        return `https://open.spotify.com/embed/${match[1]}/${match[2]}?utm_source=generator&theme=0`;

      // SoundCloud — use their oEmbed widget
      if (url.includes("soundcloud.com"))
        return `https://w.soundcloud.com/player/?url=${encodeURIComponent(url)}&auto_play=true&color=%2300d4ff&hide_related=true&show_comments=false&show_user=false&show_reposts=false`;

      // Direct audio URL — won't embed, handled separately
      return "";
    } catch {
      return "";
    }
  }

  $: embedUrl = getEmbedUrl(audioUrl);
  $: isYouTubeUrl =
    audioUrl &&
    /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)/.test(
      audioUrl,
    );

  function handleScreenshot() {
    dispatch("screenshot");
  }

  function handleFullscreen() {
    dispatch("fullscreen");
  }

  function handleReset() {
    dispatch("reset");
  }
</script>

<div class="controls">
  <!-- Audio Section -->
  <div class="control-section">
    <h3>Audio</h3>
    <div class="button-row">
      <button class="btn" class:active={isListening} on:click={onToggle}>
        {#if isListening}
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="20"
            height="20"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <rect x="6" y="6" width="12" height="12" rx="2" />
          </svg>
          Stop
        {:else}
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="20"
            height="20"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
          >
            <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
            <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
            <line x1="12" y1="19" x2="12" y2="23" />
            <line x1="8" y1="23" x2="16" y2="23" />
          </svg>
          Start
        {/if}
      </button>
    </div>
    <div class="source-toggle">
      <button
        class="source-btn"
        class:active={audioSource === "mic"}
        on:click={() => (audioSource = "mic")}>Mic</button
      >
      <button
        class="source-btn"
        class:active={audioSource === "tab"}
        on:click={() => (audioSource = "tab")}>Tab</button
      >
      <button
        class="source-btn"
        class:active={audioSource === "link"}
        on:click={() => (audioSource = "link")}>Link</button
      >
      <button
        class="source-btn"
        class:active={audioSource === "file"}
        on:click={() => audioFileInput.click()}>File</button
      >
    </div>
    <input
      type="file"
      accept="audio/*"
      on:change={handleAudioFile}
      bind:this={audioFileInput}
      id="audio-upload"
    />
    {#if audioSource === "tab"}
      {#if supportsTabAudio}
        <p class="hint">
          Click Start, pick a tab playing music, and check <strong
            >"Share tab audio"</strong
          >. Direct audio capture — no mic needed.
        </p>
      {:else}
        <p class="hint error-hint">
          Tab audio capture is only supported in Chrome and Edge. Use <strong
            >Link</strong
          >
          or <strong>Mic</strong> instead.
        </p>
      {/if}
    {:else if audioSource === "link"}
      <input
        type="text"
        class="text-input url-input"
        bind:value={audioUrl}
        placeholder="Paste YouTube, Spotify, or SoundCloud link..."
      />
      {#if embedUrl}
        {#if isYouTubeUrl}
          <a
            class="open-link-btn"
            href={audioUrl}
            target="_blank"
            rel="noopener noreferrer"
          >
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="14"
              height="14"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              ><path
                d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"
              /><polyline points="15 3 21 3 21 9" /><line
                x1="10"
                y1="14"
                x2="21"
                y2="3"
              /></svg
            >
            Open on YouTube
          </a>
          <p class="hint">
            Play the video in the new tab, then click Start here. Uses your mic
            to capture the audio.
          </p>
        {:else}
          <div class="embed-player">
            <iframe
              src={embedUrl}
              title="Music Player"
              allow="autoplay; encrypted-media"
              frameborder="0"
              width="100%"
              height="80"
            ></iframe>
          </div>
          <p class="hint">
            Play the music above, then click Start. Uses your mic to capture the
            audio.
          </p>
        {/if}
      {:else if audioUrl}
        <p class="hint error-hint">
          Could not parse this URL. Try a YouTube, Spotify, or SoundCloud link.
        </p>
      {/if}
    {/if}
    <!-- svelte-ignore a11y-no-static-element-interactions -->
    <div
      class="drop-zone"
      class:drag-over={dragOver}
      on:drop={handleDrop}
      on:dragover={handleDragOver}
      on:dragleave={() => (dragOver = false)}
    >
      Drop audio file here
    </div>
    {#if fileName}
      <div class="file-player">
        <div class="file-name" title={fileName}>
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="12"
            height="12"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            ><path d="M9 18V5l12-2v13" /><circle cx="6" cy="18" r="3" /><circle
              cx="18"
              cy="16"
              r="3"
            /></svg
          >
          <span>{fileName}</span>
        </div>
        <div class="file-controls">
          <button
            class="play-pause-btn"
            on:click={toggleFilePlayback}
            disabled={!isListening}
          >
            {#if filePlaying}
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="currentColor"
                ><rect x="6" y="4" width="4" height="16" /><rect
                  x="14"
                  y="4"
                  width="4"
                  height="16"
                /></svg
              >
            {:else}
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="currentColor"><polygon points="5 3 19 12 5 21 5 3" /></svg
              >
            {/if}
          </button>
          <span class="file-time">{formatTime(fileCurrentTime)}</span>
          <input
            type="range"
            class="seek-slider"
            min="0"
            max={fileDuration || 0}
            step="0.1"
            value={fileCurrentTime}
            on:input={handleSeek}
            disabled={!isListening}
          />
          <span class="file-time">{formatTime(fileDuration)}</span>
        </div>
        {#if !isListening}
          <p class="hint">File loaded. Click Start to begin playback.</p>
        {/if}
      </div>
    {/if}
  </div>

  <!-- Visualization Mode -->
  <div class="control-section">
    <h3>Visualization</h3>
    <div class="source-toggle">
      {#each vizModes as mode}
        <button
          class="source-btn"
          class:active={vizMode === mode.key}
          on:click={() => (vizMode = mode.key)}>{mode.name}</button
        >
      {/each}
    </div>
    <div class="slider-row">
      <label for="slider-bars"
        >Bars <span class="slider-val">{barCount}</span></label
      >
      <input
        id="slider-bars"
        type="range"
        min="16"
        max="96"
        step="2"
        bind:value={barCount}
      />
    </div>
  </div>

  <!-- Title Position -->
  <div class="control-section">
    <h3>Title Position</h3>
    <div class="position-grid">
      <div class="pos-row">
        <button
          class="pos-btn"
          class:active={titlePosition === "top-left"}
          on:click={() => (titlePosition = "top-left")}>&#8598;</button
        >
        <button
          class="pos-btn"
          class:active={titlePosition === "top"}
          on:click={() => (titlePosition = "top")}>&#8593;</button
        >
        <button
          class="pos-btn"
          class:active={titlePosition === "top-right"}
          on:click={() => (titlePosition = "top-right")}>&#8599;</button
        >
      </div>
      <div class="pos-row">
        <button
          class="pos-btn"
          class:active={titlePosition === "left"}
          on:click={() => (titlePosition = "left")}>&#8592;</button
        >
        <button
          class="pos-btn"
          class:active={titlePosition === "center"}
          on:click={() => (titlePosition = "center")}>&#9673;</button
        >
        <button
          class="pos-btn"
          class:active={titlePosition === "right"}
          on:click={() => (titlePosition = "right")}>&#8594;</button
        >
      </div>
      <div class="pos-row">
        <button
          class="pos-btn"
          class:active={titlePosition === "bottom-left"}
          on:click={() => (titlePosition = "bottom-left")}>&#8601;</button
        >
        <button
          class="pos-btn"
          class:active={titlePosition === "bottom"}
          on:click={() => (titlePosition = "bottom")}>&#8595;</button
        >
        <button
          class="pos-btn"
          class:active={titlePosition === "bottom-right"}
          on:click={() => (titlePosition = "bottom-right")}>&#8600;</button
        >
      </div>
    </div>
  </div>

  <!-- Audio Reactivity -->
  <div class="control-section">
    <h3>Reactivity</h3>
    <div class="slider-row">
      <label for="slider-sensitivity"
        >Sensitivity <span class="slider-val">{sensitivity.toFixed(1)}</span
        ></label
      >
      <input
        id="slider-sensitivity"
        type="range"
        min="0.2"
        max="3.0"
        step="0.1"
        bind:value={sensitivity}
      />
    </div>
    <div class="slider-row">
      <label for="slider-smoothing"
        >Smoothing <span class="slider-val">{smoothing.toFixed(2)}</span></label
      >
      <input
        id="slider-smoothing"
        type="range"
        min="0.0"
        max="0.99"
        step="0.01"
        bind:value={smoothing}
      />
    </div>
    <div class="slider-row">
      <label for="slider-bass"
        >Bass Emphasis <span class="slider-val">{bassEmphasis.toFixed(2)}</span
        ></label
      >
      <input
        id="slider-bass"
        type="range"
        min="0.0"
        max="2.0"
        step="0.05"
        bind:value={bassEmphasis}
      />
    </div>
  </div>

  <!-- Effects -->
  <div class="control-section">
    <h3>Effects</h3>
    <label class="toggle-row">
      <input type="checkbox" bind:checked={particlesEnabled} />
      <span>Particles</span>
    </label>
    {#if particlesEnabled}
      <div class="slider-row">
        <label for="slider-density"
          >Density <span class="slider-val">{particleDensity.toFixed(1)}</span
          ></label
        >
        <input
          id="slider-density"
          type="range"
          min="0.1"
          max="3.0"
          step="0.1"
          bind:value={particleDensity}
        />
      </div>
    {/if}
    <div class="slider-row">
      <label for="slider-shake"
        >Shake <span class="slider-val">{shakeAmount.toFixed(1)}</span></label
      >
      <input
        id="slider-shake"
        type="range"
        min="0.0"
        max="3.0"
        step="0.1"
        bind:value={shakeAmount}
      />
    </div>
    <div class="slider-row">
      <label for="slider-zoom"
        >Zoom <span class="slider-val">{zoomIntensity.toFixed(1)}</span></label
      >
      <input
        id="slider-zoom"
        type="range"
        min="0.0"
        max="3.0"
        step="0.1"
        bind:value={zoomIntensity}
      />
    </div>
    <div class="slider-row">
      <label for="slider-glow"
        >Glow <span class="slider-val">{glowIntensity.toFixed(1)}</span></label
      >
      <input
        id="slider-glow"
        type="range"
        min="0.0"
        max="2.0"
        step="0.1"
        bind:value={glowIntensity}
      />
    </div>
  </div>

  <!-- Branding -->
  <div class="control-section">
    <h3>Branding</h3>
    <input
      type="text"
      class="text-input"
      bind:value={title}
      placeholder="Title..."
    />
    <input
      type="text"
      class="text-input"
      bind:value={subtitle}
      placeholder="Subtitle..."
      style="margin-top: 0.35rem"
    />
    <div class="inline-row" style="margin-top: 0.5rem">
      <input
        type="file"
        accept="image/*"
        on:change={handleFileUpload}
        bind:this={fileInput}
        id="logo-upload"
      />
      <label for="logo-upload" class="upload-btn">Logo</label>
      {#if logoUrl}
        <button class="clear-btn" on:click={clearLogo}>Clear</button>
      {/if}
    </div>
    {#if logoUrl}
      <div class="slider-row" style="margin-top: 0.5rem">
        <label>
          <input type="checkbox" bind:checked={logoSpin} /> Spin Logo
        </label>
      </div>
      {#if logoSpin}
        <div class="slider-row">
          <label for="slider-spin-speed"
            >Speed <span class="slider-val">{logoSpinSpeed.toFixed(1)}</span
            ></label
          >
          <input
            id="slider-spin-speed"
            type="range"
            min="0.5"
            max="20"
            step="0.5"
            bind:value={logoSpinSpeed}
          />
        </div>
      {/if}
    {/if}
  </div>

  <!-- Colors -->
  <div class="control-section">
    <h3>Colors</h3>
    <div class="preset-buttons">
      {#each Object.entries(colorPresets) as [key, preset]}
        <button
          class="preset-btn"
          class:active={selectedPreset === key && !useCustomColors}
          style="--preset-color: {preset.primary}"
          on:click={() => selectPreset(key)}
        >
          <span
            class="preset-dot"
            style="background: linear-gradient(135deg, {preset.primary}, {preset.secondary ||
              preset.primary})"
          ></span>
          {preset.name}
        </button>
      {/each}
    </div>
    <div class="custom-colors" style="margin-top: 0.5rem">
      <div class="color-row">
        <label for="color-outer">Outer</label>
        <input
          id="color-outer"
          type="color"
          bind:value={customPrimary}
          on:input={activateCustom}
        />
        <input
          type="text"
          class="hex-input"
          bind:value={customPrimary}
          on:input={activateCustom}
          placeholder="#00d4ff"
        />
      </div>
      <div class="color-row">
        <label for="color-inner">Inner</label>
        <input
          id="color-inner"
          type="color"
          bind:value={customSecondary}
          on:input={activateCustom}
        />
        <input
          type="text"
          class="hex-input"
          bind:value={customSecondary}
          on:input={activateCustom}
          placeholder="#ff006e"
        />
      </div>
    </div>
  </div>

  <!-- Background -->
  <div class="control-section">
    <h3>Background</h3>
    <div class="inline-row">
      <input
        type="file"
        accept="image/*"
        on:change={handleBgUpload}
        bind:this={bgFileInput}
        id="bg-upload"
      />
      <label for="bg-upload" class="upload-btn">Image</label>
      {#if bgImage}
        <button class="clear-btn" on:click={clearBg}>Clear</button>
      {/if}
      <input
        type="color"
        class="color-picker"
        bind:value={bgColor}
        title="Background Color"
      />
    </div>
    <label class="toggle-row">
      <input type="checkbox" bind:checked={vignette} />
      <span>Vignette</span>
    </label>
  </div>

  <!-- Actions -->
  <div class="control-section">
    <h3>Actions</h3>
    <div class="button-row action-row">
      <button class="action-btn" on:click={handleScreenshot} title="Screenshot">
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="16"
          height="16"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
        >
          <path
            d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"
          />
          <circle cx="12" cy="13" r="4" />
        </svg>
        Screenshot
      </button>
      <button class="action-btn" on:click={handleFullscreen} title="Fullscreen">
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="16"
          height="16"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
        >
          <path
            d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"
          />
        </svg>
        Fullscreen
      </button>
      <button class="action-btn reset-btn" on:click={handleReset} title="Reset">
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="16"
          height="16"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
        >
          <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
          <path d="M3 3v5h5" />
        </svg>
        Reset
      </button>
    </div>
  </div>

  <div class="keyboard-hints">
    <span>Space</span> Play/Stop &nbsp;
    <span>F</span> Fullscreen &nbsp;
    <span>H</span> Hide Panel
  </div>
</div>

<style>
  .controls {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
  }

  .control-section {
    width: 100%;
  }

  h3 {
    color: #888;
    font-size: 0.75rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: 0.75rem;
  }

  .button-row {
    display: flex;
    gap: 0.5rem;
  }

  .btn {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1.25rem;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 8px;
    color: #fff;
    cursor: pointer;
    font-size: 0.9rem;
    transition: all 0.2s ease;
  }

  .btn:hover {
    background: rgba(255, 255, 255, 0.15);
  }

  .btn.active {
    background: rgba(255, 50, 50, 0.2);
    border-color: rgba(255, 50, 50, 0.5);
    color: #ff6b6b;
  }

  .preset-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .preset-btn {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.75rem;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    color: #aaa;
    cursor: pointer;
    font-size: 0.8rem;
    transition: all 0.2s ease;
  }

  .preset-btn:hover {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
  }

  .preset-btn.active {
    border-color: var(--preset-color);
    color: #fff;
    box-shadow: 0 0 10px var(--preset-color);
  }

  .preset-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    flex-shrink: 0;
  }

  .inline-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  input[type="file"] {
    display: none;
  }

  .upload-btn {
    padding: 0.5rem 1rem;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 6px;
    color: #fff;
    cursor: pointer;
    font-size: 0.85rem;
  }

  .upload-btn:hover {
    background: rgba(255, 255, 255, 0.15);
  }

  .clear-btn {
    padding: 0.5rem 0.75rem;
    background: rgba(255, 100, 100, 0.1);
    border: 1px solid rgba(255, 100, 100, 0.3);
    border-radius: 6px;
    color: #ff6b6b;
    cursor: pointer;
    font-size: 0.8rem;
  }

  .clear-btn:hover {
    background: rgba(255, 100, 100, 0.2);
  }

  .text-input {
    width: 100%;
    padding: 0.5rem 0.75rem;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 6px;
    color: #fff;
    font-size: 0.85rem;
    box-sizing: border-box;
  }

  .text-input:focus {
    outline: none;
    border-color: rgba(255, 255, 255, 0.4);
  }

  .color-picker {
    width: 36px;
    height: 30px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    background: none;
  }

  .custom-colors {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  .color-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .color-row label {
    color: #888;
    font-size: 0.75rem;
    min-width: 60px;
  }

  .color-row input[type="color"] {
    width: 30px;
    height: 26px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    background: none;
  }

  .hex-input {
    width: 80px;
    padding: 0.3rem 0.5rem;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 4px;
    color: #fff;
    font-size: 0.75rem;
    font-family: monospace;
  }

  .hex-input:focus {
    outline: none;
    border-color: rgba(255, 255, 255, 0.4);
  }

  .toggle-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    cursor: pointer;
    color: #aaa;
    font-size: 0.85rem;
  }

  .toggle-row input[type="checkbox"] {
    accent-color: #00d4ff;
    cursor: pointer;
  }

  .source-toggle {
    display: flex;
    gap: 0;
    margin-top: 0.5rem;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.15);
  }

  .source-btn {
    flex: 1;
    padding: 0.4rem 0.5rem;
    background: rgba(255, 255, 255, 0.05);
    border: none;
    color: #888;
    font-size: 0.75rem;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .source-btn:not(:last-child) {
    border-right: 1px solid rgba(255, 255, 255, 0.15);
  }

  .source-btn.active {
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
  }

  .source-btn:hover {
    background: rgba(255, 255, 255, 0.1);
  }

  .drop-zone {
    margin-top: 0.5rem;
    padding: 0.6rem;
    border: 1px dashed rgba(255, 255, 255, 0.15);
    border-radius: 6px;
    text-align: center;
    color: #555;
    font-size: 0.75rem;
    transition: all 0.2s ease;
  }

  .drop-zone.drag-over {
    border-color: #00d4ff;
    color: #00d4ff;
    background: rgba(0, 212, 255, 0.05);
  }

  .slider-row {
    margin-top: 0.5rem;
  }

  .slider-row label {
    display: flex;
    justify-content: space-between;
    color: #888;
    font-size: 0.75rem;
    margin-bottom: 0.25rem;
  }

  .slider-val {
    color: #bbb;
    font-family: monospace;
  }

  .slider-row input[type="range"] {
    width: 100%;
    height: 4px;
    -webkit-appearance: none;
    appearance: none;
    background: rgba(255, 255, 255, 0.15);
    border-radius: 2px;
    outline: none;
    cursor: pointer;
  }

  .slider-row input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #00d4ff;
    cursor: pointer;
    border: none;
  }

  .slider-row input[type="range"]::-moz-range-thumb {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #00d4ff;
    cursor: pointer;
    border: none;
  }

  .action-row {
    flex-wrap: wrap;
  }

  .action-btn {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.75rem;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 6px;
    color: #aaa;
    cursor: pointer;
    font-size: 0.8rem;
    transition: all 0.2s ease;
  }

  .action-btn:hover {
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
  }

  .action-btn.reset-btn {
    border-color: rgba(255, 100, 100, 0.3);
    color: #ff6b6b;
  }

  .action-btn.reset-btn:hover {
    background: rgba(255, 100, 100, 0.15);
  }

  .keyboard-hints {
    padding-top: 0.5rem;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    color: #444;
    font-size: 0.65rem;
    text-align: center;
    line-height: 1.8;
  }

  .keyboard-hints span {
    display: inline-block;
    padding: 0.1rem 0.4rem;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 3px;
    color: #666;
    font-family: monospace;
    font-size: 0.65rem;
  }

  .url-input {
    margin-top: 0.5rem;
  }

  .embed-player {
    margin-top: 0.5rem;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.1);
  }

  .embed-player iframe {
    display: block;
  }

  .hint {
    margin: 0.4rem 0 0 0;
    color: #666;
    font-size: 0.7rem;
    line-height: 1.4;
  }

  .error-hint {
    color: #ff6b6b;
  }

  .hint strong {
    color: #aaa;
  }

  .open-link-btn {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: rgba(255, 0, 0, 0.08);
    border: 1px solid rgba(255, 0, 0, 0.25);
    border-radius: 6px;
    color: #ff4444;
    font-size: 0.8rem;
    text-decoration: none;
    transition: all 0.2s ease;
    width: fit-content;
  }

  .open-link-btn:hover {
    background: rgba(255, 0, 0, 0.15);
    color: #ff6666;
  }

  .file-player {
    margin-top: 0.6rem;
    padding: 0.6rem;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 8px;
  }

  .file-name {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: #bbb;
    font-size: 0.75rem;
    margin-bottom: 0.5rem;
    overflow: hidden;
  }

  .file-name span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .file-controls {
    display: flex;
    align-items: center;
    gap: 0.4rem;
  }

  .play-pause-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .play-pause-btn:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.2);
  }

  .play-pause-btn:disabled {
    opacity: 0.3;
    cursor: not-allowed;
  }

  .file-time {
    color: #666;
    font-size: 0.65rem;
    font-family: monospace;
    flex-shrink: 0;
    min-width: 32px;
    text-align: center;
  }

  .seek-slider {
    flex: 1;
    height: 4px;
    -webkit-appearance: none;
    appearance: none;
    background: rgba(255, 255, 255, 0.15);
    border-radius: 2px;
    outline: none;
    cursor: pointer;
    min-width: 0;
  }

  .seek-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #00d4ff;
    cursor: pointer;
    border: none;
  }

  .seek-slider::-moz-range-thumb {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #00d4ff;
    cursor: pointer;
    border: none;
  }

  .seek-slider:disabled {
    opacity: 0.3;
    cursor: not-allowed;
  }

  .position-grid {
    display: flex;
    flex-direction: column;
    gap: 3px;
    width: fit-content;
  }

  .pos-row {
    display: flex;
    gap: 3px;
  }

  .pos-btn {
    width: 60px;
    height: 28px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 4px;
    color: #888;
    font-size: 0.65rem;
    cursor: pointer;
    transition: all 0.2s ease;
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }

  .pos-btn:hover {
    background: rgba(255, 255, 255, 0.1);
    color: #ccc;
  }

  .pos-btn.active {
    background: rgba(0, 212, 255, 0.15);
    border-color: rgba(0, 212, 255, 0.4);
    color: #00d4ff;
  }

  .pos-spacer {
    width: 60px;
    height: 28px;
  }
</style>
