안녕하세요, HolySheep AI 기술 블로그입니다. 오늘은 전 세계 개발자들이 가장 많이 요청하는 음성 인식 기술 중 하나인 Whisper large-v3를 활용한 실시간 전사 구현 방법을 상세히 안내드리겠습니다.

저는 실제 영상 자막 생성, 회의록 자동화, 실시간 번역 시스템 등을 HolySheep AI 기반으로 구축한 경험이 있습니다. 이 튜토리얼에서는 브라우저 기반 실시간 전사부터 Python 백엔드 연동까지, production-ready한 코드를 공유하겠습니다.

왜 HolySheep AI인가?

AI API 비용 최적화는 모든 개발자의 핵심 관심사입니다. 월 1,000만 토큰 기준 주요 모델 비용을 비교해 보겠습니다:

모델providers월 1,000만 토큰 비용
GPT-4.1OpenAI$8.00
Claude Sonnet 4.5Anthropic$15.00
Gemini 2.5 FlashGoogle$2.50
DeepSeek V3.2DeepSeek$0.42
Whisper large-v3HolySheep AI경쟁력 있는 가격

지금 가입하면 무료 크레딧을 즉시 받으실 수 있으며, 해외 신용카드 없이도 로컬 결제로 시작할 수 있습니다.

실시간 전사 아키텍처 개요

Whisper large-v3 실시간 전사는 크게 세 가지 방식으로 구현할 수 있습니다:

저는 세 가지 모두の実전 경험을 바탕으로 각각의 장단점과 구현 방법을 설명드리겠습니다.

프로젝트 1: 브라우저 기반 실시간 전사

