<template>
  <view class="audio-player-host">
    <view
      v-if="visible && displayAudio"
      class="audio-player"
      :class="[`mode-${displayMode}`, { 'avoid-bottom-nav': avoidBottomNav }]"
    >
      <view v-if="displayMode === 'expanded'" class="expanded-player">
        <view class="player-handle"></view>
        <view class="expanded-main">
          <image
            v-if="displayAudio.image"
            class="expanded-cover"
            :src="displayAudio.image"
            mode="aspectFill"
          />
          <view class="expanded-copy">
            <text class="player-kicker">讲解音频</text>
            <text class="expanded-title">{{ displayAudio.name }}</text>
            <text class="expanded-subtitle" :class="{ error: displayPlaybackError }">{{ expandedSubtitle }}</text>
          </view>
          <view class="expanded-side">
            <view
              v-if="isLanguageSwitchable"
              class="player-language-switch expanded-language"
              @tap.stop
            >
              <view
                v-for="option in audioLanguageOptions"
                :key="option.value"
                class="player-language-option"
                :class="{
                  active: effectiveAudioLanguage === option.value,
                  pending: displayPendingLanguage === option.value,
                  disabled: isLanguagePending && displayPendingLanguage !== option.value
                }"
                @tap.stop="handleLanguageChange(option.value)"
              >
                <text class="player-language-text">{{ formatLanguageOptionText(option) }}</text>
              </view>
            </view>
            <view v-else class="player-language-state expanded-language">
              <text class="player-language-text">{{ displayAudioLanguageShort }}</text>
            </view>
            <view class="control-btn play-btn expanded-play" @tap.stop="togglePlay">
              <svg v-if="displayPlaying" width="30" height="30" viewBox="0 0 24 24" fill="none">
                <rect x="6" y="5" width="4" height="14" rx="1" fill="currentColor"/>
                <rect x="14" y="5" width="4" height="14" rx="1" fill="currentColor"/>
              </svg>
              <svg v-else width="30" height="30" viewBox="0 0 24 24" fill="none">
                <path d="M8 5V19L19 12L8 5Z" fill="currentColor"/>
              </svg>
            </view>
          </view>
        </view>

        <view class="expanded-progress-row">
          <text class="time-text active">{{ formattedCurrentTime }}</text>
          <view class="progress-track expanded-track" @tap="handleSeek">
            <view class="progress-fill" :style="{ width: progressPercent + '%' }"></view>
            <view class="progress-thumb" :style="{ left: progressPercent + '%' }"></view>
          </view>
          <text class="time-text">{{ formattedDuration }}</text>
        </view>

        <view class="expanded-actions">
          <view class="action-btn collapse-action" @tap.stop="handleCollapse">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
              <path d="M6 15L12 9L18 15" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
            <text class="action-text">收起</text>
          </view>
          <view class="action-divider"></view>
          <view class="action-btn stop-action" @tap.stop="handleStop">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none">
              <rect x="7" y="7" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="2"/>
            </svg>
            <text class="action-text">停止播放</text>
          </view>
        </view>
      </view>

      <view v-else-if="displayMode === 'floating'" class="floating-player" @tap="handleExpand">
        <view class="floating-ring" :style="{ '--progress': progressPercent + '%' }">
          <view class="control-btn floating-play" @tap.stop="togglePlay">
            <svg v-if="displayPlaying" width="24" height="24" viewBox="0 0 24 24" fill="none">
              <rect x="6" y="5" width="4" height="14" rx="1" fill="currentColor"/>
              <rect x="14" y="5" width="4" height="14" rx="1" fill="currentColor"/>
            </svg>
            <svg v-else width="24" height="24" viewBox="0 0 24 24" fill="none">
              <path d="M8 5V19L19 12L8 5Z" fill="currentColor"/>
            </svg>
          </view>
          <text class="floating-label">讲解</text>
          <text class="floating-lang">{{ displayAudioLanguageShort }}</text>
        </view>
      </view>

      <view v-else class="mini-player" @tap="handleExpand">
        <view class="control-btn play-btn mini-play" @tap.stop="togglePlay">
          <svg v-if="displayPlaying" width="26" height="26" viewBox="0 0 24 24" fill="none">
            <rect x="6" y="5" width="4" height="14" rx="1" fill="currentColor"/>
            <rect x="14" y="5" width="4" height="14" rx="1" fill="currentColor"/>
          </svg>
          <svg v-else width="26" height="26" viewBox="0 0 24 24" fill="none">
            <path d="M8 5V19L19 12L8 5Z" fill="currentColor"/>
          </svg>
        </view>

        <view class="mini-copy">
          <text class="player-kicker">讲解音频</text>
          <text class="mini-title">{{ displayAudio.name }}</text>
        </view>

        <view class="mini-progress">
          <view class="progress-track" @tap.stop="handleSeek">
            <view class="progress-fill" :style="{ width: progressPercent + '%' }"></view>
          </view>
        </view>

        <view v-if="isLanguageSwitchable" class="player-language-switch mini-language" @tap.stop>
          <view
            v-for="option in audioLanguageOptions"
            :key="option.value"
            class="player-language-option"
            :class="{
              active: effectiveAudioLanguage === option.value,
              pending: displayPendingLanguage === option.value,
              disabled: isLanguagePending && displayPendingLanguage !== option.value
            }"
            @tap.stop="handleLanguageChange(option.value)"
          >
            <text class="player-language-text">{{ formatLanguageOptionText(option) }}</text>
          </view>
        </view>
        <view v-else class="player-language-state mini-language">
          <text class="player-language-text">{{ displayAudioLanguageShort }}</text>
        </view>

        <view class="expand-action">
          <text class="expand-text">展开</text>
          <svg width="17" height="17" viewBox="0 0 24 24" fill="none">
            <path d="M6 9L12 15L18 9" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
          </svg>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
