/* Granite Speech WebGPU - Whisper Web Style */ * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f8fafc; color: #0f172a; min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 2rem; line-height: 1.5; } .container { width: 100%; max-width: 540px; display: flex; flex-direction: column; align-items: center; gap: 1.5rem; } /* Header */ h1 { font-size: 3rem; font-weight: 800; letter-spacing: -0.025em; color: #0f172a; text-align: center; } h2 { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.015em; color: #0f172a; text-align: center; padding: 0 1rem; white-space: nowrap; } .privacy-note { font-size: 0.8125rem; color: #64748b; text-align: center; } /* Input Card */ .input-card { width: 100%; background: white; border-radius: 0.5rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05); border: 1px solid rgba(51, 65, 85, 0.1); overflow: hidden; } .input-options { display: flex; align-items: stretch; padding: 0.5rem; gap: 0.5rem; } .input-tile { display: flex; align-items: center; justify-content: center; gap: 0.5rem; padding: 0.5rem 0.75rem; background: transparent; border: none; border-radius: 0.5rem; color: #64748b; font-size: 0.9375rem; cursor: pointer; transition: all 0.2s; flex: 1; } .input-tile:hover:not(:disabled) { color: #4f46e5; background: #eef2ff; } .input-tile.recording { color: #ef4444; background: #fef2f2; } .input-tile.recording:hover { color: #dc2626; background: #fee2e2; } .input-tile:disabled { opacity: 0.5; cursor: not-allowed; } .input-tile svg { width: 1.75rem; height: 1.75rem; flex-shrink: 0; } .file-label { cursor: pointer; transition: all 0.2s; } .file-label.drag-over { color: #2563eb; background: #eff6ff; } .input-card.drag-over { border: 2px dashed #2563eb; background: #eff6ff; } .divider { width: 1px; background: #e2e8f0; margin: 0.25rem 0; } /* Live input level. Sits above the download progress bar and reads the same RMS the speech gate does, so a user can see why the gate is (or isn't) triggering. */ .level-meter { width: 100%; height: 3px; background: #e5e7eb; } .level-fill { height: 100%; width: 0%; background: #10b981; transition: width 0.08s linear; } /* Progress Bar */ .progress-bar { width: 100%; height: 4px; background: #e5e7eb; } .progress-fill { height: 100%; background: #2563eb; width: 0%; transition: width 0.1s; border-radius: 0 2px 2px 0; } /* Status Section */ .status-section { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; color: #64748b; } .status-dot { width: 8px; height: 8px; border-radius: 50%; background: #94a3b8; } .status-dot.ready { background: #22c55e; } .status-dot.loading, .status-dot.processing { background: #f59e0b; animation: pulse 1.5s infinite; } .status-dot.recording { background: #ef4444; animation: pulse 0.8s infinite; } .status-dot.error { background: #ef4444; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .checkbox-group { display: flex; gap: 1.5rem; justify-content: center; width: 100%; max-width: 540px; } .checkbox-row { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; color: #64748b; cursor: pointer; } .checkbox-row input[type="checkbox"] { width: 1rem; height: 1rem; cursor: pointer; accent-color: #2563eb; } .checkbox-row small { color: #94a3b8; font-size: 0.75rem; } /* Model Progress */ .model-progress { font-size: 0.875rem; color: #64748b; } /* Transcript Card */ .transcript-card { width: 100%; background: white; border-radius: 0.5rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05); border: 1px solid rgba(51, 65, 85, 0.1); overflow: hidden; } .transcript-header { display: flex; justify-content: space-between; align-items: center; padding: 0.75rem 1rem; border-bottom: 1px solid #e2e8f0; font-size: 0.875rem; font-weight: 500; color: #64748b; } .transcript-actions { display: flex; gap: 0.25rem; } .icon-btn { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; background: transparent; border: none; border-radius: 0.375rem; color: #64748b; cursor: pointer; transition: all 0.2s; } .icon-btn:hover { background: #f1f5f9; color: #0f172a; } .icon-btn svg { width: 18px; height: 18px; } .transcript-output { padding: 1rem; min-height: 100px; max-height: 300px; overflow-y: auto; font-size: 0.9375rem; line-height: 1.5; color: #0f172a; word-wrap: break-word; white-space: pre-wrap; /* paragraph breaks come through as \n\n */ } /* The unstabilized tail: shown until two consecutive windows agree on it. */ .tentative { color: #94a3b8; } .transcript-stats { padding: 0 1rem 0.625rem; font-size: 0.75rem; color: #94a3b8; font-variant-numeric: tabular-nums; min-height: 1rem; } /* Footer */ .footer { text-align: center; font-size: 0.875rem; color: #64748b; } .footer a { color: #2563eb; text-decoration: none; } .footer a:hover { text-decoration: underline; } .gpu-info { font-size: 0.75rem; font-family: 'SF Mono', Monaco, 'Courier New', monospace; color: #94a3b8; } /* Browser Error */ .browser-error { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background: #f8fafc; z-index: 100; padding: 2rem; } .browser-error-content { text-align: center; max-width: 440px; } .browser-error-content h2 { font-size: 1.5rem; font-weight: 700; color: #0f172a; margin-bottom: 1rem; } .browser-error-content p { font-size: 1rem; color: #64748b; margin-bottom: 0.5rem; } /* Responsive */ @media (max-width: 640px) { body { padding: 1rem; } h1 { font-size: 2.25rem; } h2 { font-size: 1rem; } .input-tile { padding: 0.5rem; font-size: 0.8125rem; } .input-tile svg { width: 1.5rem; height: 1.5rem; } } /* Scrollbar */ .transcript-output::-webkit-scrollbar { width: 6px; } .transcript-output::-webkit-scrollbar-track { background: #f1f5f9; } .transcript-output::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; } .transcript-output::-webkit-scrollbar-thumb:hover { background: #94a3b8; } /* Dark Mode */ @media (prefers-color-scheme: dark) { body { background: #0f172a; color: #e2e8f0; } h1, h2 { color: #f1f5f9; } .input-card, .transcript-card, .audio-player { background: #1e293b; border-color: rgba(148, 163, 184, 0.1); box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.3); } .audio-time { color: #94a3b8; } .input-card.drag-over { background: #1e3a5f; border-color: #3b82f6; } .input-tile { color: #94a3b8; } .input-tile:hover:not(:disabled) { color: #818cf8; background: #312e81; } .input-tile.recording { color: #f87171; background: #450a0a; } .input-tile.recording:hover { color: #fca5a5; background: #7f1d1d; } .divider { background: #334155; } .progress-bar { background: #334155; } .status-section, .task-row, .checkbox-row, .model-progress { color: #94a3b8; } .task-row select { background: #1e293b; color: #e2e8f0; border-color: #475569; } .task-row select:focus { border-color: #3b82f6; box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3); } .transcript-header { border-color: #334155; color: #94a3b8; } .transcript-output { color: #e2e8f0; } .transcript-row .timestamp { color: #64748b; } .icon-btn { color: #94a3b8; } .icon-btn:hover { background: #334155; color: #f1f5f9; } .clear-btn { color: #64748b; } .clear-btn:hover { color: #f87171; background: #450a0a; } .footer { color: #64748b; } .footer a { color: #60a5fa; } .privacy-note { color: #64748b; } .gpu-info { color: #64748b; } .browser-error { background: #0f172a; } .browser-error-content h2 { color: #f1f5f9; } .browser-error-content p { color: #94a3b8; } /* Scrollbar dark */ .transcript-output::-webkit-scrollbar-track { background: #1e293b; } .transcript-output::-webkit-scrollbar-thumb { background: #475569; } .transcript-output::-webkit-scrollbar-thumb:hover { background: #64748b; } }