{"version":3,"sources":["../src/hooks/build-report.ts","../src/hooks/useInterview.ts","../src/processor/client-mode-processor.ts","../src/processor/server-mode-processor.ts","../src/components/AudioLevelMeter.tsx","../src/components/FeedbackNote.tsx","../src/hooks/audio-level-math.ts","../src/hooks/web-audio-support.ts","../src/hooks/useMicLevelMeter.ts","../src/components/InterviewLobby.tsx","../src/components/InterviewProgress.tsx","../src/components/LiveSignals.tsx","../src/components/MicButton.tsx","../src/hooks/useSpeechLevelMeter.ts","../src/components/QuestionAudio.tsx","../src/components/QuestionCard.tsx","../src/components/TranscriptChat.tsx","../src/components/score-tier.ts","../src/components/TranscriptViewer.tsx","../src/components/ScoreSummary.tsx","../src/components/report-export.ts","../src/components/optional-image-export.ts","../src/components/ReportCard.tsx","../src/hooks/use-integrity-signals.ts","../src/components/InterviewErrorBoundary.tsx","../src/components/InterviewWidget.tsx"],"names":["validateInterviewConfig","useMemo","defineRubric","useState","InterviewFlowEngine","useRef","useCallback","FollowUpEngine","EvaluationEngine","jsx","jsxs","useEffect","BAR_COUNT","SAMPLE_INTERVAL_MS","Fragment","answerText","useId","Component"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { CandidateAnswer, EvaluationResult, Question, Rubric } from '@interview-sdk/core';\n\nexport interface TranscriptEntry {\n  question: Question;\n  prompt: string;\n  isFollowUp: boolean;\n  answer: CandidateAnswer;\n  evaluation: EvaluationResult;\n}\n\n/**\n * Low-risk, opt-in integrity signals — tab-switch and paste-into-answer\n * counts, nothing biometric or behavioral. These are observations for a\n * human reviewer to weigh in context, not an automated cheating verdict; if\n * you track this, disclose it to candidates (most interview platforms that\n * track tab-switching say so up front).\n */\nexport interface IntegritySignals {\n  /** Number of times the browser tab/window lost visibility while the interview was in progress. */\n  tabSwitchCount: number;\n  /** Wall-clock timestamp (ms since epoch) of each tab-switch-away event. */\n  tabSwitchTimestamps: number[];\n  /** Every paste event into an answer field — character length of what was pasted, and when. */\n  pasteEvents: Array<{ length: number; timestamp: number }>;\n}\n\nexport interface InterviewReport {\n  sessionId: string;\n  totalScore: number;\n  /** Only has an entry for a dimension at least one question actually assessed (see `Question.dimensions`) — one no question addressed is simply absent, not present at 0. */\n  dimensionAverages: Record<string, number>;\n  strengths: string[];\n  weaknesses: string[];\n  missedConcepts: string[];\n  transcript: TranscriptEntry[];\n  /** Only present when the caller opted into tracking (e.g. InterviewWidget's trackIntegritySignals prop). */\n  integritySignals?: IntegritySignals;\n}\n\n/** Also used by LiveSignals to mark where a \"pass\" begins on its meters — one definition of \"strong\" shared across the live view and the final report. */\nexport const STRENGTH_THRESHOLD = 75;\nconst WEAKNESS_THRESHOLD = 40;\n\n/**\n * Builds a heuristic report from rubric scores and concept coverage already\n * present in the transcript. Strengths/weaknesses/recommendations are\n * derived from dimension averages and missed concepts, not AI-generated\n * commentary — there is no such call in this SDK today.\n */\nexport function buildReport(\n  sessionId: string,\n  rubric: Rubric,\n  transcript: TranscriptEntry[],\n  integritySignals?: IntegritySignals,\n): InterviewReport {\n  // A dimension no question in this transcript ever assessed (see\n  // Question.dimensions) simply isn't a key here at all — not present at a\n  // misleading 0, which would otherwise read as a real, failed assessment\n  // of something the candidate was never actually asked about.\n  const dimensionAverages: Record<string, number> = {};\n  const dimensionsWithData = new Set<string>();\n  for (const dimension of rubric.dimensions) {\n    const scores = transcript\n      .map((entry) => entry.evaluation.dimensionScores[dimension.id])\n      .filter((score): score is number => typeof score === 'number');\n    if (scores.length === 0) continue;\n    dimensionsWithData.add(dimension.id);\n    dimensionAverages[dimension.id] = scores.reduce((sum, score) => sum + score, 0) / scores.length;\n  }\n\n  const totalScore =\n    transcript.length > 0\n      ? transcript.reduce((sum, entry) => sum + entry.evaluation.totalScore, 0) / transcript.length\n      : 0;\n\n  const dimensionsWithScores = rubric.dimensions.filter((dimension) =>\n    dimensionsWithData.has(dimension.id),\n  );\n\n  const strengths = dimensionsWithScores\n    .filter((dimension) => (dimensionAverages[dimension.id] ?? 0) >= STRENGTH_THRESHOLD)\n    .map((dimension) => dimension.label);\n\n  const weaknesses = dimensionsWithScores\n    .filter((dimension) => (dimensionAverages[dimension.id] ?? 0) <= WEAKNESS_THRESHOLD)\n    .map((dimension) => dimension.label);\n\n  const missedConcepts = Array.from(\n    new Set(\n      transcript.flatMap((entry) =>\n        entry.evaluation.conceptCoverage.filter((c) => !c.covered).map((c) => c.concept),\n      ),\n    ),\n  );\n\n  return {\n    sessionId,\n    totalScore: Math.round(totalScore * 100) / 100,\n    dimensionAverages,\n    strengths,\n    weaknesses,\n    missedConcepts,\n    transcript,\n    ...(integritySignals ? { integritySignals } : {}),\n  };\n}\n","import {\n  InterviewFlowEngine,\n  defineRubric,\n  validateInterviewConfig,\n  type EvaluationTurn,\n  type InterviewEventEmitter,\n  type Question,\n  type RubricDimensionInput,\n  type SessionState,\n} from '@interview-sdk/core';\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport {\n  buildReport,\n  type IntegritySignals,\n  type InterviewReport,\n  type TranscriptEntry,\n} from './build-report.js';\nimport type { InterviewProcessor } from '../processor/types.js';\n\nexport interface UseInterviewOptions {\n  questions: Question[];\n  rubric: RubricDimensionInput[];\n  processor: InterviewProcessor;\n  maxFollowUpDepth?: number;\n  sessionTimeoutMs?: number;\n  onSessionEnd?: (report: InterviewReport) => void;\n  /** Resume a session from a snapshot returned by this same hook's getSnapshot() — e.g. one restored after a page refresh or loaded back from your own backend. Only read once, on mount. */\n  initialSnapshot?: InterviewSnapshot;\n  /** Read once whenever a report is built (on completion or voluntary end) and attached to it as `integritySignals` — e.g. from useIntegritySignals() in @interview-sdk/react. Omit if you don't track this. */\n  getIntegritySignals?: () => IntegritySignals;\n}\n\nexport interface SubmitAnswerOptions {\n  isSkipped?: boolean;\n  isSilence?: boolean;\n}\n\n/** Everything needed to resume this exact session elsewhere — persist it (localStorage, your own backend) and pass it back as `initialSnapshot` to pick up where it left off after a refresh or disconnect. */\nexport interface InterviewSnapshot {\n  flowState: SessionState;\n  transcript: TranscriptEntry[];\n}\n\nexport interface UseInterviewResult {\n  status: SessionState['status'];\n  /** Wall-clock time the session actually started, for a live elapsed-time display. Undefined until start() is called. */\n  startedAt: number | undefined;\n  currentQuestion: Question | undefined;\n  currentPrompt: string | undefined;\n  isFollowUpPrompt: boolean;\n  transcript: TranscriptEntry[];\n  isProcessing: boolean;\n  error: Error | undefined;\n  report: InterviewReport | undefined;\n  start: () => void;\n  pause: () => void;\n  resume: () => void;\n  /** Voluntarily ends the session before every question has been answered, building a report from whatever's been answered so far. */\n  endInterview: () => void;\n  submitAnswer: (text: string, opts?: SubmitAnswerOptions) => Promise<void>;\n  retryLastAnswer: () => Promise<void>;\n  /** A serializable snapshot of the whole session right now — see InterviewSnapshot. */\n  getSnapshot: () => InterviewSnapshot;\n  /** The underlying typed event emitter (sessionStart/sessionPause/sessionResume/sessionEnd/sessionExpired/questionAdvance/followUpGenerated/scoreComputed) — subscribe for your own analytics or logging, independent of the UI. */\n  events: InterviewEventEmitter;\n}\n\ninterface PendingProcessAnswer {\n  question: Question;\n  answer: TranscriptEntry['answer'];\n  previousTurns: EvaluationTurn[];\n  currentFollowUpDepth: number;\n  askedFollowUps: string[];\n}\n\nexport function useInterview(options: UseInterviewOptions): UseInterviewResult {\n  // Same guarantee <InterviewWidget> gives you — fails loud on empty\n  // questions, invalid rubric weights, duplicate ids, etc. — so headless\n  // consumers of this hook aren't left to discover a bad config from a\n  // more confusing downstream failure instead.\n  validateInterviewConfig({\n    questions: options.questions,\n    rubric: options.rubric,\n    maxFollowUpDepth: options.maxFollowUpDepth,\n    sessionTimeoutMs: options.sessionTimeoutMs,\n  });\n\n  const rubric = useMemo(() => defineRubric(options.rubric), [options.rubric]);\n\n  const [flow] = useState(() => {\n    const config = {\n      questions: options.questions,\n      maxFollowUpDepth: options.maxFollowUpDepth,\n      sessionTimeoutMs: options.sessionTimeoutMs,\n    };\n    return options.initialSnapshot\n      ? InterviewFlowEngine.fromState(options.initialSnapshot.flowState, config)\n      : new InterviewFlowEngine(config);\n  });\n\n  const [flowState, setFlowState] = useState<SessionState>(() =>\n    // A snapshot resumed after a real gap (page refresh, closed tab) may\n    // already be past sessionTimeoutMs — refreshExpiry() (rather than\n    // getState()) makes sure that shows up immediately as 'expired' instead\n    // of misleadingly rendering the question UI until the next submit throws.\n    options.initialSnapshot ? flow.refreshExpiry() : flow.getState(),\n  );\n  const [transcript, setTranscript] = useState<TranscriptEntry[]>(\n    () => options.initialSnapshot?.transcript ?? [],\n  );\n  const [isProcessing, setIsProcessing] = useState(false);\n  const [error, setError] = useState<Error | undefined>(undefined);\n  const [report, setReport] = useState<InterviewReport | undefined>(undefined);\n  const pendingRef = useRef<PendingProcessAnswer | undefined>(undefined);\n  // If the candidate pauses while an answer is mid-flight being scored, the\n  // AI call can't be cancelled — but applying its result's flow transition\n  // (advance/recordFollowUp) the instant it resolves would silently move\n  // the session forward behind a screen that still says \"Paused\". This\n  // stashes that transition to apply exactly once the candidate resumes,\n  // instead of either losing the real score or applying it invisibly.\n  const deferredTransitionRef = useRef<(() => void) | undefined>(undefined);\n\n  const currentQuestion = options.questions[flowState.currentQuestionIndex];\n  const askedFollowUps = currentQuestion\n    ? (flowState.followUpPromptsAsked[currentQuestion.id] ?? [])\n    : [];\n  const currentPrompt = askedFollowUps.at(-1) ?? currentQuestion?.prompt;\n  const isFollowUpPrompt = askedFollowUps.length > 0;\n\n  const start = useCallback(() => {\n    setFlowState(flow.start());\n  }, [flow]);\n\n  const pause = useCallback(() => {\n    setFlowState(flow.pause());\n  }, [flow]);\n\n  const resume = useCallback(() => {\n    // flow.resume() deliberately throws SessionExpiredError once the\n    // timeout has elapsed while paused — a real, expected outcome, not a\n    // bug in the engine. It must be caught here: this runs synchronously\n    // inside an onClick handler, and React error boundaries never catch\n    // event-handler exceptions, so an unguarded throw would crash the\n    // whole widget instead of surfacing a recoverable message.\n    let nextState: SessionState;\n    try {\n      nextState = flow.resume();\n    } catch (caught) {\n      setFlowState(flow.getState());\n      setError(caught instanceof Error ? caught : new Error(String(caught)));\n      return;\n    }\n    setFlowState(nextState);\n    if (nextState.status === 'in_progress' && deferredTransitionRef.current) {\n      const applyDeferredTransition = deferredTransitionRef.current;\n      deferredTransitionRef.current = undefined;\n      applyDeferredTransition();\n    }\n  }, [flow]);\n\n  const endInterview = useCallback(() => {\n    const wasAlreadyDone =\n      flow.getState().status === 'completed' || flow.getState().status === 'expired';\n    const nextState = flow.end();\n    setFlowState(nextState);\n    if (!wasAlreadyDone && nextState.status === 'completed') {\n      const finalReport = buildReport(\n        nextState.sessionId,\n        rubric,\n        transcript,\n        options.getIntegritySignals?.(),\n      );\n      setReport(finalReport);\n      options.onSessionEnd?.(finalReport);\n    }\n  }, [flow, rubric, transcript, options]);\n\n  const runProcessor = useCallback(\n    async (pending: PendingProcessAnswer) => {\n      setIsProcessing(true);\n      setError(undefined);\n      try {\n        const result = await options.processor.processAnswer({\n          question: pending.question,\n          rubric,\n          answer: pending.answer,\n          previousTurns: pending.previousTurns,\n          currentFollowUpDepth: pending.currentFollowUpDepth,\n          askedFollowUps: pending.askedFollowUps,\n        });\n\n        const nextTranscript = [\n          ...transcript,\n          {\n            question: pending.question,\n            prompt: pending.askedFollowUps.at(-1) ?? pending.question.prompt,\n            isFollowUp: pending.askedFollowUps.length > 0,\n            answer: pending.answer,\n            evaluation: result.evaluation,\n          },\n        ];\n        setTranscript(nextTranscript);\n        pendingRef.current = undefined;\n        flow.events.emit('scoreComputed', {\n          sessionId: flow.getState().sessionId,\n          questionId: pending.question.id,\n          result: result.evaluation,\n        });\n\n        const applyTransition = () => {\n          if (result.followUp) {\n            setFlowState(flow.recordFollowUp(result.followUp.prompt));\n          } else {\n            const nextState = flow.advance();\n            setFlowState(nextState);\n            if (nextState.status === 'completed') {\n              const finalReport = buildReport(\n                nextState.sessionId,\n                rubric,\n                nextTranscript,\n                options.getIntegritySignals?.(),\n              );\n              setReport(finalReport);\n              options.onSessionEnd?.(finalReport);\n            }\n          }\n        };\n\n        const statusWhenScoringLanded = flow.getState().status;\n        if (statusWhenScoringLanded === 'paused') {\n          deferredTransitionRef.current = applyTransition;\n        } else if (statusWhenScoringLanded === 'in_progress') {\n          applyTransition();\n        }\n        // Any other status (completed/expired) means the session was ended\n        // by other means — endInterview(), or expiry — while this answer\n        // was still being scored. The score itself is preserved above, but\n        // applying a late advance()/recordFollowUp() on top of an\n        // already-finished session would silently mutate it and could\n        // fire onSessionEnd a second time with a different report.\n      } catch (caught) {\n        setError(caught instanceof Error ? caught : new Error(String(caught)));\n      } finally {\n        setIsProcessing(false);\n      }\n    },\n    [flow, options, rubric, transcript],\n  );\n\n  const submitAnswer = useCallback(\n    async (text: string, opts: SubmitAnswerOptions = {}) => {\n      if (isProcessing) return;\n\n      const beforeCount = flow.getState().answers.length;\n      // flow.submitAnswer() throws SessionExpiredError/InterviewSdkError for\n      // real, expected states (session expired mid-question, submitting\n      // outside \"in_progress\"). Left unguarded, this rejects a promise\n      // nobody awaits (onSubmit={(text) => void interview.submitAnswer(text)}),\n      // producing a silent unhandled rejection instead of visible feedback.\n      let nextState: SessionState;\n      try {\n        nextState = flow.submitAnswer({ text, ...opts });\n      } catch (caught) {\n        setFlowState(flow.getState());\n        setError(caught instanceof Error ? caught : new Error(String(caught)));\n        return;\n      }\n      setFlowState(nextState);\n\n      if (nextState.answers.length === beforeCount) {\n        // The flow engine treats this as the same turn already answered.\n        // If the last processing attempt for it failed, retry rather than\n        // silently no-op'ing — a caller may reasonably call submitAnswer\n        // again instead of retryLastAnswer().\n        if (pendingRef.current) {\n          await runProcessor(pendingRef.current);\n        }\n        return;\n      }\n\n      const question = options.questions[nextState.currentQuestionIndex];\n      if (!question) return;\n\n      // Only this question's own prior turns (i.e. earlier follow-ups on the\n      // same question) belong here — every unrelated question answered\n      // earlier in the interview was previously included too, which grew\n      // unbounded over a long interview and, worse, actively confused\n      // evaluation: by question 5 or so the model was handed several\n      // unrelated (sometimes duplicate-text) prior answers in the same\n      // request despite the system prompt telling it to ignore them, and\n      // real testing showed that noise was enough to make it misjudge a\n      // perfectly good final answer. A brand-new top-level question\n      // correctly gets an empty array here — there's no earlier turn to\n      // provide context from yet.\n      const previousTurns: EvaluationTurn[] = transcript\n        .filter((entry) => entry.question.id === question.id)\n        .map((entry) => ({\n          question: entry.question,\n          answer: entry.answer,\n        }));\n\n      const pending: PendingProcessAnswer = {\n        question,\n        answer: nextState.answers.at(-1)!,\n        previousTurns,\n        currentFollowUpDepth: nextState.followUpDepthForCurrentQuestion,\n        askedFollowUps: nextState.followUpPromptsAsked[question.id] ?? [],\n      };\n      pendingRef.current = pending;\n\n      await runProcessor(pending);\n    },\n    [flow, isProcessing, options.questions, runProcessor, transcript],\n  );\n\n  const retryLastAnswer = useCallback(async () => {\n    if (isProcessing || !pendingRef.current) return;\n    await runProcessor(pendingRef.current);\n  }, [isProcessing, runProcessor]);\n\n  const getSnapshot = useCallback(\n    (): InterviewSnapshot => ({ flowState: flow.getState(), transcript }),\n    [flow, transcript],\n  );\n\n  return {\n    status: flowState.status,\n    startedAt: flowState.startedAt,\n    currentQuestion,\n    currentPrompt,\n    isFollowUpPrompt,\n    transcript,\n    isProcessing,\n    error,\n    report,\n    start,\n    pause,\n    resume,\n    endInterview,\n    submitAnswer,\n    retryLastAnswer,\n    getSnapshot,\n    events: flow.events,\n  };\n}\n","import {\n  EvaluationEngine,\n  FollowUpEngine,\n  type AIProviderAdapter,\n  type FollowUpContext,\n  type FollowUpEngineConfig,\n} from '@interview-sdk/core';\nimport type { InterviewProcessor, ProcessAnswerInput, ProcessAnswerResult } from './types.js';\n\n/**\n * Client Mode: evaluation and follow-up generation run directly in the\n * browser against a developer-supplied AIProviderAdapter. Prototyping only\n * — see InterviewWidget's production guard (§10).\n */\nexport class ClientModeProcessor implements InterviewProcessor {\n  private readonly evaluationEngine = new EvaluationEngine();\n  private readonly followUpEngine: FollowUpEngine;\n\n  constructor(\n    private readonly adapter: AIProviderAdapter,\n    followUpConfig?: FollowUpEngineConfig,\n  ) {\n    this.followUpEngine = new FollowUpEngine(followUpConfig);\n  }\n\n  async processAnswer(input: ProcessAnswerInput): Promise<ProcessAnswerResult> {\n    const evaluation = await this.evaluationEngine.evaluate({\n      question: input.question,\n      rubric: input.rubric,\n      answer: input.answer,\n      adapter: this.adapter,\n      previousTurns: input.previousTurns,\n    });\n\n    const followUpContext: FollowUpContext = {\n      question: input.question,\n      answer: input.answer,\n      evaluation,\n      currentDepth: input.currentFollowUpDepth,\n      askedFollowUps: input.askedFollowUps,\n    };\n\n    const decision = this.followUpEngine.decide(followUpContext);\n    if (!decision.shouldGenerate) {\n      return { evaluation };\n    }\n\n    const followUp = await this.followUpEngine.generate(followUpContext, this.adapter);\n    return { evaluation, followUp };\n  }\n}\n","import type { InterviewProcessor, ProcessAnswerInput, ProcessAnswerResult } from './types.js';\n\nexport class ServerModeRequestError extends Error {}\n\nconst MAX_ERROR_BODY_LENGTH = 500;\n\n/**\n * A non-2xx response body is either the developer's own handler returning\n * a clean `{ error: string }` (the expected case — extract just that\n * message), or something else entirely reaching the browser unfiltered —\n * most commonly a framework's own HTML dev-error page when the route\n * handler itself crashed (e.g. Next.js's error overlay), but also possibly\n * a reverse proxy's error page or a raw stack trace. An HTML body is never\n * meant to be read as text, so it's replaced outright rather than merely\n * truncated; anything else unexpected is at least capped to a bounded,\n * safe length instead of dumped verbatim into a candidate-facing UI.\n */\nfunction describeErrorBody(bodyText: string, contentType: string | null): string {\n  try {\n    const parsed = JSON.parse(bodyText) as { error?: unknown };\n    if (typeof parsed.error === 'string') return parsed.error;\n  } catch {\n    // Not JSON — fall through below.\n  }\n  if (contentType?.includes('text/html') || bodyText.trimStart().startsWith('<!DOCTYPE')) {\n    return 'The server returned an unexpected error page instead of a JSON response — check your server logs for details.';\n  }\n  return bodyText.length > MAX_ERROR_BODY_LENGTH\n    ? `${bodyText.slice(0, MAX_ERROR_BODY_LENGTH)}…`\n    : bodyText;\n}\n\nexport interface ServerModeProcessorConfig {\n  /**\n   * URL to POST each answer to. The developer's `@interview-sdk/server`\n   * route handles evaluation and follow-up generation server-side, so AI\n   * keys and score integrity never reach the browser. Defaults to\n   * `/api/interview/answer`.\n   */\n  endpoint?: string;\n  /** Override fetch (e.g. for testing). */\n  fetchImpl?: typeof fetch;\n  /** Extra headers to send with every request (e.g. a session/auth token). */\n  headers?: Record<string, string>;\n}\n\n/**\n * Server Mode: each answer is sent in a single request to the developer's\n * own backend, which evaluates it (using the same @interview-sdk/core\n * engines, server-side) and returns the result. This is the wire contract\n * @interview-sdk/server (Phase 5) implements — see that package's docs for\n * the authoritative shape once it exists.\n */\nexport class ServerModeProcessor implements InterviewProcessor {\n  private readonly endpoint: string;\n  private readonly fetchImpl: typeof fetch;\n  private readonly headers: Record<string, string>;\n\n  constructor(config: ServerModeProcessorConfig = {}) {\n    this.endpoint = config.endpoint ?? '/api/interview/answer';\n    this.fetchImpl = config.fetchImpl ?? fetch.bind(globalThis);\n    this.headers = config.headers ?? {};\n  }\n\n  async processAnswer(input: ProcessAnswerInput): Promise<ProcessAnswerResult> {\n    const response = await this.fetchImpl(this.endpoint, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json', ...this.headers },\n      body: JSON.stringify(input),\n    });\n\n    if (!response.ok) {\n      const bodyText = await response.text().catch(() => '');\n      const detail = bodyText ? describeErrorBody(bodyText, response.headers.get('Content-Type')) : '';\n      throw new ServerModeRequestError(\n        `Interview server responded with ${response.status} ${response.statusText}` +\n          (detail ? `: ${detail}` : ''),\n      );\n    }\n\n    const data = (await response.json()) as Partial<ProcessAnswerResult> | null;\n    if (!data || typeof data !== 'object' || !data.evaluation) {\n      throw new ServerModeRequestError('Interview server response did not include an evaluation.');\n    }\n\n    return data as ProcessAnswerResult;\n  }\n}\n","import type { CSSProperties } from 'react';\n\nexport interface AudioLevelMeterProps {\n  /** Normalized 0..1 amplitude samples — one bar per entry. */\n  levels: number[];\n  /** Color/role of the meter: the AI's voice, or the candidate's mic. */\n  variant: 'speaking' | 'listening';\n  /** Renders a calm ambient pulse instead of reactive bars — used whenever live amplitude data isn't available. */\n  isIdle?: boolean;\n  className?: string;\n}\n\n/**\n * Purely presentational amplitude bars, shared by QuestionAudio (AI\n * speaking) and MicButton (candidate listening). Contains no Web Audio\n * code — callers own sampling the real data.\n */\nexport function AudioLevelMeter({ levels, variant, isIdle = false, className }: AudioLevelMeterProps) {\n  const barCount = Math.max(levels.length, 3);\n  const bars = Array.from({ length: barCount }, (_, i) => levels[i] ?? 0.08);\n\n  const classes = ['isdk-audio-meter', `isdk-audio-meter--${variant}`, isIdle ? 'isdk-audio-meter--idle' : '']\n    .filter(Boolean)\n    .join(' ');\n\n  return (\n    <span className={className ? `${classes} ${className}` : classes} aria-hidden=\"true\">\n      {bars.map((level, i) => (\n        <span key={i} style={{ '--isdk-meter-level': Math.max(0.08, level) } as CSSProperties} />\n      ))}\n    </span>\n  );\n}\n","import type { TranscriptEntry } from '../hooks/build-report.js';\n\nexport interface FeedbackNoteProps {\n  transcript: TranscriptEntry[];\n  /** Label shown above the note. Defaults to 'AI Interviewer', matching the stage tile's own label. */\n  assistantName?: string;\n}\n\n/**\n * A chat-style note surfacing the AI's own rationale for the most recent\n * scored answer — evaluation.rationale is a real field populated by the\n * evaluation engine's model response (see @interview-sdk/core), not\n * fabricated UI copy. Renders nothing when no scored entry has a rationale\n * yet (e.g. a skipped/silent answer, or an adapter that doesn't return one).\n */\nexport function FeedbackNote({ transcript, assistantName = 'AI Interviewer' }: FeedbackNoteProps) {\n  const latestWithRationale = [...transcript].reverse().find((entry) => entry.evaluation.rationale);\n  if (!latestWithRationale) return null;\n\n  return (\n    <div className=\"isdk-feedback-note\" role=\"status\">\n      <p className=\"isdk-feedback-note__label\">{assistantName}</p>\n      <p className=\"isdk-feedback-note__text\">{latestWithRationale.evaluation.rationale}</p>\n    </div>\n  );\n}\n","/**\n * Pure amplitude math shared by the AI-speaking meter (offline envelope of\n * decoded TTS audio) and the candidate-mic meter (live analyser bytes). No\n * DOM/Web Audio types here on purpose — fully unit-testable without a\n * browser.\n */\n\n/** Average magnitude of `channelData` bucketed into `bucketCount` values, each normalized to roughly 0..1. */\nexport function computeEnvelope(channelData: Float32Array, bucketCount: number): number[] {\n  if (bucketCount <= 0 || channelData.length === 0) return [];\n\n  const bucketSize = Math.max(1, Math.floor(channelData.length / bucketCount));\n  const envelope: number[] = [];\n\n  for (let i = 0; i < bucketCount; i++) {\n    const start = i * bucketSize;\n    const end = i === bucketCount - 1 ? channelData.length : start + bucketSize;\n    let sum = 0;\n    let count = 0;\n    for (let j = start; j < end && j < channelData.length; j++) {\n      sum += Math.abs(channelData[j]!);\n      count++;\n    }\n    envelope.push(count > 0 ? sum / count : 0);\n  }\n\n  const peak = Math.max(...envelope, 1e-6);\n  return envelope.map((value) => Math.min(1, value / peak));\n}\n\n/** Windowed snapshot of `barCount` envelope values centered around normalized playback `progress` (0..1). */\nexport function sampleEnvelope(envelope: number[], progress: number, barCount: number): number[] {\n  if (envelope.length === 0 || barCount <= 0) return new Array(Math.max(0, barCount)).fill(0);\n\n  const center = Math.round(Math.min(1, Math.max(0, progress)) * (envelope.length - 1));\n  const half = Math.floor(barCount / 2);\n\n  return Array.from({ length: barCount }, (_, i) => {\n    const index = center - half + i;\n    return index >= 0 && index < envelope.length ? envelope[index]! : 0;\n  });\n}\n\n/** Buckets raw analyser byte data (0-255) into `barCount` normalized 0..1 averages. */\nexport function bytesToLevels(data: Uint8Array, barCount: number): number[] {\n  if (barCount <= 0 || data.length === 0) return [];\n\n  const bucketSize = Math.max(1, Math.floor(data.length / barCount));\n  const levels: number[] = [];\n\n  for (let i = 0; i < barCount; i++) {\n    const start = i * bucketSize;\n    const end = i === barCount - 1 ? data.length : start + bucketSize;\n    let sum = 0;\n    let count = 0;\n    for (let j = start; j < end && j < data.length; j++) {\n      sum += data[j]!;\n      count++;\n    }\n    levels.push(count > 0 ? sum / count / 255 : 0);\n  }\n\n  return levels;\n}\n","/** Feature-detects AudioContext (incl. Safari's legacy-prefixed constructor) without `any`. */\nexport function getAudioContextCtor(): typeof AudioContext | undefined {\n  if (typeof window === 'undefined') return undefined;\n  return (\n    window.AudioContext ??\n    (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n  );\n}\n","import { useEffect, useState } from 'react';\nimport { bytesToLevels } from './audio-level-math.js';\nimport { getAudioContextCtor } from './web-audio-support.js';\n\nexport interface UseMicLevelMeterResult {\n  levels: number[];\n  isSupported: boolean;\n}\n\nconst BAR_COUNT = 5;\nconst SAMPLE_INTERVAL_MS = 60;\n\n/**\n * Live amplitude meter for the candidate's own mic input, sourced from the\n * SAME MediaStream already feeding MediaRecorder — never a second\n * getUserMedia() request. The analyser graph terminates at the\n * AnalyserNode and is never connected onward to `ctx.destination`, so the\n * candidate's own voice is never routed back to their speakers.\n *\n * Only the feature-detection/no-throw/cleanup paths are exercisable under\n * jsdom (no real AudioContext exists there); the amplitude math itself is\n * covered in audio-level-math.test.ts.\n */\nexport function useMicLevelMeter(stream: MediaStream | null, active: boolean): UseMicLevelMeterResult {\n  const [levels, setLevels] = useState<number[]>(() => new Array(BAR_COUNT).fill(0));\n  const [isSupported, setIsSupported] = useState(false);\n\n  // Only rendered while actively recording (see MicButton), so there is no\n  // window in which a stale value from a previous recording bout would be\n  // visible — no reset needed when this re-runs for a new stream/session.\n  useEffect(() => {\n    const Ctor = getAudioContextCtor();\n    if (!Ctor || !stream || !active) return;\n\n    let frame: number;\n    let ctx: AudioContext;\n    let analyser: AnalyserNode;\n    let source: MediaStreamAudioSourceNode;\n\n    try {\n      ctx = new Ctor();\n      analyser = ctx.createAnalyser();\n      analyser.fftSize = 256;\n      source = ctx.createMediaStreamSource(stream);\n      source.connect(analyser); // deliberately not connected onward to ctx.destination — no feedback\n    } catch {\n      return;\n    }\n\n    const data = new Uint8Array(analyser.frequencyBinCount);\n    let lastSample = 0;\n    let hasMarkedSupported = false;\n\n    const tick = (time: number) => {\n      // Deferred to the first real tick (not called synchronously in the\n      // effect body) — an AudioContext existing doesn't guarantee data ever\n      // actually flows; this marks \"supported\" only once it demonstrably does.\n      if (!hasMarkedSupported) {\n        hasMarkedSupported = true;\n        setIsSupported(true);\n      }\n      if (time - lastSample >= SAMPLE_INTERVAL_MS) {\n        lastSample = time;\n        analyser.getByteFrequencyData(data);\n        setLevels(bytesToLevels(data, BAR_COUNT));\n      }\n      frame = requestAnimationFrame(tick);\n    };\n    frame = requestAnimationFrame(tick);\n\n    return () => {\n      cancelAnimationFrame(frame);\n      source.disconnect();\n      analyser.disconnect();\n      if (ctx.state !== 'closed') void ctx.close().catch(() => {});\n    };\n  }, [stream, active]);\n\n  return { levels, isSupported };\n}\n","import { useEffect, useState } from 'react';\nimport { AudioLevelMeter } from './AudioLevelMeter.js';\nimport { useMicLevelMeter } from '../hooks/useMicLevelMeter.js';\n\nexport interface InterviewLobbyProps {\n  /** Begins the real interview session — typically useInterview's start(). */\n  onJoin: () => void;\n  /** Whether this interview uses voice at all (a transcribe function was given to InterviewWidget). Text-only mode skips the mic check entirely. */\n  voiceEnabled: boolean;\n  totalQuestions?: number;\n  /**\n   * Requests a live mic stream purely to preview it — never produces a\n   * recording, distinct from MicButton's Blob-producing AudioRecorder.\n   * Defaults to a real getUserMedia({ audio: true }) call. Injectable for\n   * testing.\n   */\n  requestMicStream?: () => Promise<MediaStream>;\n  /** Overrides the primary action's visible text. Defaults to 'Start interview'. */\n  joinLabel?: string;\n}\n\nasync function defaultRequestMicStream(): Promise<MediaStream> {\n  if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n    throw new Error('This browser does not support microphone capture.');\n  }\n  return navigator.mediaDevices.getUserMedia({ audio: true });\n}\n\ntype CheckStatus = 'idle' | 'checking' | 'active' | 'error';\n\n/**\n * A pre-join screen — a mic check plus a \"ready to begin\" confirmation,\n * matching the lobby real video-call/interview platforms put candidates\n * through before a live session. The mic check is always optional: joining\n * never depends on it succeeding, matching this codebase's standing rule\n * that voice is an enhancement, never a blocker.\n */\nexport function InterviewLobby({\n  onJoin,\n  voiceEnabled,\n  totalQuestions,\n  requestMicStream = defaultRequestMicStream,\n  joinLabel = 'Start interview',\n}: InterviewLobbyProps) {\n  const [status, setStatus] = useState<CheckStatus>('idle');\n  const [stream, setStream] = useState<MediaStream | null>(null);\n  const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);\n\n  const { levels, isSupported } = useMicLevelMeter(stream, status === 'active');\n\n  useEffect(() => {\n    // Stop the preview stream if the candidate navigates away without\n    // joining — the mic must never stay hot longer than this screen.\n    return () => {\n      stream?.getTracks().forEach((track) => track.stop());\n    };\n  }, [stream]);\n\n  const testMicrophone = async () => {\n    setStatus('checking');\n    setErrorMessage(undefined);\n    try {\n      const nextStream = await requestMicStream();\n      setStream(nextStream);\n      setStatus('active');\n    } catch (error) {\n      setErrorMessage(\n        error instanceof Error ? error.message : 'Microphone access was denied or is unavailable.',\n      );\n      setStatus('error');\n    }\n  };\n\n  const handleJoin = () => {\n    stream?.getTracks().forEach((track) => track.stop());\n    onJoin();\n  };\n\n  // One cohesive meta line rather than two flat, disconnected sentences —\n  // question count and answer mode are both quick facts, not separate\n  // thoughts, so they read as a single scannable line.\n  const metaParts: string[] = [];\n  if (typeof totalQuestions === 'number') {\n    metaParts.push(`${totalQuestions} question${totalQuestions === 1 ? '' : 's'}`);\n  }\n  if (!voiceEnabled) {\n    metaParts.push('Answered by typing');\n  }\n\n  return (\n    <div className=\"isdk-lobby\">\n      <p className=\"isdk-kicker\">Before you begin</p>\n      <h2 className=\"isdk-lobby__title\">\n        {voiceEnabled ? 'Ready to join the interview?' : 'Ready to begin?'}\n      </h2>\n      {metaParts.length > 0 && <p className=\"isdk-lobby__meta\">{metaParts.join(' · ')}</p>}\n\n      {voiceEnabled && (\n        <div className=\"isdk-lobby__mic-check\">\n          {status === 'active' ? (\n            <div className=\"isdk-lobby__mic-active\">\n              <AudioLevelMeter levels={levels} variant=\"listening\" isIdle={!isSupported} />\n              <span>Microphone connected — speak to see the level move.</span>\n            </div>\n          ) : (\n            <button\n              className=\"isdk-btn isdk-btn--secondary\"\n              type=\"button\"\n              onClick={() => void testMicrophone()}\n              disabled={status === 'checking'}\n            >\n              {status === 'checking' ? 'Checking microphone…' : 'Test microphone'}\n            </button>\n          )}\n          {status === 'error' && (\n            <p className=\"isdk-lobby__hint\" role=\"note\" aria-label=\"Microphone check\">\n              {errorMessage} You can still continue — typing is always available as a fallback.\n            </p>\n          )}\n        </div>\n      )}\n\n      <div className=\"isdk-lobby__actions\">\n        <button className=\"isdk-btn isdk-btn--primary\" type=\"button\" onClick={handleJoin}>\n          {joinLabel}\n        </button>\n      </div>\n    </div>\n  );\n}\n","import type { Question } from '@interview-sdk/core';\nimport type { TranscriptEntry } from '../hooks/build-report.js';\n\nexport interface InterviewProgressProps {\n  questions: Question[];\n  currentQuestion: Question | undefined;\n  transcript: TranscriptEntry[];\n}\n\ntype QuestionStatus = 'done' | 'active' | 'upcoming';\n\nfunction statusOf(\n  question: Question,\n  currentQuestion: Question | undefined,\n  transcript: TranscriptEntry[],\n): QuestionStatus {\n  if (currentQuestion?.id === question.id) return 'active';\n  const answered = transcript.some((entry) => !entry.isFollowUp && entry.question.id === question.id);\n  return answered ? 'done' : 'upcoming';\n}\n\nconst STATUS_LABEL: Record<QuestionStatus, string> = {\n  done: 'Completed',\n  active: 'In progress',\n  upcoming: 'Not started yet',\n};\n\n/**\n * A running checklist of every question in the interview and where the\n * candidate is right now — built entirely from data useInterview already\n * exposes (questions + transcript + currentQuestion), no new session state.\n */\nexport function InterviewProgress({ questions, currentQuestion, transcript }: InterviewProgressProps) {\n  return (\n    <div className=\"isdk-progress\">\n      <p className=\"isdk-kicker\">Interview progress</p>\n      <ol className=\"isdk-progress__list\" aria-label=\"Interview progress\">\n        {questions.map((question) => {\n          const status = statusOf(question, currentQuestion, transcript);\n          const topic = question.concepts?.[0];\n\n          return (\n            <li\n              key={question.id}\n              className={`isdk-progress__item isdk-progress__item--${status}`}\n            >\n              <span className=\"isdk-progress__icon\" role=\"img\" aria-label={STATUS_LABEL[status]}>\n                {status === 'done' && (\n                  <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n                    <path\n                      d=\"M3 8.5L6.2 11.5L13 4.5\"\n                      stroke=\"currentColor\"\n                      strokeWidth=\"1.8\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                )}\n                {status === 'active' && <span className=\"isdk-progress__dot\" aria-hidden=\"true\" />}\n              </span>\n              <div className=\"isdk-progress__body\">\n                {topic && <p className=\"isdk-progress__topic\">{topic}</p>}\n                <p className=\"isdk-progress__prompt\">{question.prompt}</p>\n              </div>\n            </li>\n          );\n        })}\n      </ol>\n    </div>\n  );\n}\n","import type { Rubric } from '@interview-sdk/core';\nimport { STRENGTH_THRESHOLD, type TranscriptEntry } from '../hooks/build-report.js';\n\nexport interface LiveSignalsProps {\n  rubric: Rubric;\n  transcript: TranscriptEntry[];\n}\n\n/**\n * Live per-dimension score meters for the most recently answered question —\n * reads straight from the latest transcript entry's real dimensionScores,\n * the same evaluation data ScoreSummary uses for the final report. Renders\n * nothing until at least one answer has been scored.\n *\n * Styled as a vertical instrument cluster (think a mixing console's level\n * meters) rather than the horizontal \"skill bar\" almost every scoring UI\n * defaults to — each meter also carries a real tick at STRENGTH_THRESHOLD,\n * the same cutoff the final report uses to call a dimension a strength, so\n * \"does this clear the bar\" reads at a glance instead of requiring the\n * candidate or reviewer to do the comparison themselves.\n */\nexport function LiveSignals({ rubric, transcript }: LiveSignalsProps) {\n  const latest = transcript.at(-1);\n  if (!latest) return null;\n\n  return (\n    <div className=\"isdk-live-signals\" role=\"log\" aria-live=\"polite\">\n      <p className=\"isdk-kicker\">Live signals</p>\n      <ul className=\"isdk-live-signals__list\">\n        {rubric.dimensions.map((dimension) => {\n          const score = latest.evaluation.dimensionScores[dimension.id];\n          if (score === undefined) return null;\n          const clamped = Math.min(100, Math.max(0, score));\n\n          return (\n            <li key={dimension.id} className=\"isdk-live-signals__meter\">\n              <span className=\"isdk-live-signals__score isdk-tabular\">{Math.round(clamped)}</span>\n              <span\n                className=\"isdk-live-signals__track\"\n                role=\"img\"\n                aria-label={`${dimension.label}: ${Math.round(clamped)} out of 100`}\n              >\n                <span\n                  className=\"isdk-live-signals__threshold\"\n                  style={{ bottom: `${STRENGTH_THRESHOLD}%` }}\n                />\n                <span\n                  className={\n                    clamped >= STRENGTH_THRESHOLD\n                      ? 'isdk-live-signals__fill isdk-live-signals__fill--pass'\n                      : 'isdk-live-signals__fill'\n                  }\n                  style={{ height: `${clamped}%` }}\n                />\n              </span>\n              <span className=\"isdk-live-signals__label\">{dimension.label}</span>\n            </li>\n          );\n        })}\n      </ul>\n    </div>\n  );\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { AudioLevelMeter } from './AudioLevelMeter.js';\nimport { useMicLevelMeter } from '../hooks/useMicLevelMeter.js';\n\nexport interface AudioRecorder {\n  stop: () => Promise<Blob>;\n  /**\n   * The live MediaStream backing this recording, when available — enables\n   * a real input-level meter on the SAME stream already feeding\n   * MediaRecorder, never a second getUserMedia() request. Optional:\n   * test-injected fake recorders can omit it and the live meter simply\n   * doesn't render.\n   */\n  stream?: MediaStream;\n}\n\nasync function createBrowserAudioRecorder(): Promise<AudioRecorder> {\n  if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n    throw new Error('This browser does not support microphone capture.');\n  }\n  if (typeof MediaRecorder === 'undefined') {\n    throw new Error('This browser does not support MediaRecorder.');\n  }\n\n  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n  const mediaRecorder = new MediaRecorder(stream);\n  const chunks: Blob[] = [];\n  mediaRecorder.ondataavailable = (event) => {\n    if (event.data.size > 0) chunks.push(event.data);\n  };\n  mediaRecorder.start();\n\n  return {\n    stream,\n    stop: () =>\n      new Promise<Blob>((resolve) => {\n        mediaRecorder.onstop = () => {\n          for (const track of stream.getTracks()) track.stop();\n          resolve(new Blob(chunks, { type: mediaRecorder.mimeType || 'audio/webm' }));\n        };\n        mediaRecorder.stop();\n      }),\n  };\n}\n\nexport interface MicButtonProps {\n  /** Turns captured audio into text — typically a VoiceProviderAdapter's transcribe(). */\n  transcribe: (audio: Blob) => Promise<string>;\n  onTranscript: (text: string) => void;\n  /**\n   * Called on mic denial, capture failure, empty audio, or a transcription\n   * error. The candidate always has the text input in QuestionCard as a\n   * fallback — this button never needs to render its own.\n   */\n  onError?: (error: Error) => void;\n  disabled?: boolean;\n  label?: string;\n  /** Injectable for testing; defaults to a real getUserMedia/MediaRecorder implementation. */\n  createRecorder?: () => Promise<AudioRecorder>;\n  /** Notified whenever recording starts/stops — informational only, not fired for the initial mount. */\n  onRecordingChange?: (isRecording: boolean) => void;\n  /** Promotes this control to the primary, inviting call-to-action (e.g. once it's the candidate's turn). Defaults to false — zero visual change for existing callers. */\n  emphasized?: boolean;\n  /** Live input-amplitude meter while recording, sourced from the same capture stream already feeding MediaRecorder. Defaults to true; renders nothing when no stream is available. */\n  showLevelMeter?: boolean;\n  /** Notified with the live amplitude data whenever it changes — lets a caller drive its own separate meter display (e.g. a self-view tile) instead of this button's own inline one. Informational only. */\n  onLevelsChange?: (levels: number[], isSupported: boolean) => void;\n}\n\nexport function MicButton({\n  transcribe,\n  onTranscript,\n  onError,\n  disabled = false,\n  label = 'Record answer',\n  createRecorder = createBrowserAudioRecorder,\n  onRecordingChange,\n  emphasized = false,\n  showLevelMeter = true,\n  onLevelsChange,\n}: MicButtonProps) {\n  const [isRecording, setIsRecording] = useState(false);\n  const [isTranscribing, setIsTranscribing] = useState(false);\n  // The stream is also mirrored into state (not read from the ref during\n  // render) since the level-meter hook below needs a reactive value.\n  const [activeStream, setActiveStream] = useState<MediaStream | null>(null);\n  const recorderRef = useRef<AudioRecorder | null>(null);\n  const isFirstRender = useRef(true);\n\n  useEffect(() => {\n    if (isFirstRender.current) {\n      isFirstRender.current = false;\n      return;\n    }\n    onRecordingChange?.(isRecording);\n    // eslint-disable-next-line react-hooks/exhaustive-deps -- notify on isRecording transitions only, skipping the initial mount\n  }, [isRecording]);\n\n  const { levels, isSupported } = useMicLevelMeter(activeStream, isRecording);\n\n  useEffect(() => {\n    onLevelsChange?.(levels, isSupported);\n    // eslint-disable-next-line react-hooks/exhaustive-deps -- report on data changes only; onLevelsChange is expected to be stable per session\n  }, [levels, isSupported]);\n\n  // Mirrored into a ref (rather than cleaning up on every `activeStream`\n  // change) so the mount-cleanup effect below only stops tracks once, at\n  // actual unmount — not a second time on top of stopAndTranscribe's own\n  // recorder.stop(), which already nulls activeStream as part of its normal\n  // stop flow.\n  const activeStreamRef = useRef<MediaStream | null>(null);\n  useEffect(() => {\n    activeStreamRef.current = activeStream;\n  }, [activeStream]);\n\n  useEffect(() => {\n    // Only stopAndTranscribe's own recorder.stop() releases tracks — if this\n    // button unmounts mid-recording (e.g. the candidate clicks Pause, which\n    // swaps the whole question view away without ever calling stop), the\n    // mic would otherwise stay hot indefinitely. Mirrors InterviewLobby's\n    // own preview-stream cleanup.\n    return () => {\n      activeStreamRef.current?.getTracks().forEach((track) => track.stop());\n    };\n  }, []);\n\n  const stopAndTranscribe = useCallback(async () => {\n    const recorder = recorderRef.current;\n    recorderRef.current = null;\n    setIsRecording(false);\n    setActiveStream(null);\n    if (!recorder) return;\n\n    setIsTranscribing(true);\n    try {\n      const audio = await recorder.stop();\n      if (audio.size === 0) {\n        throw new Error(\n          'No audio was captured. Check your microphone and try again, or type your answer.',\n        );\n      }\n      const text = await transcribe(audio);\n      onTranscript(text);\n    } catch (error) {\n      onError?.(error instanceof Error ? error : new Error(String(error)));\n    } finally {\n      setIsTranscribing(false);\n    }\n  }, [onError, onTranscript, transcribe]);\n\n  const startRecording = useCallback(async () => {\n    try {\n      const recorder = await createRecorder();\n      recorderRef.current = recorder;\n      setActiveStream(recorder.stream ?? null);\n      setIsRecording(true);\n    } catch (error) {\n      onError?.(\n        error instanceof Error\n          ? error\n          : new Error('Microphone access was denied or is unavailable.'),\n      );\n    }\n  }, [createRecorder, onError]);\n\n  const handleClick = useCallback(() => {\n    if (isRecording) {\n      void stopAndTranscribe();\n    } else {\n      void startRecording();\n    }\n  }, [isRecording, startRecording, stopAndTranscribe]);\n\n  const buttonClass = emphasized ? 'isdk-btn isdk-btn--mic isdk-btn--mic-invite' : 'isdk-btn isdk-btn--mic';\n\n  return (\n    <span className=\"isdk-mic\">\n      <button\n        className={buttonClass}\n        type=\"button\"\n        onClick={handleClick}\n        disabled={disabled || isTranscribing}\n        aria-pressed={isRecording}\n        aria-label={isRecording ? 'Stop recording' : label}\n      >\n        {isRecording ? (\n          <svg className=\"isdk-btn__icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n            <rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"2\" fill=\"currentColor\" />\n          </svg>\n        ) : (\n          <svg className=\"isdk-btn__icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n            <rect x=\"9\" y=\"3\" width=\"6\" height=\"11\" rx=\"3\" fill=\"currentColor\" />\n            <path\n              d=\"M5 11a7 7 0 0 0 14 0M12 18v3\"\n              stroke=\"currentColor\"\n              strokeWidth=\"1.6\"\n              strokeLinecap=\"round\"\n            />\n          </svg>\n        )}\n        <span className=\"isdk-btn__label\">\n          {isTranscribing ? 'Transcribing…' : isRecording ? 'Stop recording' : label}\n        </span>\n      </button>\n      {showLevelMeter && isRecording && (\n        <AudioLevelMeter levels={levels} variant=\"listening\" isIdle={!isSupported} />\n      )}\n    </span>\n  );\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { computeEnvelope, sampleEnvelope } from './audio-level-math.js';\nimport { getAudioContextCtor } from './web-audio-support.js';\n\nexport interface UseSpeechLevelMeterResult {\n  levels: number[];\n  isSupported: boolean;\n}\n\nconst BAR_COUNT = 5;\nconst ENVELOPE_BUCKETS = 200;\nconst SAMPLE_INTERVAL_MS = 60; // ~16 updates/sec — plenty smooth, cheap enough to re-render on\n\nfunction toArrayBuffer(data: ArrayBuffer | Uint8Array): ArrayBuffer {\n  if (data instanceof ArrayBuffer) return data.slice(0);\n  return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer;\n}\n\n/**\n * Drives a live-feeling amplitude meter for synthesized speech WITHOUT ever\n * routing the <audio> element through a live AnalyserNode. A freshly\n * created AudioContext starts 'suspended' until a user gesture resumes it\n * — exactly the autoplay case this widget already has to handle — so if\n * the element's output were re-routed through a suspended context's graph,\n * playback would go silent with no error while `ended` still fires\n * normally. That would regress already-working audio behind an invisible\n * bug. Instead: decode the already-in-memory TTS bytes into an amplitude\n * envelope once, offline, and sample it against the real element's own\n * currentTime/duration during real playback. The element's native output\n * is never touched.\n *\n * Only the feature-detection/no-throw/cleanup paths are exercisable under\n * jsdom (no real AudioContext or audio decoding exists there) — the\n * amplitude math itself is covered in audio-level-math.test.ts.\n */\nexport function useSpeechLevelMeter(\n  audioEl: HTMLAudioElement | null,\n  rawAudio: ArrayBuffer | Uint8Array | null,\n  isPlaying: boolean,\n): UseSpeechLevelMeterResult {\n  const [levels, setLevels] = useState<number[]>(() => new Array(BAR_COUNT).fill(0));\n  const [isSupported, setIsSupported] = useState(false);\n  const envelopeRef = useRef<number[] | null>(null);\n\n  // One instance of this hook lives for one QuestionAudio mount, which\n  // itself remounts fresh per prompt (key={prompt} on the caller) — so\n  // `rawAudio` only ever transitions null -> populated once per mount,\n  // never back again, and the initial useState values above already cover\n  // the \"nothing decoded yet\" case with no reset needed here.\n  useEffect(() => {\n    const Ctor = getAudioContextCtor();\n    if (!Ctor || !rawAudio) return;\n\n    let cancelled = false;\n    const ctx = new Ctor();\n\n    ctx\n      .decodeAudioData(toArrayBuffer(rawAudio))\n      .then((buffer) => {\n        if (cancelled) return;\n        envelopeRef.current = computeEnvelope(buffer.getChannelData(0), ENVELOPE_BUCKETS);\n        setIsSupported(true);\n      })\n      .catch(() => {\n        // A decode failure only degrades the visualization to idle — it is\n        // never reported as a voice error; the question audio itself\n        // (already decoded natively by the <audio> element) is unaffected.\n      })\n      .finally(() => {\n        if (ctx.state !== 'closed') void ctx.close().catch(() => {});\n      });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [rawAudio]);\n\n  useEffect(() => {\n    if (!isPlaying || !audioEl || !isSupported) return;\n\n    let frame: number;\n    let lastSample = 0;\n\n    const tick = (time: number) => {\n      if (time - lastSample >= SAMPLE_INTERVAL_MS && envelopeRef.current) {\n        lastSample = time;\n        const duration = audioEl.duration;\n        const progress = duration > 0 ? audioEl.currentTime / duration : 0;\n        setLevels(sampleEnvelope(envelopeRef.current, progress, BAR_COUNT));\n      }\n      frame = requestAnimationFrame(tick);\n    };\n    frame = requestAnimationFrame(tick);\n\n    return () => cancelAnimationFrame(frame);\n  }, [isPlaying, audioEl, isSupported]);\n\n  return { levels, isSupported };\n}\n","import type { SynthesisResult } from '@interview-sdk/core';\nimport { useEffect, useState } from 'react';\nimport { AudioLevelMeter } from './AudioLevelMeter.js';\nimport { useSpeechLevelMeter } from '../hooks/useSpeechLevelMeter.js';\n\nexport interface QuestionAudioProps {\n  /** The prompt text to speak — re-synthesized whenever this changes. */\n  text: string;\n  /** Turns text into speech — typically a VoiceProviderAdapter's synthesize(). */\n  synthesize: (text: string) => Promise<SynthesisResult>;\n  /**\n   * Called when synthesis fails or playback is unavailable. The question\n   * text is always visible in QuestionCard regardless — this is a bonus\n   * audio channel, never the only way to get the question.\n   */\n  onError?: (error: Error) => void;\n  /** Attempts to play as soon as audio is ready. Defaults to true. */\n  autoPlay?: boolean;\n  /** Fires on the <audio> element's real 'play' event — turn-state signal for a caller coordinating a mic control. */\n  onPlaybackStart?: () => void;\n  /** Fires on the <audio> element's real 'pause'/'ended' events. */\n  onPlaybackEnd?: () => void;\n  /** Shows a live playback-amplitude presence indicator next to the control. Defaults to true; gracefully falls back to an ambient pulse wherever Web Audio decoding isn't available. */\n  showLevelMeter?: boolean;\n  /** Mutes the underlying <audio> element's output — playback (and onPlaybackStart/onPlaybackEnd turn-state) still runs normally, only the sound is silenced. Defaults to false. */\n  muted?: boolean;\n  /** A short label (e.g. \"AI\") centered in the orb while not speaking, so the tile reads as an identity, not a bare circle — replaced by the live level meter once playback actually starts. Omit for the orb's previous meter-only behavior. */\n  avatarLabel?: string;\n}\n\ntype Status = 'loading' | 'ready' | 'error';\n\n/**\n * Speaks a question aloud via a developer-supplied synthesize() and always\n * offers a manual control — autoplay is attempted but browsers block it\n * without a prior user gesture, so a blocked attempt just falls back to a\n * \"Play question\" button instead of failing silently.\n */\nexport function QuestionAudio({\n  text,\n  synthesize,\n  onError,\n  autoPlay = true,\n  onPlaybackStart,\n  onPlaybackEnd,\n  showLevelMeter = true,\n  muted = false,\n  avatarLabel,\n}: QuestionAudioProps) {\n  // A state-backed ref (not useRef): the level-meter hook below needs to\n  // react to the element becoming available, and reading a plain ref's\n  // `.current` during render isn't safe/reactive.\n  const [audioEl, setAudioEl] = useState<HTMLAudioElement | null>(null);\n  const [audioUrl, setAudioUrl] = useState<string | null>(null);\n  const [rawAudio, setRawAudio] = useState<ArrayBuffer | Uint8Array | null>(null);\n  const [status, setStatus] = useState<Status>('loading');\n  const [hasPlayedOnce, setHasPlayedOnce] = useState(false);\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  // Assumes one QuestionAudio instance per question/follow-up prompt (the\n  // caller renders it with `key={text}`) — a fresh mount naturally starts\n  // from the default 'loading' state, so there's nothing to reset here.\n  useEffect(() => {\n    let cancelled = false;\n    let objectUrl: string | null = null;\n\n    synthesize(text)\n      .then((result) => {\n        if (cancelled) return;\n        objectUrl = URL.createObjectURL(\n          new Blob([result.audio as BlobPart], { type: result.mimeType }),\n        );\n        setAudioUrl(objectUrl);\n        setRawAudio(result.audio);\n        setStatus('ready');\n      })\n      .catch((error: unknown) => {\n        if (cancelled) return;\n        setStatus('error');\n        onError?.(error instanceof Error ? error : new Error(String(error)));\n      });\n\n    return () => {\n      cancelled = true;\n      if (objectUrl) URL.revokeObjectURL(objectUrl);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps -- re-synthesize on text change only; synthesize/onError are expected to be stable per session\n  }, [text]);\n\n  useEffect(() => {\n    // audioEl starts null and populates via the ref callback below on its\n    // own commit/render — without this guard, `audioEl?.play()` on that\n    // first null pass resolves to `Promise.resolve(undefined)`, which\n    // always \"succeeds\" and would wrongly mark hasPlayedOnce before the\n    // real autoplay attempt (once the element actually exists) ever runs.\n    if (status !== 'ready' || !autoPlay || !audioEl) return;\n    // Always start from the beginning — matters once a caller's own replay\n    // logic (or this same effect re-firing for any reason) targets an\n    // element that already reached the end: play() from an end-of-track\n    // position produces no audible sound at all.\n    // eslint-disable-next-line react-hooks/immutability -- a real DOM element's own playback position, not application state; audioEl is only state-backed for the level-meter hook's reactivity (see the field comment above)\n    audioEl.currentTime = 0;\n    // Wrapped in Promise.resolve(): most browsers return a Promise from\n    // play(), but older Safari can return undefined instead — treat that as\n    // playback having started, same as it does natively.\n    Promise.resolve(audioEl.play())\n      .then(() => setHasPlayedOnce(true))\n      .catch(() => {\n        // Autoplay was blocked (no prior user gesture in this tab) — the\n        // \"Play question\" button below covers this, so it's not an error.\n      });\n  }, [status, autoPlay, audioEl]);\n\n  const { levels, isSupported } = useSpeechLevelMeter(audioEl, rawAudio, isPlaying);\n\n  if (status === 'error') return null;\n\n  const play = () => {\n    if (!audioEl) return;\n    // \"Replay question\" after the track already ended must rewind first —\n    // otherwise play() just resumes from the end-of-track position and\n    // produces no audible sound, even though playback technically \"works\".\n    // eslint-disable-next-line react-hooks/immutability -- a real DOM element's own playback position, not application state\n    audioEl.currentTime = 0;\n    Promise.resolve(audioEl.play())\n      .then(() => setHasPlayedOnce(true))\n      .catch((error: unknown) =>\n        onError?.(error instanceof Error ? error : new Error(String(error))),\n      );\n  };\n\n  const handlePlaybackStart = () => {\n    setIsPlaying(true);\n    onPlaybackStart?.();\n  };\n\n  const handlePlaybackEnd = () => {\n    setIsPlaying(false);\n    onPlaybackEnd?.();\n  };\n\n  return (\n    <div className=\"isdk-question-audio\">\n      {audioUrl && (\n        // eslint-disable-next-line jsx-a11y/media-has-caption -- the question text is always shown visibly in QuestionCard; this audio is a redundant, optional channel\n        <audio\n          ref={setAudioEl}\n          src={audioUrl}\n          muted={muted}\n          onPlay={handlePlaybackStart}\n          onPause={handlePlaybackEnd}\n          onEnded={handlePlaybackEnd}\n        />\n      )}\n      {showLevelMeter && (\n        <span\n          className={\n            isPlaying\n              ? 'isdk-question-audio__orb isdk-question-audio__orb--active'\n              : 'isdk-question-audio__orb'\n          }\n        >\n          {avatarLabel && !isPlaying ? (\n            <span className=\"isdk-question-audio__orb-label\" aria-hidden=\"true\">\n              {avatarLabel}\n            </span>\n          ) : (\n            <AudioLevelMeter levels={levels} variant=\"speaking\" isIdle={!isSupported || !isPlaying} />\n          )}\n        </span>\n      )}\n      <button\n        className=\"isdk-btn isdk-btn--secondary isdk-btn--audio\"\n        type=\"button\"\n        onClick={play}\n        disabled={status === 'loading'}\n      >\n        {status === 'loading'\n          ? 'Loading audio…'\n          : hasPlayedOnce\n            ? 'Replay question'\n            : 'Play question'}\n      </button>\n    </div>\n  );\n}\n","import type { SynthesisResult } from '@interview-sdk/core';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { AudioLevelMeter } from './AudioLevelMeter.js';\nimport { MicButton, type AudioRecorder } from './MicButton.js';\nimport { QuestionAudio } from './QuestionAudio.js';\n\nexport interface QuestionCardProps {\n  prompt: string;\n  questionNumber: number;\n  totalQuestions: number;\n  isFollowUp?: boolean;\n  /** A short topic tag shown on the question panel and stage (e.g. the current question's first concept). Omit for no tag. */\n  topic?: string;\n  /** The candidate's own display name — used for the candidate tile's label and avatar initial. Defaults to \"You\". */\n  candidateName?: string;\n  onSubmit: (text: string) => void;\n  onSkip?: () => void;\n  onRequestHint?: () => void;\n  hint?: string;\n  isSubmitting?: boolean;\n  disabled?: boolean;\n  /**\n   * Enables the mic button, appending each transcript into the answer text.\n   * Omit for text-only mode (the accessible default — a text input is\n   * always present regardless of this prop).\n   */\n  transcribe?: (audio: Blob) => Promise<string>;\n  /**\n   * Enables speaking the prompt aloud (autoplayed, with a manual\n   * play/replay fallback) — typically a VoiceProviderAdapter's\n   * synthesize(). Omit for a silent, text-only prompt (the accessible\n   * default — the prompt is always shown as text regardless of this prop).\n   */\n  synthesize?: (text: string) => Promise<SynthesisResult>;\n  onVoiceError?: (error: Error) => void;\n  /** Forwarded to MicButton; injectable for testing or a custom capture backend. */\n  createRecorder?: () => Promise<AudioRecorder>;\n  /** Notified whenever recording starts/stops — informational only, e.g. for a caller's own REC indicator. Not fired for the initial mount. */\n  onRecordingChange?: (isRecording: boolean) => void;\n  /** Live elapsed-time label (e.g. \"3:45\"). Omit to hide the timer entirely. */\n  elapsedLabel?: string;\n  /** Fraction (0-1) of the session's time budget used so far — renders a thin progress bar next to the timer. Omit when there's no fixed time budget. */\n  elapsedFraction?: number;\n  /** Pauses the session. Omit to hide the Pause control. Available in every mode, voice or text-only. */\n  onPause?: () => void;\n  pauseDisabled?: boolean;\n  /** Notified with the character length of whatever was just pasted into the answer field — e.g. to feed InterviewWidget's integrity-signal tracking. Omit to not track pastes at all. */\n  onAnswerPaste?: (pastedLength: number) => void;\n}\n\ntype VoiceTurn = 'ai_speaking' | 'candidate_turn';\n\n/**\n * A fresh prompt (new question or follow-up) always starts a new\n * conversation turn — rather than resetting turn-state/answer-text via an\n * effect, the body below remounts fresh per prompt via `key`, the same way\n * it already remounts QuestionAudio/MicButton internally. No reset logic\n * needed anywhere.\n *\n * The live announcement of that change lives here, in this non-remounting\n * wrapper, rather than as aria-live on the visible heading inside the\n * remounted body: a brand-new DOM node arriving with aria-live already on\n * it is generally not announced by assistive tech — only text *changing*\n * inside an already-present live region is. This element's text changing\n * on every prompt update is what actually reaches a screen reader.\n *\n * Focus itself is handled separately, inside the remounting QuestionCardBody\n * below: every remount destroys whatever element previously held focus\n * (e.g. the answer field), and without an explicit refocus the browser\n * drops focus to <body> — forcing a keyboard-only candidate to Tab from the\n * very top of the page after every single answer. Moving focus to the new\n * heading each time (the standard multi-step-form pattern) fixes that.\n */\nexport function QuestionCard(props: QuestionCardProps) {\n  const { prompt, questionNumber, totalQuestions, isFollowUp } = props;\n  const announcement = isFollowUp\n    ? `Follow-up: ${prompt}`\n    : `Question ${questionNumber} of ${totalQuestions}: ${prompt}`;\n\n  return (\n    <>\n      <p aria-live=\"polite\" className=\"isdk-visually-hidden\">\n        {announcement}\n      </p>\n      <QuestionCardBody key={prompt} {...props} />\n    </>\n  );\n}\n\nfunction initialOf(name: string): string {\n  return name.trim().charAt(0).toUpperCase() || '?';\n}\n\nfunction QuestionCardBody({\n  prompt,\n  questionNumber,\n  totalQuestions,\n  isFollowUp = false,\n  topic,\n  candidateName = 'You',\n  onSubmit,\n  onSkip,\n  onRequestHint,\n  hint,\n  isSubmitting = false,\n  disabled = false,\n  transcribe,\n  synthesize,\n  onVoiceError,\n  createRecorder,\n  onRecordingChange,\n  elapsedLabel,\n  elapsedFraction,\n  onPause,\n  pauseDisabled = false,\n  onAnswerPaste,\n}: QuestionCardProps) {\n  const [answerText, setAnswerText] = useState('');\n  const [voiceTurn, setVoiceTurn] = useState<VoiceTurn>(() => (synthesize ? 'ai_speaking' : 'candidate_turn'));\n  const [isRecording, setIsRecording] = useState(false);\n  const [candidateLevels, setCandidateLevels] = useState<number[]>([]);\n  const [candidateMeterSupported, setCandidateMeterSupported] = useState(false);\n  const [speakerMuted, setSpeakerMuted] = useState(false);\n  const textareaId = useId();\n  const promptId = useId();\n  const isFirstRecordingRender = useRef(true);\n  const headingRef = useRef<HTMLHeadingElement>(null);\n\n  useEffect(() => {\n    if (isFirstRecordingRender.current) {\n      isFirstRecordingRender.current = false;\n      return;\n    }\n    onRecordingChange?.(isRecording);\n    // eslint-disable-next-line react-hooks/exhaustive-deps -- notify on isRecording transitions only, skipping the initial mount\n  }, [isRecording]);\n\n  // This whole component remounts fresh per prompt (see QuestionCard above),\n  // so a plain mount-only effect fires again on every new question/follow-up\n  // — landing focus on the new heading each time, rather than leaving it\n  // dropped on <body> once the previously-focused element is destroyed.\n  useEffect(() => {\n    headingRef.current?.focus();\n  }, []);\n\n  const isBusy = disabled || isSubmitting;\n  const hasVoice = Boolean(synthesize || transcribe);\n  const isAiSpeaking = voiceTurn === 'ai_speaking';\n  // A transcript only lands in the answer box once recording actually\n  // stops, so the box reads empty the whole time recording is in progress —\n  // without this, \"Submit answer\" stayed clickable through that window and\n  // would silently submit a blank answer if clicked mid-recording.\n  const canSubmit = !isBusy && !isRecording && answerText.trim().length > 0;\n\n  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    if (!canSubmit) return;\n    onSubmit(answerText);\n    setAnswerText('');\n  };\n\n  const handleTranscript = (text: string) => {\n    setAnswerText((current) => (current ? `${current} ${text}` : text));\n  };\n\n  // A voice-channel failure (synthesis error, mic denial, transcription\n  // failure) must never leave the candidate locked out of answering — always\n  // release the turn back to them alongside reporting the error.\n  const handleVoiceError = (error: Error) => {\n    setVoiceTurn('candidate_turn');\n    onVoiceError?.(error);\n  };\n\n  const turnLabel = isRecording\n    ? '● Recording'\n    : voiceTurn === 'ai_speaking'\n      ? 'AI is asking…'\n      : 'Your turn';\n  const turnModifier = isRecording ? 'recording' : voiceTurn === 'ai_speaking' ? 'ai-speaking' : 'candidate-turn';\n\n  const meta = (\n    <p className=\"isdk-question-card__meta\">\n      Question {questionNumber} of {totalQuestions}\n      {isFollowUp ? ' — follow-up' : ''}\n      {topic ? ` — ${topic}` : ''}\n    </p>\n  );\n\n  return (\n    <section className=\"isdk-question-card\" aria-labelledby={promptId}>\n      {hasVoice ? (\n        <div className=\"isdk-question-card__topbar isdk-question-card__topbar--overlay\">\n          {meta}\n          <span className={`isdk-question-card__turn-pill isdk-question-card__turn-pill--${turnModifier}`}>\n            {turnLabel}\n          </span>\n        </div>\n      ) : (\n        meta\n      )}\n\n      {hasVoice && (\n        <div className=\"isdk-question-card__channel\">\n          <div\n            className={\n              isAiSpeaking\n                ? 'isdk-question-card__party isdk-question-card__party--ai isdk-question-card__party--active'\n                : 'isdk-question-card__party isdk-question-card__party--ai'\n            }\n          >\n            <span className=\"isdk-question-card__party-avatar\">\n              {synthesize ? (\n                <QuestionAudio\n                  text={prompt}\n                  synthesize={synthesize}\n                  muted={speakerMuted}\n                  onError={handleVoiceError}\n                  onPlaybackStart={() => setVoiceTurn('ai_speaking')}\n                  onPlaybackEnd={() => setVoiceTurn('candidate_turn')}\n                  avatarLabel=\"AI\"\n                />\n              ) : (\n                <span className=\"isdk-question-audio__orb\" aria-hidden=\"true\">\n                  <span className=\"isdk-question-audio__orb-label\">AI</span>\n                </span>\n              )}\n            </span>\n            <span className=\"isdk-question-card__party-meta\">\n              <span className=\"isdk-question-card__party-name\">AI Interviewer</span>\n              <span className=\"isdk-question-card__party-status\">\n                {isAiSpeaking ? 'Speaking' : 'Listening'}\n              </span>\n            </span>\n          </div>\n\n          <span className=\"isdk-question-card__channel-link\" aria-hidden=\"true\">\n            <span className=\"isdk-question-card__channel-link-dot\" />\n            <span className=\"isdk-question-card__channel-link-dot\" />\n            <span className=\"isdk-question-card__channel-link-dot\" />\n          </span>\n\n          <div\n            className={\n              isRecording\n                ? 'isdk-question-card__party isdk-question-card__party--candidate isdk-question-card__party--active'\n                : 'isdk-question-card__party isdk-question-card__party--candidate'\n            }\n          >\n            <span className=\"isdk-question-card__party-meta isdk-question-card__party-meta--right\">\n              <span className=\"isdk-question-card__party-name\">{candidateName}</span>\n              <span className=\"isdk-question-card__party-status\">\n                {isRecording ? 'Recording' : 'Muted'}\n              </span>\n            </span>\n            <span className=\"isdk-question-card__party-avatar\">\n              <span className=\"isdk-question-card__avatar-mark\" aria-hidden=\"true\">\n                {initialOf(candidateName)}\n              </span>\n              <AudioLevelMeter\n                levels={candidateLevels}\n                variant=\"listening\"\n                isIdle={!candidateMeterSupported}\n              />\n            </span>\n          </div>\n        </div>\n      )}\n\n      <div className=\"isdk-question-card__caption\">\n        <span className=\"isdk-question-card__index\" aria-hidden=\"true\">\n          {String(questionNumber).padStart(2, '0')}\n        </span>\n        {/* Not a live region — this whole subtree remounts per prompt (see\n            QuestionCard above), so aria-live here would never fire. The\n            visually-hidden announcer in the non-remounting wrapper covers it. */}\n        <h2 className=\"isdk-question-card__prompt\" id={promptId} ref={headingRef} tabIndex={-1}>\n          {prompt}\n        </h2>\n      </div>\n\n      {hint && (\n        <p className=\"isdk-question-card__hint\" role=\"note\" aria-label=\"Hint\">\n          {hint}\n        </p>\n      )}\n\n      <form className=\"isdk-question-card__form\" onSubmit={handleSubmit}>\n        <div\n          className={\n            hasVoice\n              ? 'isdk-question-card__composer isdk-question-card__composer--secondary'\n              : 'isdk-question-card__composer'\n          }\n        >\n          <label className=\"isdk-question-card__label\" htmlFor={textareaId}>\n            Your answer\n          </label>\n          <textarea\n            className={\n              hasVoice\n                ? 'isdk-question-card__textarea isdk-question-card__textarea--secondary'\n                : 'isdk-question-card__textarea'\n            }\n            id={textareaId}\n            value={answerText}\n            onChange={(event) => setAnswerText(event.target.value)}\n            onPaste={\n              onAnswerPaste\n                ? (event) => onAnswerPaste(event.clipboardData.getData('text').length)\n                : undefined\n            }\n            disabled={isBusy}\n            rows={6}\n          />\n        </div>\n\n        {(elapsedLabel || onPause) && (\n          <div className=\"isdk-question-card__session-row\">\n            {elapsedLabel && (\n              <div className=\"isdk-question-card__timer\">\n                <span className=\"isdk-question-card__timer-label\">Session</span>\n                <span className=\"isdk-tabular isdk-question-card__timer-value\">{elapsedLabel}</span>\n                {typeof elapsedFraction === 'number' && (\n                  <span className=\"isdk-question-card__timer-bar\" aria-hidden=\"true\">\n                    <span\n                      style={{ width: `${Math.min(100, Math.max(0, elapsedFraction * 100))}%` }}\n                    />\n                  </span>\n                )}\n              </div>\n            )}\n            {onPause && (\n              <button\n                type=\"button\"\n                className=\"isdk-btn isdk-btn--secondary isdk-question-card__pause\"\n                onClick={onPause}\n                disabled={pauseDisabled}\n              >\n                Pause\n              </button>\n            )}\n          </div>\n        )}\n\n        {hasVoice ? (\n          <div className=\"isdk-question-card__toolbar\">\n            <div className=\"isdk-question-card__toolbar-zone isdk-question-card__toolbar-zone--left\">\n              {synthesize && (\n                <button\n                  type=\"button\"\n                  className=\"isdk-btn isdk-btn--secondary isdk-btn--toolbar\"\n                  onClick={() => setSpeakerMuted((current) => !current)}\n                  aria-pressed={speakerMuted}\n                >\n                  <svg className=\"isdk-btn__icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n                    <path d=\"M4 9v6h4l5 4V5L8 9H4z\" fill=\"currentColor\" />\n                    {!speakerMuted && (\n                      <path\n                        d=\"M16 9a4 4 0 0 1 0 6\"\n                        stroke=\"currentColor\"\n                        strokeWidth=\"1.6\"\n                        strokeLinecap=\"round\"\n                      />\n                    )}\n                    {speakerMuted && (\n                      <path\n                        d=\"M15 9l5 6M20 9l-5 6\"\n                        stroke=\"currentColor\"\n                        strokeWidth=\"1.6\"\n                        strokeLinecap=\"round\"\n                      />\n                    )}\n                  </svg>\n                  <span className=\"isdk-btn__label\">Speaker</span>\n                </button>\n              )}\n            </div>\n\n            <div className=\"isdk-question-card__toolbar-zone isdk-question-card__toolbar-zone--center\">\n              {transcribe && (\n                <MicButton\n                  transcribe={transcribe}\n                  onTranscript={handleTranscript}\n                  onError={handleVoiceError}\n                  disabled={isBusy || isAiSpeaking}\n                  emphasized={voiceTurn === 'candidate_turn' && !isRecording}\n                  onRecordingChange={setIsRecording}\n                  showLevelMeter={false}\n                  onLevelsChange={(levels, supported) => {\n                    setCandidateLevels(levels);\n                    setCandidateMeterSupported(supported);\n                  }}\n                  {...(createRecorder ? { createRecorder } : {})}\n                />\n              )}\n            </div>\n\n            <div className=\"isdk-question-card__toolbar-zone isdk-question-card__toolbar-zone--right\">\n              {onRequestHint && (\n                <button\n                  className=\"isdk-btn isdk-btn--secondary isdk-btn--toolbar\"\n                  type=\"button\"\n                  onClick={onRequestHint}\n                  disabled={isBusy}\n                >\n                  <svg className=\"isdk-btn__icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n                    <path\n                      d=\"M9 18h6M10 21h4M7 9a5 5 0 1 1 8.5 3.5c-.7.7-1.2 1.5-1.4 2.5H9.9c-.2-1-.7-1.8-1.4-2.5A5 5 0 0 1 7 9Z\"\n                      stroke=\"currentColor\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                  <span className=\"isdk-btn__label\">Hints</span>\n                </button>\n              )}\n\n              {onSkip && (\n                <button\n                  className=\"isdk-btn isdk-btn--secondary isdk-btn--toolbar\"\n                  type=\"button\"\n                  onClick={onSkip}\n                  disabled={isBusy}\n                >\n                  <svg className=\"isdk-btn__icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n                    <path d=\"M5 6v12l8-6-8-6zM14 6v12l8-6-8-6z\" fill=\"currentColor\" />\n                  </svg>\n                  <span className=\"isdk-btn__label\">Skip</span>\n                </button>\n              )}\n\n              <button className=\"isdk-btn isdk-btn--primary\" type=\"submit\" disabled={!canSubmit}>\n                Submit answer\n              </button>\n            </div>\n          </div>\n        ) : (\n          <div className=\"isdk-question-card__actions\">\n            <button className=\"isdk-btn isdk-btn--primary\" type=\"submit\" disabled={!canSubmit}>\n              Submit answer\n            </button>\n            {onRequestHint && (\n              <button\n                className=\"isdk-btn isdk-btn--secondary\"\n                type=\"button\"\n                onClick={onRequestHint}\n                disabled={isBusy}\n              >\n                Request a hint\n              </button>\n            )}\n            {onSkip && (\n              <button\n                className=\"isdk-btn isdk-btn--secondary\"\n                type=\"button\"\n                onClick={onSkip}\n                disabled={isBusy}\n              >\n                Skip question\n              </button>\n            )}\n          </div>\n        )}\n      </form>\n    </section>\n  );\n}\n","import { useEffect, useRef } from 'react';\nimport type { TranscriptEntry } from '../hooks/build-report.js';\n\nexport interface TranscriptChatProps {\n  transcript: TranscriptEntry[];\n  /** Label shown above the AI's messages. Defaults to 'AI Interviewer'. */\n  assistantName?: string;\n  /** Label shown above the candidate's messages. Defaults to 'You'. */\n  candidateName?: string;\n}\n\nfunction answerText(entry: TranscriptEntry): string {\n  if (entry.answer.isSkipped) return '(Skipped)';\n  if (entry.answer.isSilence) return '(No response)';\n  return entry.answer.text;\n}\n\n/**\n * The running conversation as chat bubbles — every AI prompt and every\n * candidate answer, in order, plus the AI's own rationale (a real field\n * from EvaluationResult, not fabricated copy) when one was returned for\n * that answer. This is also the accessibility \"captions\" requirement\n * TranscriptViewer documents: nothing here is audio-only, so this list\n * keeps the same `role=\"log\"` live-region contract.\n */\nexport function TranscriptChat({\n  transcript,\n  assistantName = 'AI Interviewer',\n  candidateName = 'You',\n}: TranscriptChatProps) {\n  const bottomRef = useRef<HTMLLIElement>(null);\n\n  useEffect(() => {\n    bottomRef.current?.scrollIntoView?.({ block: 'end' });\n  }, [transcript.length]);\n\n  if (transcript.length === 0) {\n    return (\n      <div className=\"isdk-transcript-chat-wrap\">\n        <p className=\"isdk-kicker\">Transcript</p>\n        <p className=\"isdk-transcript-chat__empty\">Nothing recorded yet — this fills in as you answer.</p>\n      </div>\n    );\n  }\n\n  // This is a scrollable region taller than its viewport with no focusable\n  // descendants — without tabIndex, a keyboard-only user has no way to\n  // scroll it once the conversation grows past the visible height.\n  return (\n    <div className=\"isdk-transcript-chat-wrap\">\n      <p className=\"isdk-kicker\">Transcript</p>\n      <ol\n        className=\"isdk-transcript-chat\"\n        aria-label=\"Interview transcript\"\n        role=\"log\"\n        aria-live=\"polite\"\n        // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n        tabIndex={0}\n      >\n        {transcript.map((entry, index) => (\n          <li className=\"isdk-transcript-chat__group\" key={`${entry.question.id}-${index}`}>\n            <div className=\"isdk-transcript-chat__bubble isdk-transcript-chat__bubble--ai\">\n              <p className=\"isdk-transcript-chat__from\">{assistantName}</p>\n              <p className=\"isdk-transcript-chat__text\">{entry.prompt}</p>\n            </div>\n            <div className=\"isdk-transcript-chat__bubble isdk-transcript-chat__bubble--you\">\n              <p className=\"isdk-transcript-chat__from\">{candidateName}</p>\n              <p className=\"isdk-transcript-chat__text\">{answerText(entry)}</p>\n            </div>\n            {entry.evaluation.rationale && (\n              <div\n                className=\"isdk-transcript-chat__bubble isdk-transcript-chat__bubble--note\"\n                role=\"status\"\n              >\n                <p className=\"isdk-transcript-chat__from isdk-transcript-chat__from--note\">\n                  <span className=\"isdk-transcript-chat__note-icon\" aria-hidden=\"true\" />\n                  Feedback\n                </p>\n                <p className=\"isdk-transcript-chat__text\">{entry.evaluation.rationale}</p>\n              </div>\n            )}\n          </li>\n        ))}\n        <li ref={bottomRef} aria-hidden=\"true\" />\n      </ol>\n    </div>\n  );\n}\n","export type ScoreTier = 'pass' | 'flag' | 'neutral';\n\n// Same cutoffs build-report.ts uses to classify strengths (>=75) and\n// weaknesses (<=40), so a score reads the same color whether it's on the\n// transcript, the score table, or the strengths/weaknesses list.\nconst PASS_THRESHOLD = 75;\nconst FLAG_THRESHOLD = 40;\n\nexport function scoreTier(score: number): ScoreTier {\n  if (score >= PASS_THRESHOLD) return 'pass';\n  if (score <= FLAG_THRESHOLD) return 'flag';\n  return 'neutral';\n}\n","import type { CSSProperties } from 'react';\nimport type { TranscriptEntry } from '../hooks/build-report.js';\nimport { scoreTier } from './score-tier.js';\n\nexport interface TranscriptViewerProps {\n  transcript: TranscriptEntry[];\n}\n\nfunction answerText(entry: TranscriptEntry): string {\n  if (entry.answer.isSkipped) return '(Skipped)';\n  if (entry.answer.isSilence) return '(No response)';\n  return entry.answer.text;\n}\n\n/**\n * Renders the full text of every prompt and answer. This is also the\n * accessibility \"captions\" requirement: nothing in this SDK is presented as\n * audio-only — whatever a voice layer speaks or transcribes always has a\n * visible text equivalent here.\n */\nexport function TranscriptViewer({ transcript }: TranscriptViewerProps) {\n  if (transcript.length === 0) {\n    return <p className=\"isdk-transcript__empty\">No answers yet.</p>;\n  }\n\n  // A follow-up shares its parent question's slot rather than claiming the\n  // next number itself — counting the raw array index instead would number\n  // every question AFTER the first follow-up one too high (a transcript's\n  // length grows with follow-ups, but there's still the same number of\n  // actual questions).\n  const questionNumbers = transcript.reduce<number[]>((numbers, entry) => {\n    const previous = numbers.at(-1) ?? 0;\n    numbers.push(entry.isFollowUp ? previous : previous + 1);\n    return numbers;\n  }, []);\n\n  return (\n    <ol className=\"isdk-transcript\" aria-label=\"Interview transcript\" role=\"log\" aria-live=\"polite\">\n      {transcript.map((entry, index) => (\n        <li\n          className={\n            entry.isFollowUp\n              ? 'isdk-transcript__entry isdk-transcript__entry--followup'\n              : 'isdk-transcript__entry'\n          }\n          key={`${entry.question.id}-${index}`}\n          style={{ '--isdk-d': index } as CSSProperties}\n        >\n          <div className=\"isdk-transcript__entry-head\">\n            <strong className=\"isdk-kicker\">\n              {entry.isFollowUp ? 'Follow-up' : `Q${questionNumbers[index]}`}\n            </strong>\n            <span\n              className={`isdk-transcript__score isdk-chip isdk-chip--${scoreTier(entry.evaluation.totalScore)} isdk-tabular`}\n            >\n              Score: {entry.evaluation.totalScore}/100\n            </span>\n          </div>\n          <p className=\"isdk-transcript__prompt\">{entry.prompt}</p>\n          <p className=\"isdk-transcript__answer\">{answerText(entry)}</p>\n        </li>\n      ))}\n    </ol>\n  );\n}\n","import { useId } from 'react';\nimport type { Rubric } from '@interview-sdk/core';\nimport { scoreTier } from './score-tier.js';\n\nexport interface ScoreSummaryProps {\n  totalScore: number;\n  rubric: Rubric;\n  dimensionAverages: Record<string, number>;\n}\n\nexport function ScoreSummary({ totalScore, rubric, dimensionAverages }: ScoreSummaryProps) {\n  const headingId = useId();\n\n  return (\n    <section className=\"isdk-score-summary\" aria-labelledby={headingId}>\n      {/* h3: this section always nests inside ReportCard's own \"Interview\n          Report\" h2, as a sibling of its \"Strengths\"/\"Areas for\n          improvement\" h3 sections — not a peer of the report's own title. */}\n      <h3 className=\"isdk-score-summary__title\" id={headingId}>\n        Score Summary\n      </h3>\n      <p className=\"isdk-score-summary__total\">\n        <span className=\"isdk-score-summary__total-label\">Overall score</span>\n        <strong\n          className={`isdk-chip isdk-chip--${scoreTier(totalScore)} isdk-tabular isdk-score-summary__total-value`}\n        >\n          {totalScore}/100\n        </strong>\n      </p>\n      <table className=\"isdk-score-summary__table\">\n        <caption>Score breakdown by rubric dimension</caption>\n        <thead>\n          <tr>\n            <th scope=\"col\">Dimension</th>\n            <th scope=\"col\">Score</th>\n          </tr>\n        </thead>\n        <tbody>\n          {rubric.dimensions.map((dimension) => {\n            // Absent means no question assessed this dimension at all (see\n            // Question.dimensions) — omitted here rather than shown as a\n            // false, demoralizing 0/100 for something never actually asked.\n            const rawScore = dimensionAverages[dimension.id];\n            if (rawScore === undefined) return null;\n            const score = Math.round(rawScore * 100) / 100;\n            return (\n              <tr key={dimension.id}>\n                <th scope=\"row\">{dimension.label}</th>\n                <td>\n                  <span className={`isdk-chip isdk-chip--${scoreTier(score)} isdk-tabular`}>\n                    {score}/100\n                  </span>\n                </td>\n              </tr>\n            );\n          })}\n        </tbody>\n      </table>\n    </section>\n  );\n}\n","import type { TranscriptEntry } from '../hooks/build-report.js';\n\n// A field starting with =, +, -, or @ is interpreted as a formula by\n// Excel/Sheets when the CSV is opened — a candidate's own answer text is\n// untrusted input, so this is real CSV/formula-injection risk (OWASP),\n// not just delimiter-escaping. Prefixing with a leading apostrophe forces\n// spreadsheet apps to treat it as literal text.\nconst FORMULA_TRIGGER = /^[=+\\-@]/;\n\nfunction escapeCsvField(value: string): string {\n  const safeValue = FORMULA_TRIGGER.test(value) ? `'${value}` : value;\n  if (/[\",\\n]/.test(safeValue)) {\n    return `\"${safeValue.replace(/\"/g, '\"\"')}\"`;\n  }\n  return safeValue;\n}\n\nexport function transcriptToCsv(transcript: TranscriptEntry[]): string {\n  const header = ['question', 'prompt', 'isFollowUp', 'answer', 'score'];\n  const rows = transcript.map((entry) => [\n    entry.question.id,\n    entry.prompt,\n    String(entry.isFollowUp),\n    entry.answer.text,\n    String(entry.evaluation.totalScore),\n  ]);\n  return [header, ...rows].map((row) => row.map(escapeCsvField).join(',')).join('\\n');\n}\n\nexport function downloadBlob(blob: Blob, filename: string): void {\n  const url = URL.createObjectURL(blob);\n  const anchor = document.createElement('a');\n  anchor.href = url;\n  anchor.download = filename;\n  document.body.appendChild(anchor);\n  anchor.click();\n  document.body.removeChild(anchor);\n  URL.revokeObjectURL(url);\n}\n\n/** Same download idiom as {@link downloadBlob}, for a data: URL that's already encoded — no object URL to revoke. */\nexport function downloadDataUrl(dataUrl: string, filename: string): void {\n  const anchor = document.createElement('a');\n  anchor.href = dataUrl;\n  anchor.download = filename;\n  document.body.appendChild(anchor);\n  anchor.click();\n  document.body.removeChild(anchor);\n}\n","export interface HtmlToImageOptions {\n  backgroundColor?: string;\n  pixelRatio?: number;\n  filter?: (domNode: HTMLElement) => boolean;\n}\n\nexport interface HtmlToImageModule {\n  toPng<T extends HTMLElement>(node: T, options?: HtmlToImageOptions): Promise<string>;\n}\n\n/**\n * A literal `import('html-to-image')` — not hidden behind a `new\n * Function(...)`-constructed specifier. That trick (used here previously)\n * makes bundlers skip the import entirely at build time, but a bare\n * specifier built that way can never resolve at runtime either: per the\n * ECMAScript/WHATWG module spec, native `import()` of a bare specifier\n * only resolves via an import map or a bundler's own static rewrite —\n * hiding the call from every bundler (verified against real Vite dev and\n * production builds) means no bundler ever rewrites it, so it fails with\n * \"Failed to resolve module specifier\" in every real browser, always,\n * even when the consumer has installed the package. A literal import()\n * is what Vite/webpack/Rollup can actually see and correctly resolve (to\n * the real module when installed, or a normal build error when it truly\n * isn't) — this package's own build (tsup) is told to treat\n * `html-to-image` as `external` (see tsup.config.ts) so it never needs to\n * be installed just to build `@interview-sdk/react` itself.\n */\nexport async function loadHtmlToImage(): Promise<HtmlToImageModule> {\n  return (await import('html-to-image')) as unknown as HtmlToImageModule;\n}\n","import type { Rubric } from '@interview-sdk/core';\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport type { InterviewReport } from '../hooks/build-report.js';\nimport { ScoreSummary } from './ScoreSummary.js';\nimport { TranscriptViewer } from './TranscriptViewer.js';\nimport { downloadBlob, downloadDataUrl, transcriptToCsv } from './report-export.js';\nimport { loadHtmlToImage } from './optional-image-export.js';\n\nexport interface ReportCardProps {\n  report: InterviewReport;\n  rubric: Rubric;\n  /** Called when image or CSV generation fails and the report falls back to a JSON download. */\n  onExportError?: (error: Error, format: 'image' | 'csv') => void;\n}\n\nconst FALLBACK_MESSAGE: Record<'image' | 'csv', string> = {\n  image: \"Image export isn't available here — downloaded a JSON file instead.\",\n  csv: \"CSV export failed — downloaded a JSON file instead.\",\n};\n\n/**\n * Excludes the action buttons and any export notice from the captured\n * image — only the report content itself. html-to-image's real filter\n * callback (verified against its source) runs on every child node during\n * cloning, including raw Text nodes with no `.classList` — despite its\n * typed signature claiming `HTMLElement` — so non-Element nodes must be\n * passed through rather than assumed to have classList.\n */\nexport function isExportable(domNode: HTMLElement): boolean {\n  if (!(domNode instanceof Element)) return true;\n  return !domNode.classList.contains('isdk-report-card__actions')\n    && !domNode.classList.contains('isdk-report-card__export-notice');\n}\n\nexport function ReportCard({ report, rubric, onExportError }: ReportCardProps) {\n  const headingId = useId();\n  const headingRef = useRef<HTMLHeadingElement>(null);\n  const cardRef = useRef<HTMLElement>(null);\n  // This component mounts fresh exactly once, the moment the interview\n  // completes — moving focus here (rather than leaving it on whatever\n  // button was last clicked, or dropped to <body>) is what actually tells a\n  // screen-reader or keyboard user the report has arrived.\n  useEffect(() => {\n    headingRef.current?.focus();\n  }, []);\n  // Surfaces the same fallback the catch blocks below already handle\n  // silently at the data layer — so a candidate who clicks \"Export Image\"\n  // and gets a JSON file actually finds out why, instead of a mislabeled\n  // download with no explanation.\n  const [fallback, setFallback] = useState<'image' | 'csv' | null>(null);\n\n  const exportJson = useCallback(() => {\n    downloadBlob(\n      new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' }),\n      `interview-report-${report.sessionId}.json`,\n    );\n  }, [report]);\n\n  const exportCsv = useCallback(() => {\n    setFallback(null);\n    try {\n      const csv = transcriptToCsv(report.transcript);\n      downloadBlob(\n        new Blob([csv], { type: 'text/csv' }),\n        `interview-report-${report.sessionId}.csv`,\n      );\n    } catch (error) {\n      onExportError?.(error instanceof Error ? error : new Error(String(error)), 'csv');\n      setFallback('csv');\n      exportJson();\n    }\n  }, [report, onExportError, exportJson]);\n\n  const exportImage = useCallback(async () => {\n    setFallback(null);\n    try {\n      if (!cardRef.current) throw new Error('Report card is not mounted.');\n      const htmlToImage = await loadHtmlToImage();\n      const dataUrl = await htmlToImage.toPng(cardRef.current, {\n        pixelRatio: 2,\n        filter: isExportable,\n      });\n      downloadDataUrl(dataUrl, `interview-report-${report.sessionId}.png`);\n    } catch (error) {\n      onExportError?.(error instanceof Error ? error : new Error(String(error)), 'image');\n      setFallback('image');\n      exportJson();\n    }\n  }, [report, onExportError, exportJson]);\n\n  return (\n    <article className=\"isdk-report-card\" aria-labelledby={headingId} ref={cardRef}>\n      <div className=\"isdk-report-card__head\">\n        <h2 className=\"isdk-report-card__title\" id={headingId} ref={headingRef} tabIndex={-1}>\n          Interview Report\n        </h2>\n        <span className=\"isdk-stamp\">\n          <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n            <path\n              d=\"M3 8.5L6.2 11.5L13 4.5\"\n              stroke=\"currentColor\"\n              strokeWidth=\"1.8\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n            />\n          </svg>\n          Complete\n        </span>\n      </div>\n\n      <ScoreSummary\n        totalScore={report.totalScore}\n        rubric={rubric}\n        dimensionAverages={report.dimensionAverages}\n      />\n\n      <section className=\"isdk-report-card__section\">\n        <h3 className=\"isdk-report-card__section-title\">Strengths</h3>\n        {report.strengths.length > 0 ? (\n          <ul>\n            {report.strengths.map((strength) => (\n              <li key={strength}>{strength}</li>\n            ))}\n          </ul>\n        ) : (\n          <p>None identified.</p>\n        )}\n      </section>\n\n      <section className=\"isdk-report-card__section\">\n        <h3 className=\"isdk-report-card__section-title\">Areas for improvement</h3>\n        {report.weaknesses.length > 0 ? (\n          <ul>\n            {report.weaknesses.map((weakness) => (\n              <li key={weakness}>{weakness}</li>\n            ))}\n          </ul>\n        ) : (\n          <p>None identified.</p>\n        )}\n      </section>\n\n      {report.missedConcepts.length > 0 && (\n        <section className=\"isdk-report-card__section\">\n          <h3 className=\"isdk-report-card__section-title\">Recommended review topics</h3>\n          <ul>\n            {report.missedConcepts.map((concept) => (\n              <li key={concept}>{concept}</li>\n            ))}\n          </ul>\n        </section>\n      )}\n\n      {report.integritySignals && (\n        <section className=\"isdk-report-card__section\">\n          <h3 className=\"isdk-report-card__section-title\">Session integrity</h3>\n          <p className=\"isdk-report-card__integrity-note\">\n            Tab switches: {report.integritySignals.tabSwitchCount} · Pastes into an answer:{' '}\n            {report.integritySignals.pasteEvents.length}. Informational only — weigh these in\n            context, not as an automated verdict.\n          </p>\n        </section>\n      )}\n\n      <TranscriptViewer transcript={report.transcript} />\n\n      {fallback && (\n        <p className=\"isdk-report-card__export-notice\" role=\"status\">\n          {FALLBACK_MESSAGE[fallback]}\n        </p>\n      )}\n\n      <div className=\"isdk-report-card__actions\">\n        <button\n          className=\"isdk-btn isdk-btn--secondary\"\n          type=\"button\"\n          onClick={() => {\n            setFallback(null);\n            exportJson();\n          }}\n        >\n          Export JSON\n        </button>\n        <button className=\"isdk-btn isdk-btn--secondary\" type=\"button\" onClick={exportCsv}>\n          Export CSV\n        </button>\n        <button\n          className=\"isdk-btn isdk-btn--secondary\"\n          type=\"button\"\n          onClick={() => void exportImage()}\n        >\n          Export Image\n        </button>\n      </div>\n    </article>\n  );\n}\n","import { useCallback, useEffect, useMemo, useRef } from 'react';\nimport type { IntegritySignals } from './build-report.js';\n\nexport interface UseIntegritySignalsResult {\n  /** Call whenever the interview's own active/inactive state changes (e.g. in_progress vs. paused/not_started) — tab switches and pastes are only counted while active. */\n  setActive: (active: boolean) => void;\n  /** Call from your answer input's onPaste with the pasted text's length. */\n  recordPaste: (pastedLength: number) => void;\n  /** A point-in-time copy of everything recorded so far — safe to call repeatedly (e.g. when building a report). */\n  getSnapshot: () => IntegritySignals;\n}\n\n/**\n * Tracks two low-risk, non-biometric integrity signals: how many times the\n * candidate's tab lost visibility, and how many times they pasted into an\n * answer. `enabled` gates whether the underlying `visibilitychange` listener\n * is attached at all — pass the developer's own opt-in flag (e.g.\n * InterviewWidget's `trackIntegritySignals` prop), not a constant true, so a\n * candidate isn't tracked unless the integrating developer explicitly chose\n * to.\n */\nexport function useIntegritySignals(enabled: boolean): UseIntegritySignalsResult {\n  const activeRef = useRef(false);\n  const tabSwitchCountRef = useRef(0);\n  const tabSwitchTimestampsRef = useRef<number[]>([]);\n  const pasteEventsRef = useRef<Array<{ length: number; timestamp: number }>>([]);\n\n  useEffect(() => {\n    if (!enabled || typeof document === 'undefined') return;\n    const handleVisibilityChange = () => {\n      if (!activeRef.current || !document.hidden) return;\n      tabSwitchCountRef.current += 1;\n      tabSwitchTimestampsRef.current = [...tabSwitchTimestampsRef.current, Date.now()];\n    };\n    document.addEventListener('visibilitychange', handleVisibilityChange);\n    return () => document.removeEventListener('visibilitychange', handleVisibilityChange);\n  }, [enabled]);\n\n  const setActive = useCallback((active: boolean) => {\n    activeRef.current = active;\n  }, []);\n\n  const recordPaste = useCallback((pastedLength: number) => {\n    if (!activeRef.current) return;\n    pasteEventsRef.current = [\n      ...pasteEventsRef.current,\n      { length: pastedLength, timestamp: Date.now() },\n    ];\n  }, []);\n\n  const getSnapshot = useCallback(\n    (): IntegritySignals => ({\n      tabSwitchCount: tabSwitchCountRef.current,\n      tabSwitchTimestamps: tabSwitchTimestampsRef.current,\n      pasteEvents: pasteEventsRef.current,\n    }),\n    [],\n  );\n\n  return useMemo(\n    () => ({ setActive, recordPaste, getSnapshot }),\n    [setActive, recordPaste, getSnapshot],\n  );\n}\n","import { Component, type ErrorInfo, type ReactNode } from 'react';\n\ninterface InterviewErrorBoundaryProps {\n  children: ReactNode;\n  shellClassName: string;\n  /**\n   * Whenever this changes while an error is showing, the boundary resets\n   * itself automatically — e.g. InterviewWidget derives it from\n   * questions/rubric, so a host app editing those live (a question\n   * builder) recovers on its own the moment the config becomes valid\n   * again, instead of staying stuck until something manually resets it.\n   */\n  resetKey: string;\n}\n\ninterface InterviewErrorBoundaryState {\n  error: Error | undefined;\n}\n\n/**\n * A real AI provider's response shape is outside this SDK's control — a\n * malformed reply, an unexpected schema change, or any other unforeseen\n * error should surface as a recoverable message, never blank the whole\n * page with no feedback. React only catches render-time errors at a class\n * component boundary like this one; there is no hook equivalent.\n *\n * Developer misconfiguration (missing adapter, invalid rubric, etc.) is\n * checked in InterviewWidget before this boundary ever renders its child,\n * so those still throw loudly and immediately instead of landing here.\n */\nexport class InterviewErrorBoundary extends Component<\n  InterviewErrorBoundaryProps,\n  InterviewErrorBoundaryState\n> {\n  override state: InterviewErrorBoundaryState = { error: undefined };\n\n  static getDerivedStateFromError(error: Error): InterviewErrorBoundaryState {\n    return { error };\n  }\n\n  override componentDidCatch(error: Error, info: ErrorInfo): void {\n    console.error('InterviewWidget crashed:', error, info.componentStack);\n  }\n\n  override componentDidUpdate(prevProps: InterviewErrorBoundaryProps): void {\n    if (this.state.error && prevProps.resetKey !== this.props.resetKey) {\n      this.reset();\n    }\n  }\n\n  private reset = (): void => {\n    this.setState({ error: undefined });\n  };\n\n  override render(): ReactNode {\n    const { error } = this.state;\n    if (!error) return this.props.children;\n\n    return (\n      <div className={`${this.props.shellClassName} isdk-widget--center`}>\n        <div className=\"isdk-widget__hero\">\n          <div className=\"isdk-lobby\">\n            <p className=\"isdk-kicker\">Something went wrong</p>\n            <h2 className=\"isdk-lobby__title\">The interview hit an unexpected error</h2>\n            <p className=\"isdk-lobby__meta\">{error.message}</p>\n            <div className=\"isdk-lobby__actions\">\n              <button className=\"isdk-btn isdk-btn--primary\" type=\"button\" onClick={this.reset}>\n                Try again\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n","import type {\n  AIProviderAdapter,\n  Question,\n  RubricDimensionInput,\n  SynthesisResult,\n} from '@interview-sdk/core';\nimport { defineRubric } from '@interview-sdk/core';\nimport { useEffect, useMemo, useState, type CSSProperties } from 'react';\nimport { useInterview, type InterviewSnapshot, type UseInterviewResult } from '../hooks/useInterview.js';\nimport type { InterviewReport } from '../hooks/build-report.js';\nimport { useIntegritySignals } from '../hooks/use-integrity-signals.js';\nimport { ClientModeProcessor } from '../processor/client-mode-processor.js';\nimport { ServerModeProcessor } from '../processor/server-mode-processor.js';\nimport { InterviewErrorBoundary } from './InterviewErrorBoundary.js';\nimport { InterviewLobby } from './InterviewLobby.js';\nimport { InterviewProgress } from './InterviewProgress.js';\nimport { LiveSignals } from './LiveSignals.js';\nimport { QuestionCard } from './QuestionCard.js';\nimport { ReportCard } from './ReportCard.js';\nimport { TranscriptChat } from './TranscriptChat.js';\n\n/**\n * Best-effort only — a full disk, private-browsing storage lockout, or\n * disabled localStorage must never break the interview itself, so every\n * failure here is swallowed and treated as \"nothing to resume from.\"\n */\nfunction readPersistedSnapshot(key: string | undefined): InterviewSnapshot | undefined {\n  if (!key || typeof window === 'undefined') return undefined;\n  try {\n    const raw = window.localStorage.getItem(key);\n    if (!raw) return undefined;\n    return JSON.parse(raw) as InterviewSnapshot;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction writePersistedSnapshot(key: string, snapshot: InterviewSnapshot): void {\n  try {\n    window.localStorage.setItem(key, JSON.stringify(snapshot));\n  } catch {\n    // Best-effort — see readPersistedSnapshot above.\n  }\n}\n\nfunction clearPersistedSnapshot(key: string): void {\n  try {\n    window.localStorage.removeItem(key);\n  } catch {\n    // Best-effort — see readPersistedSnapshot above.\n  }\n}\n\nexport type InterviewMode = 'client' | 'server';\n\nexport interface InterviewWidgetProps {\n  questions: Question[];\n  rubric: RubricDimensionInput[];\n  mode: InterviewMode;\n  /** Required in Client Mode: the AI provider adapter that runs directly in the browser. Prototyping only. */\n  adapter?: AIProviderAdapter;\n  /** Server Mode: URL of the developer's own answer-processing endpoint. Defaults to /api/interview/answer. */\n  apiBaseUrl?: string;\n  /** Server Mode: extra headers (e.g. an auth token) sent with every request. */\n  serverHeaders?: Record<string, string>;\n  maxFollowUpDepth?: number;\n  sessionTimeoutMs?: number;\n  onSessionEnd?: (report: InterviewReport) => void;\n  /** Enables the mic button; omit for text-only mode (the accessible default). */\n  transcribe?: (audio: Blob) => Promise<string>;\n  /** Speaks each question/follow-up aloud; omit for a silent, text-only prompt (the accessible default). */\n  synthesize?: (text: string) => Promise<SynthesisResult>;\n  onVoiceError?: (error: Error) => void;\n  /**\n   * Client Mode exposes AI keys in the browser and lets scores be computed\n   * client-side — it refuses to run under NODE_ENV=production unless this\n   * is explicitly set. Use Server Mode for production instead.\n   */\n  allowClientModeInProduction?: boolean;\n  /**\n   * Requests a live mic stream for the pre-join lobby's mic check — purely a\n   * preview, never a recording. Defaults to a real getUserMedia call.\n   * Injectable for testing.\n   */\n  requestMicStream?: () => Promise<MediaStream>;\n  /** Overrides the lobby's primary action text. Defaults to 'Start interview'. */\n  joinLabel?: string;\n  /** Optional session context shown in the header (e.g. \"Senior Software Engineer — Round 2\"). */\n  roleTitle?: string;\n  /** The candidate's own display name, shown on their tile. Defaults to \"You\". */\n  candidateName?: string;\n  /**\n   * Called when the final report's image or CSV export fails and falls\n   * back to a JSON download (e.g. the optional `html-to-image` peer\n   * dependency isn't installed) — the report itself already shows a\n   * visible notice when this happens; use this to also log it or notify\n   * your own backend.\n   */\n  onExportError?: (error: Error, format: 'image' | 'csv') => void;\n  /**\n   * When set, automatically saves the session to localStorage under this\n   * key after every state change, and resumes from it on mount — a page\n   * refresh or accidental tab close picks back up on the same question with\n   * the same transcript, no manual getSnapshot()/initialSnapshot wiring\n   * needed. Cleared automatically once the interview completes or expires.\n   * Omit for a fresh session on every mount (the previous default, still\n   * fully supported). Use a key that's unique per candidate+interview (e.g.\n   * including a session or candidate id) — otherwise two different\n   * candidates on the same browser profile would resume each other's answers.\n   */\n  persistKey?: string;\n  /**\n   * Tracks two low-risk, non-biometric integrity signals while the\n   * interview is active — how many times the tab lost focus, and how many\n   * times the candidate pasted into an answer — and attaches them to the\n   * final report as `integritySignals`. Off by default; if you turn this\n   * on, disclose it to candidates (most platforms that track tab-switching\n   * say so up front). These are signals for a human reviewer to weigh in\n   * context, never an automated cheating verdict.\n   */\n  trackIntegritySignals?: boolean;\n  /** Extra class name(s) added to the root element, alongside `isdk-widget`. */\n  className?: string;\n  /**\n   * Inline styles on the root element — the escape hatch for hosts whose\n   * ancestor chain doesn't carry a definite height all the way down (e.g.\n   * `style={{ height: '100dvh' }}`), since the widget's own CSS fills 100%\n   * of its container but can't invent a height no ancestor provides.\n   */\n  style?: CSSProperties;\n}\n\nfunction assertClientModeAllowed(\n  mode: InterviewMode,\n  allowInProduction: boolean | undefined,\n): void {\n  if (mode !== 'client') return;\n  const isProduction = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\n  if (isProduction && !allowInProduction) {\n    throw new Error(\n      'InterviewWidget: Client Mode is prototyping-only and refuses to run with ' +\n        'NODE_ENV=production, because it exposes AI provider keys in the browser and ' +\n        'lets scores be computed (and tampered with) client-side. Use mode=\"server\" ' +\n        'with @interview-sdk/server for production, or pass allowClientModeInProduction ' +\n        'if you have deliberately accepted that risk.',\n    );\n  }\n}\n\nfunction buildHint(question: Question): string | undefined {\n  if (!question.concepts || question.concepts.length === 0) return undefined;\n  return `Consider covering: ${question.concepts.join(', ')}.`;\n}\n\nfunction formatElapsed(ms: number): string {\n  const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n  const minutes = Math.floor(totalSeconds / 60);\n  const seconds = totalSeconds % 60;\n  return `${minutes}:${seconds.toString().padStart(2, '0')}`;\n}\n\n/**\n * mode/adapter are a one-time setup choice in real apps (typically a\n * memoized value, never re-derived per keystroke), so throwing loudly and\n * immediately here — outside InterviewErrorBoundary — fits: it's exactly\n * the kind of developer mistake that should be impossible to miss.\n *\n * Rubric/question validation is deliberately NOT duplicated here. Unlike\n * mode/adapter, questions and rubric are exactly the props a host app is\n * likely to edit live (a question builder, a dashboard) — a momentarily\n * blank prompt while someone retypes it is normal, not a bug, and must\n * never take the whole host page down with it. useInterview below already\n * validates this config as the single source of truth for both this\n * component and any headless consumer of that hook; here, that throw\n * happens inside the boundary, so a real invalid config still fails\n * clearly and immediately (the boundary's fallback renders the message),\n * it just can't escape and unmount an app embedding this widget.\n */\nexport function InterviewWidget(props: InterviewWidgetProps) {\n  assertClientModeAllowed(props.mode, props.allowClientModeInProduction);\n\n  if (props.mode === 'client' && !props.adapter) {\n    throw new Error(\n      'InterviewWidget: mode=\"client\" requires an `adapter` prop (an @interview-sdk/adapter-* instance).',\n    );\n  }\n\n  const shellClassName = ['isdk-widget', props.className].filter(Boolean).join(' ');\n  // Lets the boundary auto-recover once the config it choked on actually\n  // changes (the next keystroke fixing that blank prompt) instead of\n  // staying stuck on its fallback until something manually resets it. This\n  // computation runs here, outside the boundary, so it must never throw\n  // itself — a circular reference in caller-supplied questions/rubric (a\n  // plausible authoring mistake) would otherwise crash synchronously before\n  // the boundary ever gets a chance to render its fallback. Auto-recovery\n  // simply won't fire for such a pathological config; the widget still\n  // renders instead of crashing, which is what actually matters here.\n  let resetKey: string;\n  try {\n    resetKey = JSON.stringify({\n      questions: props.questions,\n      rubric: props.rubric,\n      maxFollowUpDepth: props.maxFollowUpDepth,\n      sessionTimeoutMs: props.sessionTimeoutMs,\n    });\n  } catch {\n    resetKey = 'unserializable-config';\n  }\n  return (\n    <InterviewErrorBoundary shellClassName={shellClassName} resetKey={resetKey}>\n      <InterviewWidgetInner {...props} />\n    </InterviewErrorBoundary>\n  );\n}\n\nfunction InterviewWidgetInner({\n  questions,\n  rubric,\n  mode,\n  adapter,\n  apiBaseUrl,\n  serverHeaders,\n  maxFollowUpDepth,\n  sessionTimeoutMs,\n  onSessionEnd,\n  transcribe,\n  synthesize,\n  onVoiceError,\n  requestMicStream,\n  joinLabel,\n  roleTitle,\n  candidateName,\n  onExportError,\n  persistKey,\n  trackIntegritySignals = false,\n  className,\n  style,\n}: InterviewWidgetProps) {\n  const processor = useMemo(\n    () =>\n      mode === 'client'\n        ? new ClientModeProcessor(adapter!, { maxDepth: maxFollowUpDepth })\n        : new ServerModeProcessor({ endpoint: apiBaseUrl, headers: serverHeaders }),\n    // Constructed once for the widget's lifetime — mode/adapter/endpoint are\n    // not expected to change on an already-mounted interview.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [],\n  );\n\n  // Tracks which prompt the hint was requested for, rather than a plain\n  // boolean, so the hint doesn't keep showing once the prompt moves on\n  // (new question or new follow-up) without another explicit request.\n  const [hintRequestedForPrompt, setHintRequestedForPrompt] = useState<string | undefined>(\n    undefined,\n  );\n  // Mirrors QuestionCard's own recording state up here, purely so the\n  // header's REC badge reflects genuinely-live recording — never a\n  // constant, faked \"always recording\" claim.\n  const [isRecording, setIsRecording] = useState(false);\n\n  // Read exactly once, on mount — matching initialSnapshot's own contract in\n  // useInterview. persistKey changing on an already-mounted widget does not\n  // re-trigger a resume; that's a config change, not a runtime event.\n  const [initialSnapshot] = useState(() => readPersistedSnapshot(persistKey));\n\n  const integrity = useIntegritySignals(trackIntegritySignals);\n\n  const interview: UseInterviewResult = useInterview({\n    questions,\n    rubric,\n    processor,\n    maxFollowUpDepth,\n    sessionTimeoutMs,\n    onSessionEnd,\n    ...(initialSnapshot ? { initialSnapshot } : {}),\n    ...(trackIntegritySignals ? { getIntegritySignals: integrity.getSnapshot } : {}),\n  });\n\n  useEffect(() => {\n    integrity.setActive(interview.status === 'in_progress');\n  }, [interview.status, integrity]);\n\n  const { status: interviewStatus, transcript: interviewTranscript, getSnapshot } = interview;\n  useEffect(() => {\n    if (!persistKey) return;\n    if (interviewStatus === 'not_started') return;\n    if (interviewStatus === 'completed' || interviewStatus === 'expired') {\n      clearPersistedSnapshot(persistKey);\n      return;\n    }\n    writePersistedSnapshot(persistKey, getSnapshot());\n  }, [persistKey, interviewStatus, interviewTranscript, getSnapshot]);\n\n  // Computed after useInterview so its config validation (which collects\n  // every issue into one clear error) is what a bad rubric surfaces as,\n  // rather than defineRubric's own narrower first-issue-only error.\n  const normalizedRubric = useMemo(() => defineRubric(rubric), [rubric]);\n\n  // A live elapsed-time display, ticking only while the session is actually\n  // running — sourced from the flow engine's real startedAt timestamp\n  // (exposed by useInterview), not reinvented as separate session logic.\n  const [now, setNow] = useState(() => Date.now());\n  useEffect(() => {\n    if (interview.status !== 'in_progress') return;\n    const id = window.setInterval(() => setNow(Date.now()), 1000);\n    return () => window.clearInterval(id);\n  }, [interview.status]);\n  const elapsedLabel = interview.startedAt\n    ? formatElapsed(now - interview.startedAt)\n    : undefined;\n  const elapsedFraction =\n    interview.startedAt && sessionTimeoutMs\n      ? Math.min(1, Math.max(0, (now - interview.startedAt) / sessionTimeoutMs))\n      : undefined;\n\n  const shellClassName = ['isdk-widget', className].filter(Boolean).join(' ');\n\n  if (interview.status === 'completed' && interview.report) {\n    return (\n      <div className={`${shellClassName} isdk-widget--center`} style={style}>\n        <div className=\"isdk-widget__hero\">\n          <ReportCard\n            report={interview.report}\n            rubric={normalizedRubric}\n            {...(onExportError ? { onExportError } : {})}\n          />\n        </div>\n      </div>\n    );\n  }\n\n  if (interview.status === 'expired') {\n    return (\n      <div className={`${shellClassName} isdk-widget--center`} style={style}>\n        <div className=\"isdk-widget__hero\">\n          <div className=\"isdk-lobby\">\n            <p className=\"isdk-kicker\">Session expired</p>\n            <h2 className=\"isdk-lobby__title\">This interview session has timed out</h2>\n            <p className=\"isdk-lobby__meta\">\n              {interview.error?.message ??\n                'The time limit for this session was reached, so it can no longer be resumed.'}\n            </p>\n          </div>\n        </div>\n      </div>\n    );\n  }\n\n  if (interview.status === 'not_started') {\n    return (\n      <div className={`${shellClassName} isdk-widget--center`} style={style}>\n        <div className=\"isdk-widget__hero\">\n          <InterviewLobby\n            onJoin={interview.start}\n            voiceEnabled={Boolean(transcribe)}\n            totalQuestions={questions.length}\n            {...(requestMicStream ? { requestMicStream } : {})}\n            {...(joinLabel ? { joinLabel } : {})}\n          />\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className={shellClassName} style={style}>\n      <header className=\"isdk-widget__header\">\n        {roleTitle ? <p className=\"isdk-widget__role-title\">{roleTitle}</p> : <span aria-hidden=\"true\" />}\n        <div className=\"isdk-widget__header-actions\">\n          {isRecording && <span className=\"isdk-widget__rec-badge\">● REC</span>}\n          <button\n            className=\"isdk-btn isdk-btn--secondary isdk-widget__end\"\n            type=\"button\"\n            onClick={() => interview.endInterview()}\n          >\n            End Interview\n          </button>\n        </div>\n      </header>\n\n      {interview.error && (\n        <div className=\"isdk-widget__error\" role=\"alert\">\n          <p>{interview.error.message}</p>\n          <button\n            className=\"isdk-btn isdk-btn--secondary\"\n            type=\"button\"\n            onClick={() => void interview.retryLastAnswer()}\n          >\n            Retry\n          </button>\n        </div>\n      )}\n\n      {interview.status === 'paused' ? (\n        <div className=\"isdk-widget__paused\">\n          <div className=\"isdk-widget__hero\">\n            <div className=\"isdk-lobby\">\n              <p className=\"isdk-kicker\">Paused</p>\n              <h2 className=\"isdk-lobby__title\">Taking a break?</h2>\n              <p className=\"isdk-lobby__meta\">Your progress is saved. Resume when you&rsquo;re ready.</p>\n              <div className=\"isdk-lobby__actions\">\n                <button\n                  className=\"isdk-btn isdk-btn--primary\"\n                  type=\"button\"\n                  onClick={interview.resume}\n                >\n                  Resume\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n      ) : (\n        interview.currentQuestion && (\n          <div className=\"isdk-widget__body\">\n            <div className=\"isdk-widget__stage-wrap\">\n              <QuestionCard\n                prompt={interview.currentPrompt ?? interview.currentQuestion.prompt}\n                questionNumber={\n                  questions.findIndex((q) => q.id === interview.currentQuestion!.id) + 1\n                }\n                totalQuestions={questions.length}\n                isFollowUp={interview.isFollowUpPrompt}\n                topic={interview.currentQuestion.concepts?.[0]}\n                {...(candidateName ? { candidateName } : {})}\n                isSubmitting={interview.isProcessing}\n                onSubmit={(text) => void interview.submitAnswer(text)}\n                onSkip={() => void interview.submitAnswer('', { isSkipped: true })}\n                onRequestHint={() => setHintRequestedForPrompt(interview.currentPrompt)}\n                hint={\n                  hintRequestedForPrompt === interview.currentPrompt\n                    ? buildHint(interview.currentQuestion)\n                    : undefined\n                }\n                transcribe={transcribe}\n                synthesize={synthesize}\n                onVoiceError={onVoiceError}\n                onRecordingChange={setIsRecording}\n                elapsedLabel={elapsedLabel}\n                elapsedFraction={elapsedFraction}\n                onPause={interview.pause}\n                pauseDisabled={interview.status !== 'in_progress'}\n                {...(trackIntegritySignals ? { onAnswerPaste: integrity.recordPaste } : {})}\n              />\n            </div>\n\n            <aside className=\"isdk-widget__sidebar\" aria-label=\"Interview progress and transcript\">\n              <InterviewProgress\n                questions={questions}\n                currentQuestion={interview.currentQuestion}\n                transcript={interview.transcript}\n              />\n              <TranscriptChat\n                transcript={interview.transcript}\n                {...(candidateName ? { candidateName } : {})}\n              />\n              <LiveSignals rubric={normalizedRubric} transcript={interview.transcript} />\n            </aside>\n          </div>\n        )\n      )}\n    </div>\n  );\n}\n"]}