export interface AudioItem {
  id: string
  name: string
  audioUrl: string
  image?: string
  duration?: number
  language?: AudioLanguage
  supportedLanguages?: AudioLanguage[]
}

export type AudioLanguage = 'zh-CN' | 'en-US'
export type AudioDisplayMode = 'expanded' | 'mini' | 'floating'

const props = defineProps<{
  visible?: boolean
  audio?: AudioItem | null
  autoPlay?: boolean
  avoidBottomNav?: boolean
  mode?: AudioDisplayMode
  controlled?: boolean
  playing?: boolean
  loading?: boolean
  playbackError?: string
  currentTime?: number
  durationValue?: number
  pendingLanguage?: AudioLanguage | ''
}>()

const emit = defineEmits<{
  'update:visible': [value: boolean]
  play: [audio: AudioItem]
  pause: [audio: AudioItem]
  ended: [audio: AudioItem]
  timeUpdate: [currentTime: number]
  error: [audio: AudioItem | null, message: string]
  toggle: []
  close: []
  collapse: []
  stop: []
  expand: []
  seek: [percent: number]
  'language-change': [language: AudioLanguage]
}>()

// 状态
const currentAudio = ref<AudioItem | null>(null)
const isPlaying = ref(false)
const localCurrentTime = ref(0)
const duration = ref(0)
const isLoading = ref(false)
const localPlaybackError = ref('')
const h5AudioElement = ref<HTMLAudioElement | null>(null)

// 内部音频上下文
let innerAudioContext: any = null
let isStoppingAudio = false
let cleanupH5Audio: (() => void) | null = null

// 计算进度百分比
const progressPercent = computed(() => {
  const totalDuration = resolvedDuration.value
  if (totalDuration === 0) return 0
  return (displayCurrentTime.value / totalDuration) * 100
})