가장 접근하기 쉬운 방법은 브라우저에서 직접 마이크 입력을 캡처하여 HolySheep AI Whisper API로 전송하는 방식입니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Whisper 실시간 전사 - HolySheep AI</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
            min-height: 100vh;
            padding: 40px 20px;
            color: #fff;
        }
        .container {
            max-width: 900px;
            margin: 0 auto;
        }
        h1 {
            text-align: center;
            margin-bottom: 10px;
            font-size: 2rem;
        }
        .subtitle {
            text-align: center;
            color: #888;
            margin-bottom: 40px;
        }
        .api-key-section {
            background: rgba(255,255,255,0.1);
            padding: 20px;
            border-radius: 12px;
            margin-bottom: 30px;
        }
        .api-key-section label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
        }
        .api-key-section input {
            width: 100%;
            padding: 12px;
            border: 1px solid #333;
            border-radius: 8px;
            background: #0f0f23;
            color: #fff;
            font-family: monospace;
        }
        .controls {
            display: flex;
            gap: 15px;
            margin-bottom: 30px;
            flex-wrap: wrap;
        }
        button {
            padding: 15px 30px;
            font-size: 1rem;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-weight: 600;
            transition: all 0.3s;
        }
        .btn-start {
            background: #4ade80;
            color: #000;
        }
        .btn-start:hover { background: #22c55e; }
        .btn-stop {
            background: #ef4444;
            color: #fff;
        }
        .btn-stop:hover { background: #dc2626; }
        .btn-clear {
            background: #3b82f6;
            color: #fff;
        }
        .btn-clear:hover { background: #2563eb; }
        .status {
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 20px;
            text-align: center;
            font-weight: 500;
        }
        .status.recording {
            background: rgba(74, 222, 128, 0.2);
            border: 1px solid #4ade80;
            color: #4ade80;
        }
        .status.idle {
            background: rgba(255,255,255,0.1);
            color: #888;
        }
        .transcript-box {
            background: rgba(0,0,0,0.3);
            border-radius: 12px;
            padding: 25px;
            min-height: 400px;
            max-height: 600px;
            overflow-y: auto;
            border: 1px solid #333;
        }
        .transcript-box h3 {
            color: #4ade80;
            margin-bottom: 15px;
        }
        .transcript-item {
            padding: 12px;
            margin-bottom: 10px;
            background: rgba(255,255,255,0.05);
            border-radius: 8px;
            border-left: 3px solid #4ade80;
        }
        .transcript-item .timestamp {
            color: #888;
            font-size: 0.85rem;
            margin-bottom: 5px;
        }
        .transcript-item .text {
            font-size: 1.1rem;
            line-height: 1.6;
        }
        .error {
            color: #ef4444;
            background: rgba(239,68,68,0.1);
            border-left-color: #ef4444;
        }
        .stats {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 15px;
            margin-bottom: 20px;
        }
        .stat-card {
            background: rgba(255,255,255,0.05);
            padding: 15px;
            border-radius: 8px;
            text-align: center;
        }
        .stat-card .value {
            font-size: 1.5rem;
            font-weight: bold;
            color: #4ade80;
        }
        .stat-card .label {
            color: #888;
            font-size: 0.85rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🎙️ Whisper large-v3 실시간 전사</h1>
        <p class="subtitle">HolySheep AI API 활용 | 브라우저 기반</p>

        <div class="api-key-section">
            <label>🔑 HolySheep AI API Key</label>
            <input type="password" id="apiKey" placeholder="hs-로 시작하는 API 키를 입력하세요">
        </div>

        <div class="stats">
            <div class="stat-card">
                <div class="value" id="requestCount">0</div>
                <div class="label">전송 횟수</div>
            </div>
            <div class="stat-card">
                <div class="value" id="totalDuration">0s</div>
                <div class="label">총 녹음 시간</div>
            </div>
            <div class="stat-card">
                <div class="value" id="avgLatency">0ms</div>
                <div class="label">평균 지연 시간</div>
            </div>
        </div>

        <div class="controls">
            <button class="btn-start" id="startBtn" onclick="startRecording()">
                ▶️ 녹음 시작
            </button>
            <button class="btn-stop" id="stopBtn" onclick="stopRecording()" disabled>
                ⏹️ 녹음 중지
            </button>
            <button class="btn-clear" onclick="clearTranscript()">
                🗑️ 초기화
            </button>
        </div>

        <div class="status idle" id="status">마이크 권한을 허용하고 녹음을 시작하세요</div>

        <div class="transcript-box" id="transcriptBox">
            <h3>📝 전사 결과</h3>
            <div id="transcripts"></div>
        </div>
    </div>

    <script>
        // HolySheep AI API 설정
        const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
        
        // 상태 관리
        let mediaRecorder = null;
        let audioChunks = [];
        let isRecording = false;
        let recordingStartTime = null;
        let stats = { requestCount: 0, latencies: [] };
        let recordingInterval = null;

        async function startRecording() {
            const apiKey = document.getElementById('apiKey').value.trim();
            if (!apiKey) {
                alert('HolySheep AI API 키를 입력해주세요.');
                return;
            }

            try {
                // 마이크 권한 요청
                const stream = await navigator.mediaDevices.getUserMedia({ 
                    audio: {
                        echoCancellation: true,
                        noiseSuppression: true,
                        sampleRate: 16000
                    }
                });

                // MediaRecorder 설정 (webm 형식, 16kHz)
                mediaRecorder = new MediaRecorder(stream, {
                    mimeType: 'audio/webm;codecs=opus'
                });

                audioChunks = [];
                
                mediaRecorder.ondataavailable = async (event) => {
                    if (event.data.size > 0) {
                        audioChunks.push(event.data);
                    }
                };

                mediaRecorder.onstop = async () => {
                    const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
                    await transcribeAudio(audioBlob, apiKey);
                    audioChunks = [];
                };

                // 5초마다 녹음 세그먼트 전송
                recordingInterval = setInterval(() => {
                    if (mediaRecorder.state === 'recording') {
                        mediaRecorder.stop();
                        setTimeout(() => {
                            if (isRecording) {
                                mediaRecorder.start();
                            }
                        }, 100);
                    }
                }, 5000);

                mediaRecorder.start(1000); // 1초마다 데이터 수집
                isRecording = true;
                recordingStartTime = Date.now();

                // UI 업데이트
                document.getElementById('startBtn').disabled = true;
                document.getElementById('stopBtn').disabled = false;
                document.getElementById('status').textContent = '🎤 녹음 중... (5초마다 전사 전송)';
                document.getElementById('status').className = 'status recording';

            } catch (error) {
                console.error('마이크 접근 오류:', error);
                alert('마이크 접근이 거부되었습니다. 권한을 허용해주세요.');
            }
        }

        async function transcribeAudio(audioBlob, apiKey) {
            const startTime = Date.now();
            
            try {
                const formData = new FormData();
                formData.append('file', audioBlob, 'audio.webm');
                formData.append('model', 'whisper-large-v3');
                formData.append('response_format', 'text');
                formData.append('language', 'ko'); // 한국어指定

                const response = await fetch(${HOLYSHEEP_BASE_URL}/audio/transcriptions, {
                    method: 'POST',
                    headers: {
                        'Authorization': Bearer ${apiKey}
                    },
                    body: formData
                });

                const latency = Date.now() - startTime;
                stats.requestCount++;
                stats.latencies.push(latency);

                if (!response.ok) {
                    const error = await response.text();
                    throw new Error(API 오류: ${response.status} - ${error});
                }

                const text = await response.text();
                
                // 전사 결과 표시
                addTranscriptItem(text, latency);
                
                // 통계 업데이트
                updateStats();

            } catch (error) {
                console.error('전사 오류:', error);
                addTranscriptItem([오류] ${error.message}, 0, true);
            }
        }

        function stopRecording() {
            if (mediaRecorder && mediaRecorder.state !== 'inactive') {
                mediaRecorder.stop();
                mediaRecorder.stream.getTracks().forEach(track => track.stop());
            }
            
            if (recordingInterval) {
                clearInterval(recordingInterval);
            }

            isRecording = false;
            
            // UI 업데이트
            document.getElementById('startBtn').disabled = false;
            document.getElementById('stopBtn').disabled = true;
            document.getElementById('status').textContent = '녹음 중지됨';
            document.getElementById('status').className = 'status idle';
        }

        function addTranscriptItem(text, latency, isError = false) {
            const container = document.getElementById('transcripts');
            const now = new Date();
            const time = now.toLocaleTimeString('ko-KR');
            
            const item = document.createElement('div');
            item.className = transcript-item ${isError ? 'error' : ''};
            item.innerHTML = `
                <div class="timestamp">${time} | ${latency}ms</div>
                <div class="text">${text}</div>
            `;
            
            container.appendChild(item);
            
            // 자동 스크롤
            const box = document.getElementById('transcriptBox');
            box.scrollTop = box.scrollHeight;
        }

        function updateStats() {
            const totalDuration = Math.round((Date.now() - recordingStartTime) / 1000);
            const avgLatency = stats.latencies.length > 0 
                ? Math.round(stats.latencies.reduce((a, b) => a + b, 0) / stats.latencies.length)
                : 0;

            document.getElementById('requestCount').textContent = stats.requestCount;
            document.getElementById('totalDuration').textContent = ${totalDuration}s;
            document.getElementById('avgLatency').textContent = ${avgLatency}ms;
        }

        function clearTranscript() {
            document.getElementById('transcripts').innerHTML = '';
            stats = { requestCount: 0, latencies: [] };
            recordingStartTime = Date.now();
            updateStats();
        }
    </script>
</body>
</html>

이 코드를 realtime-transcribe.html로 저장하고 브라우저에서 열면 즉시 사용할 수 있습니다.

프로젝트 2: Python 백엔드 실시간 전사 서버

production 환경에서는 Python 백엔드로 서버 사이드 처리가 더 안정적입니다. PyAudio를 사용한 스트리밍 전사 구현입니다.

# requirements.txt

pip install openai pyaudio wave threading requests

import pyaudio import wave import threading import time import requests import base64 import json from datetime import datetime class HolySheepWhisperClient: """HolySheep AI Whisper large-v3 실시간 전사 클라이언트""" def __init__(self, api_key: str): self.api_key = api_key self.base_url = "https://api.holysheep.ai/v1" self.model = "whisper-large-v3" # 오디오 설정 self.chunk_size = 1024 self.sample_rate = 16000 self.channels = 1 self.sample_format = pyaudio.paInt16 # 녹음 상태 self.is_recording = False self.frames = [] self.lock = threading.Lock() # 통계 self.total_requests = 0 self.total_latency = 0 def transcribe_audio(self, audio_data: bytes) -> dict: """HolySheep AI Whisper API로 오디오 데이터 전사""" start_time = time.time() files = { 'file': ('audio.wav', audio_data, 'audio/wav'), 'model': (None, self.model), 'response_format': (None, 'verbose_json'), 'language': (None, 'ko'), } headers = { 'Authorization': f'Bearer {self.api_key}' } try: response = requests.post( f'{self.base_url}/audio/transcriptions', files=files, headers=headers, timeout=30 ) latency = (time.time() - start_time) * 1000 # ms self.total_requests += 1 self.total_latency += latency if response.status_code == 200: result = response.json() return { 'success': True, 'text': result.get('text', ''), 'latency_ms': round(latency, 2), 'language': result.get('language', 'unknown') } else: return { 'success': False, 'error': f'HTTP {response.status_code}: {response.text}', 'latency_ms': round(latency, 2) } except requests.exceptions.Timeout: return {'success': False, 'error': '요청 시간 초과'} except Exception as e: return {'success': False, 'error': str(e)} def save_audio_chunk(self, frames: list) -> bytes: """오디오 프레임을 WAV 바이트로 변환""" import io import struct audio_data = b''.join(frames) # WAV 헤더 생성 buffer = io.BytesIO() with wave.open(buffer, 'wb') as wf: wf.setnchannels(self.channels) wf.setsampwidth(2) # 16-bit wf.setframerate(self.sample_rate) wf.writeframes(audio_data) return buffer.getvalue() def recording_worker(self, p: pyaudio.PyAudio, stream): """백그라운드 녹음 워커""" while self.is_recording: try: data = stream.read(self.chunk_size, exception_on_overflow=False) with self.lock: self.frames.append(data) except Exception as e: print(f"녹음 오류: {e}") break def transcription_worker(self, interval_seconds: int = 5): """백그라운드 전사 워커 - 지정된 간격으로 전사 실행""" while self.is_recording: time.sleep(interval_seconds) if not self.is_recording: break with self.lock: if len(self.frames) == 0: continue current_frames = self.frames.copy() self.frames = [] # 오디오 데이터 변환 audio_bytes = self.save_audio_chunk(current_frames) # 전사 실행 result = self.transcribe_audio(audio_bytes) # 결과 출력 timestamp = datetime.now().strftime('%H:%M:%S') if result['success']: print(f"\n[{timestamp}] ⏱️ {result['latency_ms']}ms") print(f" 📝 {result['text']}") print(f" 📊 평균 지연: {round(self.total_latency / self.total_requests, 2)}ms") else: print(f"\n[{timestamp}] ❌ 오류: {result['error']}") def start(self, duration_seconds: int = 60): """녹음 및 전사 시작""" print(f"🎙️ HolySheep AI Whisper large-v3 실시간 전사 시작") print(f" 모델: {self.model}") print(f" 샘플레이트: {self.sample_rate}Hz") print(f" 예상 소요 시간: {duration_seconds}초") print("-" * 50) # PyAudio 초기화 p = pyaudio.PyAudio() try: # 오디오 스트림 열기 stream = p.open( format=self.sample_format, channels=self.channels, rate=self.sample_rate, input=True, frames_per_buffer=self.chunk_size ) self.is_recording = True self.frames = [] # 워커 스레드 시작 recording_thread = threading.Thread( target=self.recording_worker, args=(p, stream) ) transcription_thread = threading.Thread( target=self.transcription_worker, args=(5,) # 5초마다 전사 ) recording_thread.start() transcription_thread.start() print("🎤 녹음 중... Ctrl+C로 종료\n") # 메인 스레드에서 duration 동안 대기 start_time = time.time() while time.time() - start_time < duration_seconds: time.sleep(1) elapsed = int(time.time() - start_time) print(f"\r⏱️ 경과 시간: {elapsed}s / {duration_seconds}s", end='', flush=True) except KeyboardInterrupt: print("\n\n⏹️ 사용자에 의해 중단됨") finally: self.is_recording = False stream.stop_stream() stream.close() p.terminate() print(f"\n\n📊 최종 통계") print(f" 총 전사 요청: {self.total_requests}회") if self.total_requests > 0: print(f" 평균 응답 지연: {round(self.total_latency / self.total_requests, 2)}ms") def main(): # HolySheep AI API 키 설정 API_KEY = "YOUR_HOLYSHEEP_API_KEY" # hs-로 시작하는 키 if API_KEY == "YOUR_HOLYSHEEP_API_KEY": print("⚠️ HolySheep AI API 키를 설정해주세요.") print(" https://www.holysheep.ai/register 에서 가입하세요.") return # 클라이언트 생성 및 시작 client = HolySheepWhisperClient(API_KEY) client.start(duration_seconds=60) # 60초 녹음 if __name__ == "__main__": main()

실행 전에 반드시 필요한 패키지를 설치하세요:

pip install openai pyaudio requests

macOS의 경우 추가 설치

brew install portaudio pip install pyaudio

Ubuntu/Debian의 경우

sudo apt-get install portaudio19-dev pip install pyaudio

프로젝트 3: Node.js + Express 실시간 전사 API

Node.js 환경에서 HolySheep AI Whisper API를 호출하는 REST API 서버를 구축합니다.

// package.json dependencies
// npm install express multer node-fetch form-data cors dotenv

require('dotenv').config();
const express = require('express');
const multer = require('multer');
const cors = require('cors');
const FormData = require('form-data');
const fetch = require('node-fetch');
const path = require('path');

const app = express();
const PORT = process.env.PORT || 3000;

// HolySheep AI 설정
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';

// 미들웨어
app.use(cors());
app.use(express.json());
app.use(express.static('public'));

// 파일 업로드 설정
const storage = multer.memoryStorage();
const upload = multer({ 
    storage: storage,
    limits: { fileSize: 25 * 1024 * 1024 }, // 25MB 제한
    fileFilter: (req, file, cb) => {
        const allowedTypes = ['audio/wav', 'audio/mpeg', 'audio/mp3', 'audio/webm', 'audio/ogg', 'audio/m4a'];
        if (allowedTypes.includes(file.mimetype)) {
            cb(null, true);
        } else {
            cb(new Error('지원되지 않는 오디오 형식입니다.'));
        }
    }
});

// 통계 저장
const stats = {
    totalRequests: 0,
    totalLatency: 0,
    requestHistory: []
};

/**
 * HolySheep AI Whisper API 호출
 */
async function transcribeWithHolySheep(audioBuffer, filename) {
    const startTime = Date.now();
    
    try {
        const formData = new FormData();
        formData.append('file', audioBuffer, {
            filename: filename,
            contentType: 'audio/webm'
        });
        formData.append('model', 'whisper-large-v3');
        formData.append('response_format', 'verbose_json');
        formData.append('language', 'ko');
        formData.append('temperature', '0');
        formData.append('timestamp_granularities[]', 'word');

        const apiKey = process.env.HOLYSHEEP_API_KEY || 'YOUR_HOLYSHEEP_API_KEY';
        
        const response = await fetch(${HOLYSHEEP_BASE_URL}/audio/transcriptions, {
            method: 'POST',
            headers: {
                'Authorization': Bearer ${apiKey}
            },
            body: formData
        });

        const latency = Date.now() - startTime;
        
        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(API 오류: ${response.status} - ${errorText});
        }

        const result = await response.json();
        
        // 통계 업데이트
        stats.totalRequests++;
        stats.totalLatency += latency;
        stats.requestHistory.push({
            timestamp: new Date().toISOString(),
            latency,
            textLength: result.text?.length || 0
        });

        return {
            success: true,
            text: result.text,
            language: result.language,
            duration: result.duration,
            words: result.words,
            latency_ms: latency,
            avg_latency_ms: Math.round(stats.totalLatency / stats.totalRequests)
        };
        
    } catch (error) {
        return {
            success: false,
            error: error.message,
            latency_ms: Date.now() - startTime
        };
    }
}

/**
 * POST /api/transcribe
 * 오디오 파일 실시간 전사
 */
app.post('/api/transcribe', upload.single('audio'), async (req, res) => {
    if (!req.file) {
        return res.status(400).json({ 
            success: false, 
            error: '오디오 파일이 제공되지 않았습니다.' 
        });
    }

    console.log(📥 파일 수신: ${req.file.originalname} (${req.file.size} bytes));
    
    const result = await transcribeWithHolySheep(
        req.file.buffer,
        req.file.originalname
    );

    if (result.success) {
        console.log(✅ 전사 완료: ${result.latency_ms}ms | ${result.text.substring(0, 50)}...);
    } else {
        console.log(❌ 전사 실패: ${result.error});
    }

    res.json(result);
});

/**
 * POST /api/transcribe-stream
 * 청크 단위 스트리밍 전사 (실시간용)
 */
app.post('/api/transcribe-stream', async (req, res) => {
    const chunks = [];
    
    req.on('data', chunk => chunks.push(chunk));
    
    req.on('end', async () => {
        const audioBuffer = Buffer.concat(chunks);
        const result = await transcribeWithHolySheep(audioBuffer, 'stream.webm');
        res.json(result);
    });
    
    req.on('error', (err) => {
        res.status(500).json({ success: false, error: err.message });
    });
});

/**
 * GET /api/stats
 * API 사용 통계 조회
 */
app.get('/api/stats', (req, res) => {
    const avgLatency = stats.totalRequests > 0 
        ? Math.round(stats.totalLatency / stats.totalRequests) 
        : 0;
    
    res.json({
        totalRequests: stats.totalRequests,
        avgLatency_ms: avgLatency,
        recentRequests: stats.requestHistory.slice(-10)
    });
});

/**
 * GET /api/health
 * 헬스 체크
 */
app.get('/api/health', (req, res) => {
    res.json({ 
        status: 'healthy', 
        service: 'HolySheep Whisper API',
        timestamp: new Date().toISOString()
    });
});

// 오류 처리 미들웨어
app.use((error, req, res, next) => {
    if (error instanceof multer.MulterError) {
        if (error.code === 'LIMIT_FILE_SIZE') {
            return res.status(400).json({ 
                success: false, 
                error: '파일 크기가 25MB를 초과했습니다.' 
            });
        }
    }
    res.status(500).json({ success: false, error: error.message });
});

// 서버 시작
app.listen(PORT, () => {
    console.log(`
╔══════════════════════════════════════════════════════╗
║  🎙️ HolySheep AI Whisper large-v3 API Server         ║
╠══════════════════════════════════════════════════════╣
║  서버 실행 중: http://localhost:${PORT}                 ║
║  전사 엔드포인트: POST /api/transcribe                 ║
║  헬스 체크: GET /api/health                           ║
║  통계 조회: GET /api/stats                            ║
╚══════════════════════════════════════════════════════╝
    `);
    
    if (!process.env.HOLYSHEEP_API_KEY || process.env.HOLYSHEEP_API_KEY === 'YOUR_HOLYSHEEP_API_KEY') {
        console.warn('⚠️ HolySheep API 키가 설정되지 않았습니다.');
        console.warn('   .env 파일에 HOLYSHEEP_API_KEY를 설정하세요.');
        console.warn('   https://www.holysheep.ai/register 에서 가입하세요.');
    }
});

프론트엔드 HTML 파일도 함께 제공합니다:

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HolySheep Whisper API - 실시간 전사 데모</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #0a0a0a;
            color: #fff;
        }
        .header {
            text-align: center;
            margin-bottom: 30px;
        }
        .header h1 { color: #4ade80; }
        .api-section {
            background: #1a1a1a;
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 20px;
        }
        .api-section label {
            display: block;
            margin-bottom: 8px;
            color: #888;
        }
        .api-section input {
            width: 100%;
            padding: 12px;
            background: #0f0f0f;
            border: 1px solid #333;
            color: #fff;
            border-radius: 6px;
        }
        .controls {
            display: flex;
            gap: 10px;
            margin: 20px 0;
        }
        button {
            padding: 12px 24px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: 600;
        }
        .record-btn { background: #4ade80; color: #000; }
        .stop-btn { background: #ef4444; color: #fff; }
        .transcript {
            background: #1a1a1a;
            padding: 20px;
            border-radius: 8px;
            min-height: 300px;
            max-height: 500px;
            overflow-y: auto;
        }
        .result-item {
            padding: 12px;
            margin-bottom: 10px;
            background: #252525;
            border-radius: 6px;
            border-left: 3px solid #4ade80;
        }
        .result-item.error { border-left-color: #ef4444; }
        .timestamp { color: #888; font-size: 0.85rem