const resolvedDuration = computed(() => {
  if (props.controlled) {
    if (props.durationValue && props.durationValue > 0) return props.durationValue
    return props.audio?.duration || 0
  }
  if (duration.value > 0) return duration.value
  return currentAudio.value?.duration || 0
})

const displayAudio = computed(() => props.controlled ? props.audio || null : currentAudio.value)
const displayPlaying = computed(() => props.controlled ? props.playing === true : isPlaying.value)
const displayLoading = computed(() => props.controlled ? props.loading === true : isLoading.value)
const displayPlaybackError = computed(() => props.controlled ? props.playbackError || '' : localPlaybackError.value)
const displayCurrentTime = computed(() => props.controlled ? props.currentTime || 0 : localCurrentTime.value)
const displayMode = computed<AudioDisplayMode>(() => props.mode || 'mini')
const audioLanguageOptions: Array<{ value: AudioLanguage; label: string; shortLabel: string }> = [
  { value: 'zh-CN', label: '中文', shortLabel: '中文' },
  { value: 'en-US', label: 'English', shortLabel: 'EN' }
]
const displayAudioLanguage = computed<AudioLanguage>(() => (
  displayAudio.value?.language === 'en-US' ? 'en-US' : 'zh-CN'
))
const supportedAudioLanguages = computed(() => {
  const languages = displayAudio.value?.supportedLanguages?.length
    ? displayAudio.value.supportedLanguages
    : [displayAudioLanguage.value]

  return Array.from(new Set(languages.filter((language): language is AudioLanguage => (
    language === 'zh-CN' || language === 'en-US'
  ))))
})
const isLanguageSwitchable = computed(() => supportedAudioLanguages.value.length > 1)
const displayPendingLanguage = computed<AudioLanguage | ''>(() => props.pendingLanguage || '')
const effectiveAudioLanguage = computed<AudioLanguage>(() => (
  displayPendingLanguage.value || displayAudioLanguage.value
))
const isLanguagePending = computed(() => Boolean(displayPendingLanguage.value))
const displayAudioLanguageShort = computed(() => (
  displayAudioLanguage.value === 'en-US' ? 'EN' : '中文'
))
const formattedCurrentTime = computed(() => formatTime(displayCurrentTime.value))
const formattedDuration = computed(() => formatTime(resolvedDuration.value))
const expandedSubtitle = computed(() => {
  if (displayPlaybackError.value) return displayPlaybackError.value
  if (displayLoading.value) return '音频加载中'
  if (!displayAudio.value?.audioUrl) return '当前语言暂无语音讲解'
  return displayAudioLanguage.value === 'en-US' ? '英文语音讲解' : '中文语音讲解'
})

// 格式化时间
const formatTime = (seconds: number): string => {
  const mins = Math.floor(seconds / 60)
  const secs = Math.floor(seconds % 60)
  return `${mins}:${secs.toString().padStart(2, '0')}`
}

const formatLanguageOptionText = (option: { shortLabel: string; value: AudioLanguage }) => (
  displayPendingLanguage.value === option.value ? `${option.shortLabel}...` : option.shortLabel
)

// 初始化音频上下文
const initAudio = () => {
  if (h5AudioElement.value) return

  if (typeof uni !== 'undefined' && uni.createInnerAudioContext) {
    innerAudioContext = uni.createInnerAudioContext()
    
    innerAudioContext.onPlay(() => {
      isPlaying.value = true
      isLoading.value = false
      localPlaybackError.value = ''
      if (currentAudio.value) {
        emit('play', currentAudio.value)
      }
    })
    
    innerAudioContext.onPause(() => {
      isPlaying.value = false
    })
    
    innerAudioContext.onStop(() => {
      isPlaying.value = false
      localCurrentTime.value = 0
    })
    
    innerAudioContext.onEnded(() => {
      isPlaying.value = false
      localCurrentTime.value = 0
      if (currentAudio.value) {
        emit('ended', currentAudio.value)
      }
    })
    
    innerAudioContext.onTimeUpdate(() => {
      localCurrentTime.value = innerAudioContext.currentTime
      duration.value = innerAudioContext.duration || 0
      emit('timeUpdate', localCurrentTime.value)
    })
    
    innerAudioContext.onError((err: any) => {
      console.error('音频播放错误:', err)
      handlePlaybackError('音频加载失败，当前提供图文讲解。', true)
    })
    
    innerAudioContext.onWaiting(() => {
      isLoading.value = true
    })
  }
}

const initH5Audio = () => {
  if (h5AudioElement.value || typeof window === 'undefined' || !window.Audio) return

  const audio = new window.Audio()
  audio.preload = 'metadata'
  audio.addEventListener('loadedmetadata', handleH5LoadedMetadata)
  audio.addEventListener('play', handleH5Play)
  audio.addEventListener('pause', handleH5Pause)
  audio.addEventListener('ended', handleH5Ended)
  audio.addEventListener('timeupdate', handleH5TimeUpdate)
  audio.addEventListener('waiting', handleH5Waiting)
  audio.addEventListener('canplay', handleH5CanPlay)
  audio.addEventListener('error', handleH5AudioError)
  h5AudioElement.value = audio

  cleanupH5Audio = () => {
    audio.removeEventListener('loadedmetadata', handleH5LoadedMetadata)
    audio.removeEventListener('play', handleH5Play)
    audio.removeEventListener('pause', handleH5Pause)
    audio.removeEventListener('ended', handleH5Ended)
    audio.removeEventListener('timeupdate', handleH5TimeUpdate)
    audio.removeEventListener('waiting', handleH5Waiting)
    audio.removeEventListener('canplay', handleH5CanPlay)
    audio.removeEventListener('error', handleH5AudioError)
    audio.pause()
    audio.removeAttribute('src')
    audio.load()
    h5AudioElement.value = null
  }
}

const isAutoplayBlocked = (error: unknown) => {
  const name = typeof error === 'object' && error && 'name' in error
    ? String((error as { name?: unknown }).name)
    : ''
  const message = error instanceof Error ? error.message : String(error || '')

  return name === 'NotAllowedError' || /user didn't interact|not allowed|gesture/i.test(message)
}

const showToast = (title: string) => {
  if (typeof uni !== 'undefined') {
    uni.showToast({
      title,
      icon: 'none'
    })
  }
}

const handlePlaybackError = (message: string, shouldClose = false) => {
  isPlaying.value = false
  isLoading.value = false
  localPlaybackError.value = message
  emit('error', currentAudio.value, message)
  showToast(message)

  if (shouldClose) {
    emit('update:visible', false)
  }
}

const resetPlaybackState = (clearAudio = true) => {
  isPlaying.value = false
  isLoading.value = false
  localCurrentTime.value = 0
  duration.value = 0
  localPlaybackError.value = ''

  if (clearAudio) {
    currentAudio.value = null
  }
}

// 播放音频
const playAudio = async (audio: AudioItem) => {
  if (!audio.audioUrl) {
    currentAudio.value = audio
    handlePlaybackError('该讲解暂无已发布音频，当前提供图文讲解。', true)
    return false
  }

  const isNewAudio = currentAudio.value?.id !== audio.id
  currentAudio.value = audio
  localPlaybackError.value = ''
  isLoading.value = true

  if (isNewAudio) {
    localCurrentTime.value = 0
    duration.value = audio.duration || 0
  }

  const h5Audio = h5AudioElement.value
  if (h5Audio) {
    if (h5Audio.getAttribute('src') !== audio.audioUrl) {
      h5Audio.src = audio.audioUrl
      h5Audio.load()
    }

    try {
      await h5Audio.play()
      return true
    } catch (error) {
      console.warn('H5 音频播放被拦截或失败:', error)
      isLoading.value = false
      isPlaying.value = false

      if (isAutoplayBlocked(error)) {
        localPlaybackError.value = '点击播放按钮开始播放'
        return false
      }

      handlePlaybackError('音频暂时无法播放，当前提供图文讲解。', true)
      return false
    }
  }

  if (!innerAudioContext) {
    initAudio()
  }
  
  if (innerAudioContext) {
    innerAudioContext.src = audio.audioUrl
    innerAudioContext.play()
    return true
  }

  handlePlaybackError('当前环境暂不支持音频播放。', true)
  return false
}

// 暂停音频
const pauseAudio = () => {
  if (h5AudioElement.value) {
    h5AudioElement.value.pause()
    return
  }

  if (innerAudioContext) {
    innerAudioContext.pause()
    if (currentAudio.value) {
      emit('pause', currentAudio.value)
    }
  }
}

// 恢复播放
const resumeAudio = () => {
  if (currentAudio.value) {
    void playAudio(currentAudio.value)
    return
  }

  if (innerAudioContext) {
    innerAudioContext.play()
  }
}

// 停止音频
const stopAudio = () => {
  const h5Audio = h5AudioElement.value
  if (h5Audio) {
    isStoppingAudio = true
    h5Audio.pause()
    h5Audio.removeAttribute('src')
    h5Audio.load()
    setTimeout(() => {
      isStoppingAudio = false
    }, 0)
  }

  if (innerAudioContext) {
    innerAudioContext.stop()
  }

  resetPlaybackState()
}

// 切换播放/暂停
const togglePlay = () => {
  if (props.controlled) {
    emit('toggle')
    return
  }

  if (isPlaying.value) {
    pauseAudio()
  } else {
    if (currentAudio.value) {
      resumeAudio()
    }
  }
}

// 跳转到指定位置
const seekTo = (percent: number) => {
  const totalDuration = resolvedDuration.value
  if (totalDuration <= 0) return

  const targetTime = (percent / 100) * totalDuration

  if (h5AudioElement.value) {
    h5AudioElement.value.currentTime = targetTime
    localCurrentTime.value = targetTime
    return
  }

  if (innerAudioContext) {
    innerAudioContext.seek(targetTime)
    localCurrentTime.value = targetTime
  }
}

// 进度条点击
const handleSeek = (e: any) => {
  const rect = e.currentTarget.getBoundingClientRect()
  const x = e.detail.x || (e.touches && e.touches[0]?.clientX - rect.left) || 0
  const percent = (x / rect.width) * 100
  const nextPercent = Math.max(0, Math.min(100, percent))
  if (props.controlled) {
    emit('seek', nextPercent)
    return
  }
  seekTo(nextPercent)
}

// 展开播放器
const handleExpand = () => {
  emit('expand')
}

const handleCollapse = () => {
  emit('collapse')
}

const handleStop = () => {
  if (props.controlled) {
    emit('stop')
    return
  }

  stopAudio()
  emit('stop')
}

const handleH5LoadedMetadata = () => {
  const audio = h5AudioElement.value
  if (!audio) return
  duration.value = Number.isFinite(audio.duration) ? audio.duration : currentAudio.value?.duration || 0
}

const handleH5Play = () => {
  isPlaying.value = true
  isLoading.value = false
  localPlaybackError.value = ''
  if (currentAudio.value) {
    emit('play', currentAudio.value)
  }
}

const handleLanguageChange = (language: AudioLanguage) => {
  if (isLanguagePending.value) return
  if (!supportedAudioLanguages.value.includes(language) || language === displayAudioLanguage.value) return
  emit('language-change', language)
}

const handleH5Pause = () => {
  isPlaying.value = false
  if (currentAudio.value) {
    emit('pause', currentAudio.value)
  }
}

const handleH5Ended = () => {
  isPlaying.value = false
  isLoading.value = false
  localCurrentTime.value = 0
  if (currentAudio.value) {
    emit('ended', currentAudio.value)
  }
}

const handleH5TimeUpdate = () => {
  const audio = h5AudioElement.value
  if (!audio) return
  localCurrentTime.value = audio.currentTime || 0
  duration.value = Number.isFinite(audio.duration) ? audio.duration : duration.value
  emit('timeUpdate', localCurrentTime.value)
}

const handleH5Waiting = () => {
  isLoading.value = true
}

const handleH5CanPlay = () => {
  isLoading.value = false
}

const handleH5AudioError = () => {
  if (isStoppingAudio || !currentAudio.value?.audioUrl) return
  handlePlaybackError('音频加载失败，当前提供图文讲解。', true)
}

// 监听外部 audio 属性变化
watch(() => props.audio, (newAudio) => {
  if (props.controlled) return

  if (newAudio && props.visible && props.autoPlay && currentAudio.value?.id !== newAudio.id) {
    void playAudio(newAudio)
  } else if (newAudio && currentAudio.value?.id !== newAudio.id) {
    currentAudio.value = newAudio
    duration.value = newAudio.duration || 0
  }
}, { immediate: true })

// 监听外部 visible 属性变化
watch(() => props.visible, (visible) => {
  if (props.controlled) return

  if (!visible) {
    stopAudio()
  }
})

onMounted(() => {
  if (props.controlled) return

  initH5Audio()
  initAudio()
})

onUnmounted(() => {
  if (props.controlled) return

  cleanupH5Audio?.()
  cleanupH5Audio = null

  if (innerAudioContext) {
    innerAudioContext.destroy()
    innerAudioContext = null
  }
})

// 暴露方法给外部调用
defineExpose({
  play: playAudio,
  pause: pauseAudio,
  stop: stopAudio,
  seekTo: seekTo,
  isPlaying: () => isPlaying.value,
  currentTime: () => localCurrentTime.value,
  duration: () => duration.value
})
</script>

<style scoped lang="scss">
.audio-player-host {
  display: contents;
}

.native-audio {
  position: fixed;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.audio-player {
  --audio-accent: #e0e100;
  --audio-surface: #050505;
  --audio-text: #ffffff;
  --audio-muted: rgba(255, 255, 255, 0.68);
  position: fixed;
  left: 50%;
  right: auto;
  bottom: calc(12px + env(safe-area-inset-bottom));
  z-index: 2200;
  width: calc(min(100vw, 430px) - 28px);
  box-sizing: border-box;
  transform: translateX(-50%);
  color: var(--audio-text);
  font-family: '鸿蒙黑体', 'HarmonyOS Sans SC', 'HarmonyOS Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

.audio-player.avoid-bottom-nav {
  bottom: calc(82px + env(safe-area-inset-bottom));
}

.audio-player.mode-floating {
  left: auto;
  right: max(16px, calc((100vw - 430px) / 2 + 16px));
  bottom: calc(102px + env(safe-area-inset-bottom));
  width: 116px;
  transform: none;
}

.audio-player.mode-floating.avoid-bottom-nav {
  bottom: calc(104px + env(safe-area-inset-bottom));
}

.expanded-player,
.mini-player,
.floating-ring {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle at 14% 18%, rgba(224, 225, 0, 0.08), transparent 28%),
    linear-gradient(135deg, #161714 0%, #050505 100%);
  border: 1px solid rgba(224, 225, 0, 0.2);
  box-shadow: 0 18px 34px rgba(0, 0, 0, 0.28);
}

.expanded-player::after,
.mini-player::after,
.floating-ring::after {
  content: '';
  position: absolute;
  inset: auto -24px -42px auto;
  width: 150px;
  height: 110px;
  border: 1px solid rgba(224, 225, 0, 0.13);
  border-radius: 50%;
  transform: rotate(-16deg);
  pointer-events: none;
}

.mini-player::after {
  inset: auto 8px 8px auto;
  width: 120px;
  height: 78px;
}

.expanded-player {
  min-height: 292px;
  padding: 28px 22px 16px;
  box-sizing: border-box;
  border-radius: 24px;
}

.player-handle {
  position: absolute;
  top: 11px;
  left: 50%;
  width: 54px;
  height: 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.22);
  transform: translateX(-50%);
}

.expanded-main {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  gap: 14px 16px;
  align-items: center;
}

.expanded-cover {
  width: 92px;
  height: 92px;
  border-radius: 14px;
  background: #262421;
}

.expanded-copy,
.mini-copy {
  min-width: 0;
}

.player-kicker,
.expanded-title,
.expanded-subtitle,
.mini-title,
.time-text,
.action-text,
.floating-label,
.floating-lang,
.expand-text {
  display: block;
}

.player-kicker {
  font-size: 17px;
  line-height: 23px;
  font-weight: 900;
  color: #ffffff;
}

.expanded-title {
  margin-top: 8px;
  font-size: 30px;
  line-height: 37px;
  font-weight: 900;
  color: #ffffff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.expanded-subtitle {
  margin-top: 3px;
  font-size: 15px;
  line-height: 21px;
  color: var(--audio-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.expanded-subtitle.error {
  color: #ffb0a2;
}

.expanded-side {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.control-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 0.2s ease, opacity 0.2s ease;

  &:active {
    transform: scale(0.92);
    opacity: 0.84;
  }
}

.play-btn,
.floating-play {
  flex-shrink: 0;
  background: var(--audio-accent);
  color: #050505;
  border-radius: 50%;
}

.expanded-play {
  width: 64px;
  height: 64px;
}

.expanded-progress-row {
  position: relative;
  z-index: 1;
  margin-top: 22px;
  display: grid;
  grid-template-columns: 66px minmax(0, 1fr) 58px;
  gap: 12px;
  align-items: center;
}

.time-text {
  font-size: 18px;
  line-height: 24px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.66);
}

.time-text.active {
  color: var(--audio-accent);
}

.progress-track {
  position: relative;
  width: 100%;
  height: 5px;
  background: rgba(255, 255, 255, 0.24);
  border-radius: 999px;
  cursor: pointer;
}

.progress-fill {
  position: absolute;
  inset: 0 auto 0 0;
  height: 100%;
  background: var(--audio-accent);
  border-radius: inherit;
  transition: width 0.1s linear;
}

.progress-thumb {
  position: absolute;
  top: 50%;
  width: 18px;
  height: 18px;
  background: var(--audio-accent);
  border-radius: 50%;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.28);
  transform: translate(-50%, -50%);
}

.expanded-actions {
  position: relative;
  z-index: 1;
  margin-top: 24px;
  padding-top: 15px;
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  align-items: center;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}

.action-btn {
  min-width: 0;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  color: #ffffff;
}

.stop-action {
  color: rgba(255, 255, 255, 0.92);
}

.action-divider {
  width: 1px;
  height: 26px;
  background: rgba(255, 255, 255, 0.16);
}

.action-text {
  font-size: 17px;
  line-height: 22px;
  font-weight: 800;
  color: currentColor;
}

.mini-player {
  min-height: 116px;
  padding: 12px 14px 14px;
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto auto;
  grid-template-areas:
    "play spacer language expand"
    "copy copy copy copy"
    "progress progress progress progress";
  column-gap: 10px;
  row-gap: 7px;
  align-items: center;
  box-sizing: border-box;
  border-radius: 30px;
}

.mini-play {
  grid-area: play;
  width: 56px;
  height: 56px;
}

.mini-copy {
  grid-area: copy;
  display: flex;
  align-items: baseline;
  gap: 8px;
  overflow: hidden;
}

.mini-copy .player-kicker {
  flex: 0 0 auto;
  white-space: nowrap;
}

.mini-title {
  flex: 1 1 auto;
  min-width: 0;
  margin-top: 2px;
  font-size: 22px;
  line-height: 28px;
  font-weight: 900;
  color: #ffffff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mini-progress {
  grid-area: progress;
  min-width: 0;
  width: 100%;
}

.mini-progress .progress-track {
  height: 4px;
}

.player-language-state,
.player-language-switch {
  flex-shrink: 0;
  min-width: 56px;
  height: 34px;
  padding: 0 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-radius: 999px;
  border: 1px solid rgba(224, 225, 0, 0.52);
  background: rgba(255, 255, 255, 0.02);
  color: var(--audio-accent);
}

.mini-language {
  grid-area: language;
  justify-self: end;
}

.player-language-switch {
  min-width: 92px;
  padding: 3px;
  justify-content: flex-start;
  gap: 2px;
}

.player-language-option {
  min-width: 40px;
  height: 26px;
  padding: 0 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-radius: 999px;
  color: #ffffff;
}

.player-language-option.active {
  background: var(--audio-accent);
  color: #050505;
}

.player-language-option.pending {
  background: rgba(224, 225, 0, 0.76);
  color: #050505;
  box-shadow: 0 0 0 1px rgba(224, 225, 0, 0.26);
}

.player-language-option.disabled {
  opacity: 0.52;
}

.player-language-text {
  font-size: 14px;
  line-height: 18px;
  font-weight: 900;
  color: currentColor;
}

.expand-action {
  grid-area: expand;
  min-width: 70px;
  height: 42px;
  padding-left: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  box-sizing: border-box;
  border-left: 1px solid rgba(255, 255, 255, 0.16);
  color: #ffffff;
}

.expand-text {
  font-size: 18px;
  line-height: 22px;
  font-weight: 900;
  color: currentColor;
}

.floating-player {
  width: 116px;
  height: 116px;
}

.floating-ring {
  width: 116px;
  height: 116px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-radius: 50%;
}

.floating-ring::before {
  content: '';
  position: absolute;
  inset: 8px;
  border-radius: 50%;
  background:
    conic-gradient(var(--audio-accent) 0 var(--progress), rgba(255, 255, 255, 0.24) var(--progress) 100%);
  mask: radial-gradient(circle, transparent 65%, #000 66%);
  pointer-events: none;
}

.floating-play {
  position: relative;
  z-index: 1;
  width: 48px;
  height: 48px;
}

.floating-label {
  position: relative;
  z-index: 1;
  margin-top: 7px;
  font-size: 18px;
  line-height: 22px;
  font-weight: 900;
  color: #ffffff;
}

.floating-lang {
  position: relative;
  z-index: 1;
  min-width: 46px;
  height: 23px;
  margin-top: 2px;
  padding: 0 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border: 1px solid rgba(224, 225, 0, 0.7);
  border-radius: 999px;
  font-size: 13px;
  line-height: 18px;
  font-weight: 900;
  color: var(--audio-accent);
}

@media (min-width: 768px) {
  .audio-player:not(.mode-floating) {
    width: 402px;
  }
}

@media (max-width: 390px) {
  .expanded-player {
    padding-left: 18px;
    padding-right: 18px;
  }

  .expanded-main {
    grid-template-columns: 76px minmax(0, 1fr);
  }

  .expanded-cover {
    width: 76px;
    height: 76px;
  }

  .expanded-title {
    font-size: 26px;
    line-height: 32px;
  }

  .mini-player {
    min-height: 110px;
    padding: 10px 12px 12px;
    grid-template-columns: 50px minmax(0, 1fr) auto;
    grid-template-areas:
      "play language expand"
      "copy copy copy"
      "progress progress progress";
    column-gap: 8px;
    row-gap: 6px;
  }

  .mini-play {
    width: 50px;
    height: 50px;
  }

  .mini-title {
    font-size: 21px;
    line-height: 27px;
  }
}
</style>
