{"version":3,"sources":["../src/api/client.ts","../src/capture/device.ts","../src/capture/console.ts","../src/capture/network.ts","../src/capture/errors.ts","../src/capture/performance.ts","../src/capture/ringBuffer.ts","../src/capture/index.ts","../src/widget/i18n.ts","../src/widget/mount.tsx","../src/widget/BoardView.tsx","../src/widget/boardViewStore.ts","../src/widget/ReportDetailView.tsx","../src/widget/CommentBubble.tsx","../src/widget/screenshot-utils.ts","../src/widget/ChangelogList.tsx","../src/widget/ReportRow.tsx","../src/widget/Fab.tsx","../src/widget/Form.tsx","../src/widget/Annotator.tsx","../src/widget/MineList.tsx","../src/widget/KpiStrip.tsx","../src/widget/Modal.tsx","../src/widget/styles.ts","../src/core.ts"],"sourcesContent":["import type {\n  BoardFilters,\n  ReportPayload,\n  SubmittedReport,\n  WidgetBoardKpis,\n  WidgetBoardRow,\n  WidgetChangelogRow,\n  WidgetCommentRow,\n  WidgetReportDetail,\n  WidgetReportRow,\n} from '../types'\n\n/** Page envelope DRF returns for paginated list endpoints. */\nexport interface BoardListPage {\n  count: number\n  next: string | null\n  previous: string | null\n  results: WidgetBoardRow[]\n}\n\nexport interface ApiClientOptions {\n  apiKey: string\n  endpoint: string\n  fetch?: typeof fetch\n  beforeSend?: (payload: ReportPayload) => ReportPayload | false | Promise<ReportPayload | false>\n  /**\n   * v0.13 — signed-identity callback. Returns the current\n   * `(userHash, exp, email)` triple if the host has called\n   * `identify({userHash, exp, ...})`. The API client reads this fresh\n   * on every request so a delayed `identify()` lights up signed\n   * headers on subsequent calls. Return `null` for the legacy\n   * unsigned path (the default until the project issues a\n   * `WidgetSigningSecret`).\n   */\n  getSignedIdentity?: () => { userHash: string; exp: number; email: string } | null\n}\n\nexport interface ApiClient {\n  submitReport(payload: ReportPayload): Promise<SubmittedReport>\n  /** POST /v1/widget/visibility/ — whether this end-user may see the widget\n   *  (Phase 4 allowlist). `email`, when the host identified one, lets an\n   *  email-based allowlist match (the backend matches external_id OR email).\n   *  Throws on error so the caller can fail closed. */\n  checkVisibility(externalId: string, email?: string): Promise<boolean>\n  /** GET /v1/reports/widget/mine/ — caller's own reports on this project. */\n  listMine(externalId: string): Promise<WidgetReportRow[]>\n  /** GET /v1/reports/widget/changelog/ — caller's resolved reports for the\n   *  \"This week\" tab; client groups by ISO week of `resolved_at`. */\n  listChangelog(externalId: string): Promise<WidgetChangelogRow[]>\n  /** GET /v1/reports/widget/<id>/ — single report + thread. */\n  getReport(reportId: string, externalId: string): Promise<WidgetReportDetail>\n  /** POST /v1/reports/widget/<id>/comments/ — submitter follow-up. */\n  addComment(\n    reportId: string,\n    externalId: string,\n    body: string,\n    clientNonce?: string,\n  ): Promise<WidgetCommentRow>\n  /** PATCH /v1/reports/widget/<id>/ — close-as-resolved by the submitter. */\n  closeAsResolved(\n    reportId: string,\n    externalId: string,\n  ): Promise<WidgetReportDetail>\n  /** PATCH /v1/reports/widget/<id>/ — reopen (→ in_progress) when the\n   *  submitter says the shipped fix didn't actually resolve the issue. */\n  reopenUnresolved(\n    reportId: string,\n    externalId: string,\n  ): Promise<WidgetReportDetail>\n  /** GET /v1/reports/widget/board/ — paginated, filtered list backing the\n   *  v0.12 Board tab. Scope follows the project's\n   *  `share_reports_with_widget` flag (submitter-only when off, project-\n   *  wide when on); `filters.mine = true` narrows even when on. */\n  listBoard(externalId: string, filters?: BoardFilters): Promise<BoardListPage>\n  /** GET /v1/reports/widget/board/kpis/ — total + per-status counts +\n   *  resolution rate. Cheap; safe to poll alongside the list. */\n  fetchBoardKpis(externalId: string, filters?: BoardFilters): Promise<WidgetBoardKpis>\n}\n\nconst SCALAR_FIELDS: Array<keyof ReportPayload> = [\n  'description', 'feedback_type', 'severity', 'env', 'page_url', 'user_agent', 'capture_method',\n]\n\nexport function assertSafeEndpoint(endpoint: string): void {\n  let parsed: URL\n  try {\n    parsed = new URL(endpoint)\n  } catch {\n    throw new Error(\n      `[mhosaic-feedback] \\`endpoint\\` is not a valid URL: ${endpoint}`,\n    )\n  }\n  if (parsed.protocol === 'https:') return\n  if (\n    parsed.protocol === 'http:' &&\n    (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1' || parsed.hostname === '[::1]')\n  ) {\n    return\n  }\n  throw new Error(\n    `[mhosaic-feedback] \\`endpoint\\` must use https:// (got ${parsed.protocol}//${parsed.hostname}). ` +\n      'http:// is only allowed for localhost in dev.',\n  )\n}\n\nexport function createApiClient(options: ApiClientOptions): ApiClient {\n  // No silent fallback to a hardcoded host — a misconfigured deploy used\n  // to leak reports to https://core.mhosaic.com (which doesn't even\n  // resolve). Failing fast at construction makes the misconfig obvious.\n  const endpoint = (options.endpoint ?? '').replace(/\\/+$/, '')\n  if (!endpoint) {\n    throw new Error(\n      '[mhosaic-feedback] `endpoint` is required (e.g. \"https://feedback.example.com\").',\n    )\n  }\n  // SECURITY: endpoint is host-controlled config. Without a scheme guard a\n  // misconfigured host (or a host XSS that injects a `&lt;script\n  // data-endpoint=\"http://attacker\"&gt;` embed tag) can steer the widget at\n  // any URL and exfiltrate every captured report — including the\n  // pk_proj_ key in the Authorization header — to attacker-controlled\n  // infra. Only allow https:; permit http:// for localhost/127.0.0.1 so\n  // dev sandboxes keep working.\n  assertSafeEndpoint(endpoint)\n  const fetcher = options.fetch ?? globalThis.fetch\n\n  async function submitReport(input: ReportPayload): Promise<SubmittedReport> {\n    let payload: ReportPayload | false = input\n    if (options.beforeSend) payload = await options.beforeSend(input)\n    if (payload === false) throw new Error('Submission cancelled by beforeSend')\n\n    const form = new FormData()\n    for (const field of SCALAR_FIELDS) {\n      form.append(field, String(payload[field]))\n    }\n    form.append('technical_context', JSON.stringify(payload.technical_context))\n    if (payload.screenshot) form.append('screenshot', payload.screenshot, 'screenshot.png')\n    // Only emit `synthetic` when truthy — a `false` value would still trigger\n    // the BooleanField parser on the backend, which is fine, but omitting it\n    // keeps the request shape byte-identical for existing curated submissions.\n    if (payload.synthetic) form.append('synthetic', 'true')\n    // v0.7: nest the identity payload so DRF's WidgetUserIdentitySerializer\n    // sees a real object. FormData can't carry nested objects directly —\n    // use a JSON-encoded string under the `user` key; DRF parses it via\n    // the JSONField semantics on the WidgetUser fields.\n    if (payload.user?.id) {\n      form.append('user', JSON.stringify(payload.user))\n    }\n    // v0.7.3: widget_version is build-time-stamped by tsup. Lets the\n    // backend show \"currently running v0.7.2\" per project on the\n    // operator Companies page (per-customer version observability).\n    if (payload.widget_version) {\n      form.append('widget_version', payload.widget_version)\n    }\n\n    // The backend honors `synthetic=true` only when this header is set,\n    // so a hand-crafted curl with the public key can't smuggle a curated\n    // report into the auto-error bucket (where the default operator view\n    // hides it). The bundled debugger always sets payload.synthetic; the\n    // user-facing widget never does.\n    const headers: Record<string, string> = {\n      Authorization: `Bearer ${options.apiKey}`,\n    }\n    if (payload.synthetic) {\n      headers['X-Mhosaic-Capture-Source'] = 'error-tracking'\n    }\n    // Signed-identity headers on the SUBMIT path too: when the host has\n    // wired up identify({userHash, exp, email}) and the backend project\n    // has a WidgetSigningSecret, the request must carry the HMAC. We\n    // also re-stamp `X-Mhosaic-User` so the verifier sees the\n    // signed-over external_id directly (the body `user` block is still\n    // sent — the backend prefers the header pair when signing is in\n    // effect; see views.py:CreateFeedbackReportView.post).\n    const signed = options.getSignedIdentity?.()\n    if (signed && signed.userHash && signed.exp && payload.user?.id) {\n      headers['X-Mhosaic-User'] = String(payload.user.id)\n      headers['X-Mhosaic-User-Hmac'] = signed.userHash\n      headers['X-Mhosaic-User-Exp'] = String(signed.exp)\n      if (signed.email) headers['X-Mhosaic-User-Email'] = signed.email\n    }\n    const response = await fetcher(`${endpoint}/api/feedback/v1/reports/`, {\n      method: 'POST',\n      headers,\n      body: form,\n    })\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`Feedback submit failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<SubmittedReport>\n  }\n\n  function widgetHeaders(externalId: string): Record<string, string> {\n    const headers: Record<string, string> = {\n      Authorization: `Bearer ${options.apiKey}`,\n      'X-Mhosaic-User': externalId,\n    }\n    // Signed-identity Phase 1 (v0.13): forward the HMAC envelope when the\n    // host has supplied one via `identify({userHash, exp, email})`. The\n    // backend gates on the project's `WidgetSigningSecret` row — projects\n    // that haven't issued a secret continue to accept the bare header.\n    const signed = options.getSignedIdentity?.()\n    if (signed && signed.userHash && signed.exp) {\n      headers['X-Mhosaic-User-Hmac'] = signed.userHash\n      headers['X-Mhosaic-User-Exp'] = String(signed.exp)\n      if (signed.email) headers['X-Mhosaic-User-Email'] = signed.email\n    }\n    return headers\n  }\n\n  async function listMine(externalId: string): Promise<WidgetReportRow[]> {\n    const response = await fetcher(`${endpoint}/api/feedback/v1/reports/widget/mine/`, {\n      method: 'GET',\n      headers: widgetHeaders(externalId),\n    })\n    if (response.status === 404) return [] // submitter has no thread yet\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`listMine failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<WidgetReportRow[]>\n  }\n\n  async function listChangelog(externalId: string): Promise<WidgetChangelogRow[]> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/reports/widget/changelog/`,\n      { method: 'GET', headers: widgetHeaders(externalId) },\n    )\n    if (response.status === 404) return []\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`listChangelog failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<WidgetChangelogRow[]>\n  }\n\n  async function getReport(\n    reportId: string,\n    externalId: string,\n  ): Promise<WidgetReportDetail> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n      { method: 'GET', headers: widgetHeaders(externalId) },\n    )\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`getReport failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<WidgetReportDetail>\n  }\n\n  async function addComment(\n    reportId: string,\n    externalId: string,\n    body: string,\n    clientNonce?: string,\n  ): Promise<WidgetCommentRow> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/reports/widget/${reportId}/comments/`,\n      {\n        method: 'POST',\n        headers: {\n          ...widgetHeaders(externalId),\n          'Content-Type': 'application/json',\n        },\n        body: JSON.stringify({\n          body,\n          ...(clientNonce !== undefined && { client_nonce: clientNonce }),\n        }),\n      },\n    )\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`addComment failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<WidgetCommentRow>\n  }\n\n  async function closeAsResolved(\n    reportId: string,\n    externalId: string,\n  ): Promise<WidgetReportDetail> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n      {\n        method: 'PATCH',\n        headers: {\n          ...widgetHeaders(externalId),\n          'Content-Type': 'application/json',\n        },\n        body: JSON.stringify({ status: 'closed' }),\n      },\n    )\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`closeAsResolved failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<WidgetReportDetail>\n  }\n\n  async function reopenUnresolved(\n    reportId: string,\n    externalId: string,\n  ): Promise<WidgetReportDetail> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n      {\n        method: 'PATCH',\n        headers: {\n          ...widgetHeaders(externalId),\n          'Content-Type': 'application/json',\n        },\n        body: JSON.stringify({ status: 'in_progress' }),\n      },\n    )\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`reopenUnresolved failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<WidgetReportDetail>\n  }\n\n  function boardQueryString(filters?: BoardFilters): string {\n    if (!filters) return ''\n    const params = new URLSearchParams()\n    // Multi-value fields use append() so repeated keys stack the way DRF's\n    // `request.query_params.getlist()` reads them on the server.\n    filters.status?.forEach((s) => params.append('status', s))\n    filters.type?.forEach((t) => params.append('type', t))\n    filters.severity?.forEach((s) => params.append('severity', s))\n    if (filters.q) params.set('q', filters.q)\n    if (filters.mine) params.set('mine', '1')\n    if (filters.ordering) params.set('ordering', filters.ordering)\n    if (filters.page && filters.page > 1) params.set('page', String(filters.page))\n    const qs = params.toString()\n    return qs ? `?${qs}` : ''\n  }\n\n  async function listBoard(\n    externalId: string,\n    filters?: BoardFilters,\n  ): Promise<BoardListPage> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/reports/widget/board/${boardQueryString(filters)}`,\n      { method: 'GET', headers: widgetHeaders(externalId) },\n    )\n    if (response.status === 404) {\n      // Caller has no WidgetUser row yet — render an empty Board rather\n      // than a hard error so the first-time experience is a clean slate.\n      return { count: 0, next: null, previous: null, results: [] }\n    }\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`listBoard failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<BoardListPage>\n  }\n\n  async function fetchBoardKpis(\n    externalId: string,\n    filters?: BoardFilters,\n  ): Promise<WidgetBoardKpis> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/reports/widget/board/kpis/${boardQueryString(filters)}`,\n      { method: 'GET', headers: widgetHeaders(externalId) },\n    )\n    if (response.status === 404) {\n      return { total: 0, by_status: {}, resolution_rate: 0, scope: 'mine' }\n    }\n    if (!response.ok) {\n      const text = await response.text().catch(() => '')\n      throw new Error(`fetchBoardKpis failed: ${response.status} ${text}`)\n    }\n    return response.json() as Promise<WidgetBoardKpis>\n  }\n\n  async function checkVisibility(\n    externalId: string,\n    email?: string,\n  ): Promise<boolean> {\n    const response = await fetcher(\n      `${endpoint}/api/feedback/v1/widget/visibility/`,\n      {\n        method: 'POST',\n        headers: {\n          ...widgetHeaders(externalId),\n          'Content-Type': 'application/json',\n        },\n        body: JSON.stringify({\n          external_id: externalId,\n          ...(email ? { email } : {}),\n        }),\n      },\n    )\n    if (!response.ok) {\n      throw new Error(`checkVisibility failed: ${response.status}`)\n    }\n    const data = (await response.json()) as { show?: boolean }\n    return Boolean(data.show)\n  }\n\n  return {\n    submitReport,\n    checkVisibility,\n    listMine,\n    listChangelog,\n    getReport,\n    addComment,\n    closeAsResolved,\n    reopenUnresolved,\n    listBoard,\n    fetchBoardKpis,\n  }\n}\n","import type { DeviceContext } from '../types'\nimport { scrubCredentials } from './scrub'\nimport { sanitizeUrl } from './urlSanitizer'\n\nexport function collectDevice(): DeviceContext {\n  const nav = navigator as Navigator & { connection?: { effectiveType?: string }; deviceMemory?: number }\n  const connection = nav.connection?.effectiveType\n  const deviceMemory = nav.deviceMemory\n  // Auth flows commonly hand off via query-string tokens (`?token=…`,\n  // `?code=…`); when the user clicks through to the host page, both the\n  // raw referrer AND the host's own pathname can carry those values.\n  // Run both through the same sanitizer used for fetch/XHR; collapse the\n  // result back to a path-only string so we don't accidentally upload the\n  // operator-side host's origin as if it were the captured page's URL.\n  const rawReferrer = document.referrer || undefined\n  const referrer = rawReferrer !== undefined ? sanitizeUrl(rawReferrer) : undefined\n  const sanitizedHere = sanitizeUrl(window.location.pathname + window.location.search)\n  let pathname: string\n  try {\n    const parsed = new URL(sanitizedHere, window.location.origin)\n    pathname = parsed.pathname + parsed.search\n  } catch {\n    pathname = window.location.pathname\n  }\n  return {\n    viewport: { w: window.innerWidth, h: window.innerHeight, dpr: window.devicePixelRatio || 1 },\n    screen: { w: window.screen.width, h: window.screen.height },\n    platform: nav.platform,\n    language: nav.language,\n    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n    timezoneOffset: new Date().getTimezoneOffset(),\n    ...(connection !== undefined && { connection }),\n    online: nav.onLine,\n    ...(deviceMemory !== undefined && { deviceMemory }),\n    hardwareConcurrency: nav.hardwareConcurrency,\n    ...(referrer !== undefined && { referrer }),\n    // Hosts commonly stuff credentials / PII into page titles\n    // (\"Inbox (3) — bob@x.com\", \"Reset password — token=abc\"). Cap to a\n    // sane length and run through the same scrubber the console/error\n    // paths use.\n    title: scrubCredentials((document.title || '')).slice(0, 200),\n    pathname,\n  }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ConsoleEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\ntype ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug'\n\nfunction safeStringify(arg: unknown): string {\n  if (arg == null) return String(arg)\n  if (typeof arg === 'string') return arg\n  if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg)\n  if (arg instanceof Error) return `${arg.name}: ${arg.message}`\n  if (arg instanceof Element) return `<${arg.tagName.toLowerCase()}>`\n  try {\n    return JSON.stringify(arg, (_k, v) => (typeof v === 'bigint' ? v.toString() : v))\n  } catch {\n    try { return String(arg) } catch { return '[unserializable]' }\n  }\n}\n\nexport function installConsolePatch(buffer: RingBuffer<ConsoleEntry>): () => void {\n  const levels: ConsoleLevel[] = ['log', 'info', 'warn', 'error', 'debug']\n  const originals: Partial<Record<ConsoleLevel, (...args: unknown[]) => void>> = {}\n  for (const level of levels) {\n    const original = console[level] as ((...args: unknown[]) => void) | undefined\n    if (typeof original !== 'function') continue\n    originals[level] = original\n    console[level] = function patched(...args: unknown[]) {\n      try {\n        const rawMessage = args.map(safeStringify).join(' ')\n        const message = scrubCredentials(rawMessage).slice(0, 2000)\n        const entry: ConsoleEntry = { level, message, ts: Date.now() }\n        if (level === 'error') {\n          const stack = new Error().stack\n          if (stack) entry.stack = stack.split('\\n').slice(2, 8).join('\\n')\n        }\n        buffer.push(entry)\n      } catch { /* never break console */ }\n      original.apply(console, args)\n    }\n  }\n  return () => {\n    for (const [level, fn] of Object.entries(originals)) {\n      (console as unknown as Record<string, (...args: unknown[]) => void>)[level] = fn\n    }\n  }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { NetworkEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\nexport function installFetchPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n  if (typeof window === 'undefined' || typeof window.fetch !== 'function') return () => {}\n  const original = window.fetch.bind(window)\n  window.fetch = async function patched(input: RequestInfo | URL, init?: RequestInit) {\n    const start = performance.now()\n    const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n    const method = (init?.method || (input instanceof Request ? input.method : 'GET')).toUpperCase()\n    try {\n      const response = await original(input, init)\n      buffer.push({ url: sanitize(url), method, status: response.status, durationMs: Math.round(performance.now() - start), ts: Date.now() })\n      return response\n    } catch (err) {\n      // Error messages from fetch (CORS rejection, abort, etc.) sometimes\n      // echo the requested URL back including its full query string. Scrub\n      // for the same patterns the console path does.\n      const rawErr = err instanceof Error ? err.message : String(err)\n      buffer.push({\n        url: sanitize(url), method, status: 0,\n        durationMs: Math.round(performance.now() - start),\n        ts: Date.now(),\n        error: scrubCredentials(rawErr),\n      })\n      throw err\n    }\n  }\n  return () => { window.fetch = original }\n}\n\nexport function installXhrPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n  if (typeof window === 'undefined' || typeof window.XMLHttpRequest !== 'function') return () => {}\n  const Original = window.XMLHttpRequest\n  const originalOpen = Original.prototype.open\n  const originalSend = Original.prototype.send\n\n  Original.prototype.open = function patchedOpen(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, method: string, url: string | URL) {\n    this.__mfb = { method: method.toUpperCase(), url: typeof url === 'string' ? url : url.toString(), start: performance.now() }\n    return originalOpen.apply(this, arguments as unknown as Parameters<typeof originalOpen>)\n  }\n\n  Original.prototype.send = function patchedSend(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, body?: Document | XMLHttpRequestBodyInit | null) {\n    this.addEventListener('loadend', () => {\n      try {\n        const ctx = this.__mfb\n        if (!ctx) return\n        buffer.push({\n          url: sanitize(ctx.url),\n          method: ctx.method,\n          status: this.status,\n          durationMs: Math.round(performance.now() - ctx.start),\n          ts: Date.now(),\n        })\n      } catch { /* noop */ }\n    })\n    return originalSend.call(this, body ?? null)\n  }\n\n  return () => {\n    Original.prototype.open = originalOpen\n    Original.prototype.send = originalSend\n  }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ErrorEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\n// Errors that bubble up to `window.error` or `unhandledrejection` often\n// carry credentials in plain text — e.g. an SDK throws `new Error(\"Bearer\n// eyJ... rejected (401)\")`, or a promise rejects with `{apiKey: \"sk_live_…\"}`.\n// Both ship to the central backend unless we scrub here.\n\nexport function installErrorHandlers(buffer: RingBuffer<ErrorEntry>): () => void {\n  if (typeof window === 'undefined') return () => {}\n  const onError = (e: ErrorEvent) => {\n    const rawStack = e.error instanceof Error ? e.error.stack : undefined\n    const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n    buffer.push({\n      message: scrubCredentials(e.message || 'Unknown error'),\n      ...(stack !== undefined && { stack }),\n      ts: Date.now(),\n      source: 'window.error',\n    })\n  }\n  const onRejection = (e: PromiseRejectionEvent) => {\n    const reason = e.reason\n    const rawMessage =\n      reason instanceof Error ? reason.message :\n      typeof reason === 'string' ? reason :\n      (() => { try { return JSON.stringify(reason) } catch { return String(reason) } })()\n    const rawStack = reason instanceof Error ? reason.stack : undefined\n    const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n    buffer.push({\n      message: scrubCredentials(rawMessage),\n      ...(stack !== undefined && { stack }),\n      ts: Date.now(),\n      source: 'unhandledrejection',\n    })\n  }\n  window.addEventListener('error', onError)\n  window.addEventListener('unhandledrejection', onRejection)\n  return () => {\n    window.removeEventListener('error', onError)\n    window.removeEventListener('unhandledrejection', onRejection)\n  }\n}\n","import { sanitizeUrl } from './urlSanitizer'\n\nexport interface PerformanceSnapshot {\n  navigation?: { type: string; duration: number }\n  longTasks: { duration: number; startTime: number }[]\n  slowResources: { name: string; duration: number; initiatorType: string }[]\n}\n\nexport function createPerformanceCollector(slowResourceMs = 1000) {\n  const longTasks: PerformanceSnapshot['longTasks'] = []\n  const slowResources: PerformanceSnapshot['slowResources'] = []\n  let observer: PerformanceObserver | null = null\n\n  if (typeof PerformanceObserver !== 'undefined') {\n    try {\n      observer = new PerformanceObserver((list) => {\n        for (const entry of list.getEntries()) {\n          if (entry.entryType === 'longtask') {\n            longTasks.push({ duration: entry.duration, startTime: entry.startTime })\n            while (longTasks.length > 20) longTasks.shift()\n          } else if (entry.entryType === 'resource') {\n            const e = entry as PerformanceResourceTiming\n            if (e.duration > slowResourceMs) {\n              // sanitizeUrl strips credential-shaped query params + the hash\n              // — the resource URL is otherwise raw input from any third-party\n              // request the host page makes (analytics pixels, OAuth\n              // callbacks with tokens in the query, etc.).\n              slowResources.push({ name: sanitizeUrl(e.name), duration: e.duration, initiatorType: e.initiatorType })\n              while (slowResources.length > 20) slowResources.shift()\n            }\n          }\n        }\n      })\n      observer.observe({ entryTypes: ['longtask', 'resource'] })\n    } catch { /* unsupported */ }\n  }\n\n  return {\n    snapshot(): PerformanceSnapshot {\n      const nav = typeof performance !== 'undefined' ? performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined : undefined\n      const navigation = nav ? { type: nav.type, duration: nav.duration } : undefined\n      return {\n        ...(navigation !== undefined && { navigation }),\n        longTasks: longTasks.slice(),\n        slowResources: slowResources.slice(),\n      }\n    },\n    dispose() { observer?.disconnect() },\n  }\n}\n","export class RingBuffer<T> {\n  private items: T[] = []\n  constructor(private readonly max: number) {}\n\n  push(item: T): void {\n    this.items.push(item)\n    while (this.items.length > this.max) this.items.shift()\n  }\n\n  snapshot(): T[] {\n    return this.items.slice()\n  }\n\n  clear(): void {\n    this.items.length = 0\n  }\n}\n","import { sanitizeUrl } from './urlSanitizer'\nimport { collectDevice } from './device'\nimport { installConsolePatch } from './console'\nimport { installFetchPatch, installXhrPatch } from './network'\nimport { installErrorHandlers } from './errors'\nimport { createPerformanceCollector } from './performance'\nimport { RingBuffer } from './ringBuffer'\nimport type { CapturedContext, ConsoleEntry, ErrorEntry, NetworkEntry } from '../types'\n\nexport interface CaptureOptions {\n  sanitizeUrl?: (url: string) => string\n  maxConsole?: number\n  maxNetwork?: number\n  maxErrors?: number\n}\n\nexport interface CaptureHandle {\n  snapshot(): CapturedContext\n  clear(): void\n  dispose(): void\n}\n\nexport function installCapture(options: CaptureOptions = {}): CaptureHandle {\n  const { maxConsole = 50, maxNetwork = 50, maxErrors = 20 } = options\n  const sanitize = options.sanitizeUrl ?? sanitizeUrl\n\n  const consoleBuf = new RingBuffer<ConsoleEntry>(maxConsole)\n  const networkBuf = new RingBuffer<NetworkEntry>(maxNetwork)\n  const errorBuf = new RingBuffer<ErrorEntry>(maxErrors)\n\n  const uninstallConsole = installConsolePatch(consoleBuf)\n  const uninstallFetch = installFetchPatch(networkBuf, sanitize)\n  const uninstallXhr = installXhrPatch(networkBuf, sanitize)\n  const uninstallErrors = installErrorHandlers(errorBuf)\n  const perf = createPerformanceCollector()\n\n  return {\n    snapshot(): CapturedContext {\n      return {\n        consoleLogs: consoleBuf.snapshot(),\n        networkRequests: networkBuf.snapshot(),\n        errors: errorBuf.snapshot(),\n        device: collectDevice(),\n        capturedAt: Date.now(),\n      }\n    },\n    clear() {\n      consoleBuf.clear(); networkBuf.clear(); errorBuf.clear()\n    },\n    dispose() {\n      uninstallConsole(); uninstallFetch(); uninstallXhr(); uninstallErrors()\n      perf.dispose()\n    },\n  }\n}\n","export const DEFAULT_STRINGS = {\n  'fab.label': 'Send feedback',\n  'form.title': 'Send feedback',\n  'form.description.label': 'What happened?',\n  'form.description.placeholder': 'Describe the issue or idea in one or two sentences.',\n  'form.type.label': 'Type',\n  'form.severity.label': 'Severity',\n  'form.submit': 'Send',\n  'form.cancel': 'Cancel',\n  'form.close': 'Close',\n  'form.submitting': 'Sending…',\n  'form.success': 'Thanks — your feedback was sent.',\n  'form.error': 'Could not send. Please try again.',\n  'form.description.required': 'Please describe the issue before sending.',\n  'form.screenshot.label': 'Screenshot',\n  'form.screenshot.cta_click': 'Click',\n  'form.screenshot.cta_rest': 'drop, or paste an image',\n  'form.screenshot.formats': 'PNG, JPEG or WebP — up to 10 MB',\n  'form.screenshot.remove': 'Remove screenshot',\n  'form.screenshot.annotate': 'Annotate',\n  'form.screenshot.error_type': 'Unsupported file type. Use PNG, JPEG or WebP.',\n  'form.screenshot.error_size': 'File too large (max {max} MB).',\n  'form.context.label': 'Page',\n  'type.bug': 'Bug',\n  'type.feature': 'Feature request',\n  'type.question': 'Question',\n  'type.praise': 'Praise',\n  'type.typo': 'Typo',\n  'severity.blocker': 'Blocker',\n  'severity.high': 'High',\n  'severity.medium': 'Medium',\n  'severity.low': 'Low',\n  'annotator.title': 'Annotate screenshot',\n  'annotator.tool.rectangle': 'Rectangle',\n  'annotator.tool.arrow': 'Arrow',\n  'annotator.tool.freehand': 'Freehand',\n  'annotator.tool.text': 'Text',\n  'annotator.tool.highlight': 'Highlight',\n  'annotator.tool.blur': 'Blur (hide sensitive data)',\n  'annotator.text_prompt': 'Enter text:',\n  'annotator.color_picker': 'Custom color',\n  'annotator.undo': 'Undo',\n  'annotator.redo': 'Redo',\n  'annotator.clear': 'Clear all',\n  'annotator.count_suffix': 'annotations',\n  'annotator.loading': 'Loading…',\n  'annotator.apply': 'Apply',\n  'annotator.applying': 'Applying…',\n  'tab.send': 'Send',\n  'tab.mine': 'My reports',\n  'tab.changelog': 'This week',\n  'tab.board': 'Board',\n  'board.kpi.total': 'Total',\n  'board.kpi.new': 'New',\n  'board.kpi.in_progress': 'In progress',\n  'board.kpi.awaiting_validation': 'Awaiting validation',\n  'board.kpi.closed': 'Closed',\n  'board.kpi.rejected': 'Rejected',\n  'board.kpi.resolution_rate': 'Resolution rate',\n  'board.sort': 'Sort',\n  'board.sort.recent': 'Newest first',\n  'board.sort.oldest': 'Oldest first',\n  'board.sort.updated': 'Recently active',\n  'board.sort.severity': 'Severity',\n  'board.sort.status': 'Status',\n  'board.filter.status': 'Status',\n  'board.filter.type': 'Type',\n  'board.filter.severity': 'Severity',\n  'board.filter.search.placeholder': 'Search…',\n  'board.filter.mine': 'Mine only',\n  'board.filter.clear': 'Clear filters',\n  'board.list.empty.title': 'Nothing here yet',\n  'board.list.empty.description': 'When reports land, they’ll show up here.',\n  'board.list.loading': 'Loading…',\n  'board.list.error': 'Couldn’t load reports.',\n  'board.retry': 'Try again',\n  'board.list.you': 'you',\n  'board.list.count': '{n} of {total}',\n  'board.detail.empty': 'Pick a report on the left to see its full thread.',\n  'board.detail.by': 'By',\n  'board.detail.confirm_resolution': 'Confirm resolution',\n  'board.detail.status_history': 'History',\n  'board.scope.project': 'Project reports',\n  'board.scope.mine': 'Your reports',\n  'board.back': 'Back',\n  'changelog.empty.title': 'Nothing resolved yet',\n  'changelog.empty.body': 'Once a report you sent is fixed it will appear here, grouped by week.',\n  'changelog.week_of': 'Week of {date}',\n  'changelog.resolved_one': '{count} resolved',\n  'changelog.resolved_many': '{count} resolved',\n  'mine.empty.title': 'No reports yet',\n  'mine.empty.body': 'Once you send feedback you can follow the thread here.',\n  'mine.refresh': 'Refresh',\n  'mine.loading': 'Loading…',\n  'mine.error': 'Could not load your reports.',\n  'mine.replies_one': '1 reply',\n  'mine.replies_many': '{count} replies',\n  'mine.filter.empty': 'No reports match this filter.',\n  'kpi.new': 'New',\n  'kpi.in_progress': 'In progress',\n  'kpi.awaiting_validation': 'Awaiting you',\n  'kpi.resolution_rate': 'Resolution rate',\n  'detail.back': 'Back',\n  'detail.thread': 'Conversation',\n  'detail.no_replies': 'No replies yet — we’ll let you know when an operator responds.',\n  'detail.compose_placeholder': 'Add a follow-up reply…',\n  'detail.compose_send': 'Reply',\n  'detail.compose_sending': 'Sending…',\n  'detail.close_cta': 'Mark as resolved',\n  'detail.close_busy': 'Marking…',\n  'detail.reopen_cta': 'Still not fixed',\n  'detail.reopen_busy': 'Reopening…',\n  'detail.teammate_notice':\n    'Viewing a teammate’s report — replies are private to the submitter.',\n  'detail.load_failed.title': 'Report not available',\n  'detail.load_failed.body':\n    'It may have been deleted, or you no longer have access to it.',\n  'detail.load_failed.cta': 'Back',\n  'detail.send_failed': 'Couldn’t send your reply. Try again.',\n  'detail.close_failed': 'Couldn’t mark as resolved. Try again.',\n  'detail.reopen_failed': 'Couldn’t reopen the report. Try again.',\n  'detail.history': 'Status history',\n  'detail.context.submitted_at': 'Submitted',\n  'detail.context.page': 'Page',\n  'detail.context.capture.manual': 'Manual capture',\n  'detail.context.capture.html2canvas': 'Auto capture',\n  'detail.context.capture.display_media': 'Screen share',\n  'detail.context.capture.none': 'No screenshot',\n  'detail.author.staff': 'Operator',\n  'detail.author.mcp': 'Mhosaic Team',\n  'detail.author.system': 'System',\n  'detail.tech.title': 'What we received',\n  'detail.tech.errors_one': 'error',\n  'detail.tech.errors_many': 'errors',\n  'detail.tech.device': 'Device',\n  'detail.tech.device.viewport': 'Viewport',\n  'detail.tech.device.platform': 'Platform',\n  'detail.tech.device.language': 'Language',\n  'detail.tech.device.timezone': 'Timezone',\n  'detail.tech.device.connection': 'Connection',\n  'detail.tech.errors': 'Runtime errors',\n  'detail.tech.console': 'Console (last 20)',\n  'detail.tech.network': 'Network (last 15)',\n  'status.new': 'New',\n  'status.in_progress': 'In progress',\n  'status.awaiting_validation': 'Awaiting your validation',\n  'status.closed': 'Closed',\n  'status.rejected': 'Rejected',\n  'status.duplicate': 'Duplicate',\n  'status.wontfix': 'Won’t fix',\n}\n\nexport type StringKey = keyof typeof DEFAULT_STRINGS\n\nconst FRENCH_STRINGS: Record<StringKey, string> = {\n  'fab.label': 'Envoyer un commentaire',\n  'form.title': 'Envoyer un commentaire',\n  'form.description.label': 'Qu’est-il arrivé ?',\n  'form.description.placeholder': 'Décrivez le problème ou l’idée en une ou deux phrases.',\n  'form.type.label': 'Type',\n  'form.severity.label': 'Sévérité',\n  'form.submit': 'Envoyer',\n  'form.cancel': 'Annuler',\n  'form.close': 'Fermer',\n  'form.submitting': 'Envoi…',\n  'form.success': 'Merci — votre commentaire a été envoyé.',\n  'form.error': 'Échec d’envoi. Veuillez réessayer.',\n  'form.description.required': 'Décrivez le problème avant d’envoyer.',\n  'form.screenshot.label': 'Capture d’écran',\n  'form.screenshot.cta_click': 'Cliquez',\n  'form.screenshot.cta_rest': 'déposez ou collez une image',\n  'form.screenshot.formats': 'PNG, JPEG ou WebP — jusqu’à 10 Mo',\n  'form.screenshot.remove': 'Retirer la capture',\n  'form.screenshot.annotate': 'Annoter',\n  'form.screenshot.error_type': 'Format non supporté. Utilisez PNG, JPEG ou WebP.',\n  'form.screenshot.error_size': 'Fichier trop volumineux (max {max} Mo).',\n  'form.context.label': 'Page',\n  'type.bug': 'Bogue',\n  'type.feature': 'Suggestion',\n  'type.question': 'Question',\n  'type.praise': 'Compliment',\n  'type.typo': 'Coquille',\n  'severity.blocker': 'Bloquant',\n  'severity.high': 'Élevée',\n  'severity.medium': 'Moyenne',\n  'severity.low': 'Faible',\n  'annotator.title': 'Annoter la capture',\n  'annotator.tool.rectangle': 'Rectangle',\n  'annotator.tool.arrow': 'Flèche',\n  'annotator.tool.freehand': 'Dessin libre',\n  'annotator.tool.text': 'Texte',\n  'annotator.tool.highlight': 'Surligner',\n  'annotator.tool.blur': 'Flouter (données sensibles)',\n  'annotator.text_prompt': 'Entrez le texte :',\n  'annotator.color_picker': 'Couleur personnalisée',\n  'annotator.undo': 'Annuler',\n  'annotator.redo': 'Refaire',\n  'annotator.clear': 'Tout effacer',\n  'annotator.count_suffix': 'annotations',\n  'annotator.loading': 'Chargement…',\n  'annotator.apply': 'Appliquer',\n  'annotator.applying': 'Application…',\n  'tab.send': 'Envoyer',\n  'tab.mine': 'Mes rapports',\n  'tab.changelog': 'Cette semaine',\n  'tab.board': 'Tableau',\n  'board.kpi.total': 'Total',\n  'board.kpi.new': 'Nouveau',\n  'board.kpi.in_progress': 'En cours',\n  'board.kpi.awaiting_validation': 'À valider',\n  'board.kpi.closed': 'Fermé',\n  'board.kpi.rejected': 'Refusé',\n  'board.kpi.resolution_rate': 'Taux de résolution',\n  'board.sort': 'Trier',\n  'board.sort.recent': 'Plus récents',\n  'board.sort.oldest': 'Plus anciens',\n  'board.sort.updated': 'Activité récente',\n  'board.sort.severity': 'Sévérité',\n  'board.sort.status': 'Statut',\n  'board.filter.status': 'Statut',\n  'board.filter.type': 'Type',\n  'board.filter.severity': 'Sévérité',\n  'board.filter.search.placeholder': 'Rechercher…',\n  'board.filter.mine': 'Les miens',\n  'board.filter.clear': 'Effacer les filtres',\n  'board.list.empty.title': 'Rien à voir ici',\n  'board.list.empty.description': 'Les rapports apparaîtront ici dès qu’ils arrivent.',\n  'board.list.loading': 'Chargement…',\n  'board.list.error': 'Impossible de charger les rapports.',\n  'board.retry': 'Réessayer',\n  'board.list.you': 'vous',\n  'board.list.count': '{n} sur {total}',\n  'board.detail.empty': 'Sélectionnez un rapport à gauche pour voir le fil complet.',\n  'board.detail.by': 'Par',\n  'board.detail.confirm_resolution': 'Confirmer la résolution',\n  'board.detail.status_history': 'Historique',\n  'board.scope.project': 'Rapports du projet',\n  'board.scope.mine': 'Vos rapports',\n  'board.back': 'Retour',\n  'changelog.empty.title': 'Rien de résolu pour l’instant',\n  'changelog.empty.body': 'Quand un rapport que vous avez envoyé est corrigé, il apparaîtra ici, regroupé par semaine.',\n  'changelog.week_of': 'Semaine du {date}',\n  'changelog.resolved_one': '{count} résolu',\n  'changelog.resolved_many': '{count} résolus',\n  'mine.empty.title': 'Aucun rapport',\n  'mine.empty.body': 'Après votre premier envoi vous pourrez suivre la conversation ici.',\n  'mine.refresh': 'Actualiser',\n  'mine.loading': 'Chargement…',\n  'mine.error': 'Impossible de charger vos rapports.',\n  'mine.replies_one': '1 réponse',\n  'mine.replies_many': '{count} réponses',\n  'mine.filter.empty': 'Aucun rapport ne correspond à ce filtre.',\n  'kpi.new': 'Nouveau',\n  'kpi.in_progress': 'En cours',\n  'kpi.awaiting_validation': 'À valider',\n  'kpi.resolution_rate': 'Taux de résolution',\n  'detail.back': 'Retour',\n  'detail.thread': 'Conversation',\n  'detail.no_replies': 'Pas encore de réponse — vous serez notifié dès qu’un opérateur répondra.',\n  'detail.compose_placeholder': 'Ajouter une réponse…',\n  'detail.compose_send': 'Répondre',\n  'detail.compose_sending': 'Envoi…',\n  'detail.close_cta': 'Marquer comme résolu',\n  'detail.close_busy': 'Validation…',\n  'detail.reopen_cta': 'Toujours pas réglé',\n  'detail.reopen_busy': 'Réouverture…',\n  'detail.teammate_notice':\n    'Vous consultez le rapport d’un coéquipier — les réponses sont privées au soumetteur.',\n  'detail.load_failed.title': 'Rapport indisponible',\n  'detail.load_failed.body':\n    'Il a peut-être été supprimé, ou vous n’y avez plus accès.',\n  'detail.load_failed.cta': 'Retour',\n  'detail.send_failed': 'Impossible d’envoyer votre réponse. Réessayez.',\n  'detail.close_failed':\n    'Impossible de marquer comme résolu. Réessayez.',\n  'detail.reopen_failed':\n    'Impossible de rouvrir le rapport. Réessayez.',\n  'detail.history': 'Historique du statut',\n  'detail.context.submitted_at': 'Envoyé',\n  'detail.context.page': 'Page',\n  'detail.context.capture.manual': 'Capture manuelle',\n  'detail.context.capture.html2canvas': 'Capture automatique',\n  'detail.context.capture.display_media': 'Partage d’écran',\n  'detail.context.capture.none': 'Sans capture',\n  'detail.author.staff': 'Opérateur',\n  'detail.author.mcp': 'Équipe Mhosaic',\n  'detail.author.system': 'Système',\n  'detail.tech.title': 'Ce que nous avons reçu',\n  'detail.tech.errors_one': 'erreur',\n  'detail.tech.errors_many': 'erreurs',\n  'detail.tech.device': 'Appareil',\n  'detail.tech.device.viewport': 'Fenêtre',\n  'detail.tech.device.platform': 'Plateforme',\n  'detail.tech.device.language': 'Langue',\n  'detail.tech.device.timezone': 'Fuseau horaire',\n  'detail.tech.device.connection': 'Connexion',\n  'detail.tech.errors': 'Erreurs d’exécution',\n  'detail.tech.console': 'Console (20 derniers)',\n  'detail.tech.network': 'Réseau (15 derniers)',\n  'status.new': 'Nouveau',\n  'status.in_progress': 'En cours',\n  'status.awaiting_validation': 'En attente de validation',\n  'status.closed': 'Fermé',\n  'status.rejected': 'Rejeté',\n  'status.duplicate': 'Doublon',\n  'status.wontfix': 'Non corrigé',\n}\n\nconst LOCALE_PACKS: Record<string, Record<StringKey, string>> = {\n  fr: FRENCH_STRINGS,\n}\n\ninterface ResolveOptions {\n  locale?: string\n}\n\nfunction packFor(locale: string | undefined): Record<StringKey, string> | null {\n  if (!locale) return null\n  // Match the language subtag only (fr-CA, fr-FR → fr).\n  const tag = locale.toLowerCase().split(/[-_]/)[0]!\n  return LOCALE_PACKS[tag] ?? null\n}\n\nexport function resolveStrings(\n  overrides: Record<string, string>,\n  options: ResolveOptions = {},\n): Record<StringKey, string> {\n  const localePack = packFor(options.locale) ?? {}\n  return {\n    ...DEFAULT_STRINGS,\n    ...localePack,\n    ...overrides,\n  } as Record<StringKey, string>\n}\n","import { h, render } from 'preact'\nimport { useCallback, useEffect, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport { BoardView } from './BoardView'\nimport { ChangelogList } from './ChangelogList'\nimport { Fab } from './Fab'\nimport { Form, type FormValues } from './Form'\nimport { MineList } from './MineList'\nimport { Modal } from './Modal'\nimport { ReportDetailView } from './ReportDetailView'\nimport { WIDGET_STYLES } from './styles'\nimport type { StringKey } from './i18n'\n\nexport interface MountOptions {\n  host: HTMLElement\n  strings: Record<StringKey, string>\n  showFAB: boolean\n  onSubmit: (values: FormValues) => Promise<void>\n  /** v0.7: ApiClient for the conversation-loop reader UI. Optional so\n   *  the synthetic / auto-error path keeps working with no host changes. */\n  api?: ApiClient\n  /** v0.7: callback that returns the host's identified user id, or\n   *  undefined when no `identify()` has been called yet. The \"My\n   *  reports\" tab + FAB visibility both depend on this. */\n  getExternalId?: () => string | undefined\n  /** Phase 4: when true (driven by the manifest's requires_visibility_check),\n   *  the FAB renders only if `checkVisibility` confirms this end-user is on the\n   *  project allowlist. Off (default) → the legacy showFAB/identity path. */\n  requiresVisibilityCheck?: boolean\n  checkVisibility?: (externalId: string) => Promise<boolean>\n}\n\n/** Pure FAB-visibility decision (Phase 4 adds the `visibilityAllowed` gate on\n *  top of the showFAB + identity rules). Exported for unit testing. */\nexport function computeFabVisible(\n  opts: Pick<MountOptions, 'showFAB' | 'getExternalId' | 'requiresVisibilityCheck'>,\n  externalId: string | undefined,\n  visibilityAllowed: boolean,\n): boolean {\n  if (!opts.showFAB) return false\n  if (opts.getExternalId !== undefined && !externalId) return false\n  if (opts.requiresVisibilityCheck && !visibilityAllowed) return false\n  return true\n}\n\nexport interface MountHandle {\n  open(): void\n  close(): void\n  dispose(): void\n  /** Force a re-render — e.g. after the host calls `identify()` so\n   *  the FAB becomes visible without a page reload. */\n  notifyIdentityChanged(): void\n}\n\ntype Status = 'idle' | 'submitting' | 'error' | 'success'\ntype Tab = 'send' | 'mine' | 'changelog' | 'board'\ninterface State {\n  open: boolean\n  status: Status\n  error?: string\n  tab: Tab\n  /** When set, MineList is replaced by the detail view for that report id. */\n  selectedReportId?: string\n}\n\nexport function mountWidget(options: MountOptions): MountHandle {\n  const shadow = options.host.attachShadow({ mode: 'open' })\n  const style = document.createElement('style')\n  style.textContent = WIDGET_STYLES\n  shadow.appendChild(style)\n  const mountPoint = document.createElement('div')\n  shadow.appendChild(mountPoint)\n\n  let currentState: State = { open: false, status: 'idle', tab: 'send' }\n  let postSubmitTimer: ReturnType<typeof setTimeout> | null = null\n\n  function rerender(state: State) {\n    currentState = state\n    render(h(Root, { state }), mountPoint)\n  }\n\n  /** Strip selectedReportId rather than reassigning `undefined` — under\n   *  exactOptionalPropertyTypes the explicit `undefined` is not assignable\n   *  back into the optional slot. */\n  function clearSelected(s: State): State {\n    const { selectedReportId: _drop, ...rest } = s\n    void _drop\n    return rest\n  }\n\n  function Root({ state }: { state: State }) {\n    const handleSubmit = useCallback(async (values: FormValues) => {\n      rerender({ ...currentState, status: 'submitting' })\n      try {\n        await options.onSubmit(values)\n        rerender({ ...currentState, status: 'success' })\n        // Track the close-the-modal timer so dispose() can cancel it.\n        // Without this, calling shutdown() mid-submit (e.g. SPA route\n        // change right after the user hits send) lets the timer fire\n        // after teardown and rerender into a stale shadow root.\n        if (postSubmitTimer !== null) clearTimeout(postSubmitTimer)\n        postSubmitTimer = setTimeout(() => {\n          postSubmitTimer = null\n          rerender({\n            ...currentState,\n            open: false,\n            status: 'idle',\n            // After a successful submit, jump the user to \"My reports\" so\n            // they immediately see their just-filed report in the list\n            // (and the conversation that's about to start).\n            tab: options.api ? 'mine' : 'send',\n          })\n        }, 1200)\n      } catch (err) {\n        // Don't leak raw API errors to the form's error banner (e.g.\n        // `Feedback submit failed: 429 Too Many Requests {…}` straight\n        // into a `<div class=\"error\">`). The strings table already has\n        // `form.error` — a friendly fallback. Raw error to console.\n        if (typeof console !== 'undefined')\n          console.warn('[mhosaic] submit:', err)\n        rerender({\n          ...currentState,\n          status: 'error',\n          error: options.strings['form.error'],\n        })\n      }\n    }, [])\n\n    const externalId = options.getExternalId?.()\n    // Phase 4: per-end-user server visibility. Default true unless the project\n    // requires a check; then false until checkVisibility() confirms allowlist\n    // membership. Re-runs whenever the resolved identity changes.\n    const [visibilityAllowed, setVisibilityAllowed] = useState(\n      !options.requiresVisibilityCheck,\n    )\n    useEffect(() => {\n      if (!options.requiresVisibilityCheck) {\n        setVisibilityAllowed(true)\n        return\n      }\n      if (!externalId || !options.checkVisibility) {\n        setVisibilityAllowed(false)\n        return\n      }\n      let cancelled = false\n      options\n        .checkVisibility(externalId)\n        .then((show) => {\n          if (!cancelled) setVisibilityAllowed(show)\n        })\n        .catch(() => {\n          if (!cancelled) setVisibilityAllowed(false)\n        })\n      return () => {\n        cancelled = true\n      }\n    }, [externalId])\n\n    // FAB-visibility gate: showFAB + (identity resolved when the host wired\n    // identity) + (server visibility when the project gates it). Legacy hosts\n    // that never wired identity stay on the old \"showFAB → visible\" path.\n    const fabVisible = computeFabVisible(options, externalId, visibilityAllowed)\n    const showMineTab = Boolean(options.api && externalId)\n\n    return (\n      <>\n        {fabVisible && (\n          <Fab\n            label={options.strings['fab.label']}\n            onClick={() => rerender({ ...currentState, open: true })}\n          />\n        )}\n        {state.open && (\n          <Modal\n            onDismiss={() =>\n              rerender(clearSelected({ ...currentState, open: false, status: 'idle' }))\n            }\n            closeLabel={options.strings['form.close']}\n            expanded={state.tab === 'board'}\n          >\n            {showMineTab && (\n              <div class=\"tab-strip\" role=\"tablist\">\n                <button\n                  type=\"button\"\n                  role=\"tab\"\n                  aria-selected={state.tab === 'send'}\n                  class={`tab-button ${state.tab === 'send' ? 'is-active' : ''}`}\n                  onClick={() =>\n                    rerender(clearSelected({ ...currentState, tab: 'send' }))\n                  }\n                >\n                  {options.strings['tab.send']}\n                </button>\n                <button\n                  type=\"button\"\n                  role=\"tab\"\n                  aria-selected={state.tab === 'mine'}\n                  class={`tab-button ${state.tab === 'mine' ? 'is-active' : ''}`}\n                  onClick={() =>\n                    rerender(clearSelected({ ...currentState, tab: 'mine' }))\n                  }\n                >\n                  {options.strings['tab.mine']}\n                </button>\n                <button\n                  type=\"button\"\n                  role=\"tab\"\n                  aria-selected={state.tab === 'changelog'}\n                  class={`tab-button ${state.tab === 'changelog' ? 'is-active' : ''}`}\n                  onClick={() =>\n                    rerender(clearSelected({ ...currentState, tab: 'changelog' }))\n                  }\n                >\n                  {options.strings['tab.changelog']}\n                </button>\n                <button\n                  type=\"button\"\n                  role=\"tab\"\n                  aria-selected={state.tab === 'board'}\n                  class={`tab-button tab-button--board ${state.tab === 'board' ? 'is-active' : ''}`}\n                  onClick={() =>\n                    rerender(clearSelected({ ...currentState, tab: 'board' }))\n                  }\n                >\n                  {options.strings['tab.board']}\n                </button>\n              </div>\n            )}\n            {state.tab === 'send' && (\n              <Form\n                strings={options.strings}\n                onSubmit={handleSubmit}\n                onCancel={() =>\n                  rerender({ ...currentState, open: false, status: 'idle' })\n                }\n                status={state.status}\n                {...(state.error !== undefined && { errorMessage: state.error })}\n              />\n            )}\n            {state.tab === 'mine' && options.api && externalId && !state.selectedReportId && (\n              <MineList\n                api={options.api}\n                externalId={externalId}\n                strings={options.strings}\n                onSelect={(row) =>\n                  rerender({ ...currentState, selectedReportId: row.id })\n                }\n              />\n            )}\n            {(state.tab === 'mine' || state.tab === 'changelog') &&\n              options.api &&\n              externalId &&\n              state.selectedReportId && (\n                <ReportDetailView\n                  api={options.api}\n                  externalId={externalId}\n                  reportId={state.selectedReportId}\n                  strings={options.strings}\n                  onBack={() =>\n                    rerender(clearSelected({ ...currentState }))\n                  }\n                />\n              )}\n            {state.tab === 'changelog' &&\n              options.api &&\n              externalId &&\n              !state.selectedReportId && (\n                <ChangelogList\n                  api={options.api}\n                  externalId={externalId}\n                  strings={options.strings}\n                  onSelect={(row) =>\n                    rerender({ ...currentState, selectedReportId: row.id })\n                  }\n                />\n              )}\n            {state.tab === 'board' && options.api && externalId && (\n              // BoardView owns its own master/detail navigation — no\n              // selectedReportId routing through the modal-level state.\n              <BoardView\n                api={options.api}\n                externalId={externalId}\n                strings={options.strings}\n              />\n            )}\n          </Modal>\n        )}\n      </>\n    )\n  }\n\n  rerender(currentState)\n\n  return {\n    open() {\n      rerender({ ...currentState, open: true })\n    },\n    close() {\n      rerender({ ...currentState, open: false, status: 'idle' })\n    },\n    dispose() {\n      if (postSubmitTimer !== null) {\n        clearTimeout(postSubmitTimer)\n        postSubmitTimer = null\n      }\n      render(null, mountPoint)\n      options.host.innerHTML = ''\n    },\n    notifyIdentityChanged() {\n      rerender({ ...currentState })\n    },\n  }\n}\n","/**\n * BoardView — the \"Voir tout\" surface (v0.12).\n *\n * Renders the project's reports in a master/detail layout with a KPI\n * strip on top and a filter row below it. Scope follows the project's\n * `share_reports_with_widget` flag server-side; the client just renders\n * what the endpoint returns and uses `is_mine` to permission-gate the\n * detail-pane status buttons.\n *\n * Architecture notes:\n *   - The list polls every 30s while open (same cadence as MineList).\n *     Filters trigger an immediate refetch.\n *   - The KPI strip refetches alongside the list so the counts always\n *     match what the user sees.\n *   - Detail pane is the existing ReportDetailView used by My-reports +\n *     This-week. Permission-gating happens via the `canModerate` prop\n *     (false for project-scoped rows that aren't `is_mine`).\n *   - The whole tab assumes the Modal it's rendered into has been\n *     `expanded` — see Modal.tsx + .modal.is-expanded in styles.ts.\n */\n\nimport { h, type JSX } from 'preact'\nimport { useEffect, useMemo, useState } from 'preact/hooks'\n\nimport type { ApiClient, BoardListPage } from '../api/client'\nimport type {\n  BoardFilters,\n  BoardSortKey,\n  FeedbackSeverity,\n  FeedbackType,\n  ReportStatus,\n  WidgetBoardKpis,\n  WidgetBoardRow,\n} from '../types'\nimport type { StringKey } from './i18n'\nimport { loadBoardView, saveBoardView } from './boardViewStore'\nimport { ReportDetailView } from './ReportDetailView'\n\nconst POLL_MS = 30_000\n\nconst SORT_OPTIONS: BoardSortKey[] = ['recent', 'oldest', 'updated', 'severity', 'status']\n\nconst STATUSES: ReportStatus[] = [\n  'new',\n  'in_progress',\n  'awaiting_validation',\n  'closed',\n  'rejected',\n]\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\ninterface BoardViewProps {\n  api: ApiClient\n  externalId: string\n  strings: Record<StringKey, string>\n}\n\nexport function BoardView({ api, externalId, strings }: BoardViewProps) {\n  const [filters, setFilters] = useState<BoardFilters>(() => loadBoardView(externalId))\n  useEffect(() => {\n    saveBoardView(externalId, filters)\n  }, [externalId, filtersHash(filters)])\n  const [page, setPage] = useState<BoardListPage | null>(null)\n  const [kpis, setKpis] = useState<WidgetBoardKpis | null>(null)\n  const [loading, setLoading] = useState(true)\n  const [error, setError] = useState<string | null>(null)\n  const [selectedId, setSelectedId] = useState<string | null>(null)\n  // Detail-pane mount tick — increments when the user picks a row so the\n  // panel can re-mount and re-fetch without us caching old data.\n  const [detailKey, setDetailKey] = useState(0)\n  const [reloadTick, setReloadTick] = useState(0)\n\n  const activeFilterCount = useMemo(() => {\n    return (\n      (filters.status?.length ?? 0) +\n      (filters.type?.length ?? 0) +\n      (filters.severity?.length ?? 0) +\n      (filters.q ? 1 : 0) +\n      (filters.mine ? 1 : 0)\n    )\n  }, [filters])\n\n  // Single source of truth for list + KPIs — they refetch together so the\n  // counts never drift from the visible rows.\n  useEffect(() => {\n    let cancelled = false\n    let timer: ReturnType<typeof setTimeout> | null = null\n    const tick = async () => {\n      try {\n        const [list, k] = await Promise.all([\n          api.listBoard(externalId, filters),\n          api.fetchBoardKpis(externalId, filters),\n        ])\n        if (cancelled) return\n        setPage(list)\n        setKpis(k)\n        setError(null)\n      } catch (e) {\n        if (cancelled) return\n        setError(e instanceof Error ? e.message : String(e))\n      } finally {\n        if (!cancelled) setLoading(false)\n        if (!cancelled) timer = setTimeout(tick, POLL_MS)\n      }\n    }\n    setLoading(true)\n    void tick()\n    return () => {\n      cancelled = true\n      if (timer) clearTimeout(timer)\n    }\n  }, [api, externalId, filtersHash(filters), reloadTick])\n\n  const selectedRow = useMemo(() => {\n    if (!selectedId || !page) return null\n    return page.results.find((r) => r.id === selectedId) ?? null\n  }, [selectedId, page])\n\n  const onPickRow = (row: WidgetBoardRow) => {\n    setSelectedId(row.id)\n    setDetailKey((k) => k + 1)\n  }\n\n  return (\n    <div class=\"board-view\">\n      <BoardHeader strings={strings} kpis={kpis} />\n      <BoardFilters\n        filters={filters}\n        onChange={setFilters}\n        activeCount={activeFilterCount}\n        strings={strings}\n      />\n      <div class=\"board-body\">\n        <div class=\"board-list-wrap\" aria-busy={loading && !page}>\n          {error && (\n            <div class=\"board-error\" role=\"alert\">\n              <span>{strings['board.list.error']}</span>\n              <button\n                type=\"button\"\n                class=\"btn btn--ghost\"\n                onClick={() => {\n                  setError(null)\n                  setLoading(true)\n                  setReloadTick((t) => t + 1)\n                }}\n              >\n                {strings['board.retry']}\n              </button>\n            </div>\n          )}\n          {!error && loading && !page && (\n            <BoardListSkeleton />\n          )}\n          {!error && page && page.results.length === 0 && !loading && (\n            <BoardEmpty strings={strings} />\n          )}\n          {!error && page && page.results.length > 0 && (\n            <BoardList\n              rows={page.results}\n              selectedId={selectedId}\n              onPick={onPickRow}\n              strings={strings}\n              total={page.count}\n            />\n          )}\n        </div>\n        <div class={`board-detail-wrap ${selectedRow ? 'has-selection' : ''}`}>\n          {selectedRow ? (\n            <ReportDetailView\n              key={detailKey}\n              api={api}\n              externalId={externalId}\n              reportId={selectedRow.id}\n              strings={strings}\n              onBack={() => setSelectedId(null)}\n              canModerate={selectedRow.is_mine}\n              variant=\"board\"\n            />\n          ) : (\n            <div class=\"board-detail-empty\">\n              <DetailEmptyIllustration />\n              <p>{strings['board.detail.empty']}</p>\n            </div>\n          )}\n        </div>\n      </div>\n    </div>\n  )\n}\n\n// ---------- subcomponents -------------------------------------------------\n\nfunction BoardHeader({\n  strings,\n  kpis,\n}: {\n  strings: Record<StringKey, string>\n  kpis: WidgetBoardKpis | null\n}) {\n  const scopeLabel =\n    kpis?.scope === 'project'\n      ? strings['board.scope.project']\n      : strings['board.scope.mine']\n  return (\n    <header class=\"board-header\">\n      <div class=\"board-header-title\">\n        <span class=\"board-header-emoji\" aria-hidden=\"true\">📋</span>\n        <div>\n          <h2 class=\"board-header-h\">{strings['tab.board']}</h2>\n          <p class=\"board-header-sub\">\n            {kpis ? `${kpis.total} · ${scopeLabel}` : scopeLabel}\n          </p>\n        </div>\n      </div>\n      <BoardKpiStrip kpis={kpis} strings={strings} />\n    </header>\n  )\n}\n\nfunction BoardKpiStrip({\n  kpis,\n  strings,\n}: {\n  kpis: WidgetBoardKpis | null\n  strings: Record<StringKey, string>\n}) {\n  // Always render 4 cards so the strip's height is stable while the\n  // first fetch is in flight — eliminates the layout shift the PNR\n  // page has on its first paint.\n  const cells: { key: string; label: string; value: string; tone: string }[] = [\n    {\n      key: 'new',\n      label: strings['board.kpi.new'],\n      value: String(kpis?.by_status?.new ?? 0),\n      tone: 'tone-new',\n    },\n    {\n      key: 'in_progress',\n      label: strings['board.kpi.in_progress'],\n      value: String(kpis?.by_status?.in_progress ?? 0),\n      tone: 'tone-progress',\n    },\n    {\n      key: 'awaiting_validation',\n      label: strings['board.kpi.awaiting_validation'],\n      value: String(kpis?.by_status?.awaiting_validation ?? 0),\n      tone: 'tone-validation',\n    },\n    {\n      key: 'rate',\n      label: strings['board.kpi.resolution_rate'],\n      value: kpis ? `${Math.round((kpis.resolution_rate || 0) * 100)}%` : '—',\n      tone: 'tone-rate',\n    },\n  ]\n  return (\n    <div class={`board-kpi-strip ${kpis ? 'is-ready' : 'is-loading'}`} aria-live=\"polite\">\n      {cells.map((c) => (\n        <div key={c.key} class={`board-kpi-card ${c.tone}`}>\n          <div class=\"board-kpi-value\">{c.value}</div>\n          <div class=\"board-kpi-label\">{c.label}</div>\n        </div>\n      ))}\n    </div>\n  )\n}\n\nfunction BoardFilters({\n  filters,\n  onChange,\n  activeCount,\n  strings,\n}: {\n  filters: BoardFilters\n  onChange: (f: BoardFilters) => void\n  activeCount: number\n  strings: Record<StringKey, string>\n}) {\n  // Under `exactOptionalPropertyTypes: true` we can't set keys to\n  // `undefined` to clear them — the optional slot doesn't accept the\n  // explicit undefined. Drop the key via destructuring instead.\n  const setStatus = (value: ReportStatus | '') => {\n    if (value === '') {\n      const { status: _drop, ...rest } = filters\n      void _drop\n      onChange(rest)\n    } else {\n      onChange({ ...filters, status: [value] })\n    }\n  }\n  const setType = (value: FeedbackType | '') => {\n    if (value === '') {\n      const { type: _drop, ...rest } = filters\n      void _drop\n      onChange(rest)\n    } else {\n      onChange({ ...filters, type: [value] })\n    }\n  }\n  const setSeverity = (value: FeedbackSeverity | '') => {\n    if (value === '') {\n      const { severity: _drop, ...rest } = filters\n      void _drop\n      onChange(rest)\n    } else {\n      onChange({ ...filters, severity: [value] })\n    }\n  }\n  const setOrdering = (value: BoardSortKey) => onChange({ ...filters, ordering: value })\n  const setSearch = (q: string) => onChange({ ...filters, q })\n  const toggleMine = () => {\n    if (filters.mine) {\n      const { mine: _drop, ...rest } = filters\n      void _drop\n      onChange(rest)\n    } else {\n      onChange({ ...filters, mine: true })\n    }\n  }\n  const clear = () => onChange({})\n  return (\n    <div class=\"board-filters\">\n      <select\n        class=\"board-filter-select\"\n        aria-label={strings['board.sort']}\n        value={filters.ordering ?? 'recent'}\n        onChange={(e) => setOrdering((e.target as HTMLSelectElement).value as BoardSortKey)}\n      >\n        {SORT_OPTIONS.map((o) => (\n          <option key={o} value={o}>\n            {strings[`board.sort.${o}` as StringKey]}\n          </option>\n        ))}\n      </select>\n      <select\n        class=\"board-filter-select\"\n        aria-label={strings['board.filter.status']}\n        value={filters.status?.[0] ?? ''}\n        onChange={(e) =>\n          setStatus((e.target as HTMLSelectElement).value as ReportStatus | '')\n        }\n      >\n        <option value=\"\">{strings['board.filter.status']}</option>\n        {STATUSES.map((s) => (\n          <option key={s} value={s}>\n            {strings[`board.kpi.${s}` as StringKey] ?? s}\n          </option>\n        ))}\n      </select>\n      <select\n        class=\"board-filter-select\"\n        aria-label={strings['board.filter.type']}\n        value={filters.type?.[0] ?? ''}\n        onChange={(e) =>\n          setType((e.target as HTMLSelectElement).value as FeedbackType | '')\n        }\n      >\n        <option value=\"\">{strings['board.filter.type']}</option>\n        {TYPES.map((t) => (\n          <option key={t} value={t}>\n            {strings[`type.${t}` as StringKey] ?? t}\n          </option>\n        ))}\n      </select>\n      <select\n        class=\"board-filter-select\"\n        aria-label={strings['board.filter.severity']}\n        value={filters.severity?.[0] ?? ''}\n        onChange={(e) =>\n          setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity | '')\n        }\n      >\n        <option value=\"\">{strings['board.filter.severity']}</option>\n        {SEVERITIES.map((s) => (\n          <option key={s} value={s}>\n            {strings[`severity.${s}` as StringKey] ?? s}\n          </option>\n        ))}\n      </select>\n      <input\n        type=\"search\"\n        class=\"board-filter-search\"\n        placeholder={strings['board.filter.search.placeholder']}\n        value={filters.q ?? ''}\n        onInput={(e) => setSearch((e.target as HTMLInputElement).value)}\n      />\n      <label class=\"board-filter-toggle\">\n        <input\n          type=\"checkbox\"\n          checked={Boolean(filters.mine)}\n          onChange={toggleMine}\n        />\n        <span>{strings['board.filter.mine']}</span>\n      </label>\n      {activeCount > 0 && (\n        <button type=\"button\" class=\"board-filter-clear\" onClick={clear}>\n          <CloseIcon />\n          {strings['board.filter.clear']}\n        </button>\n      )}\n    </div>\n  )\n}\n\nfunction BoardList({\n  rows,\n  selectedId,\n  onPick,\n  strings,\n  total,\n}: {\n  rows: WidgetBoardRow[]\n  selectedId: string | null\n  onPick: (row: WidgetBoardRow) => void\n  strings: Record<StringKey, string>\n  total: number\n}) {\n  return (\n    <>\n      <div class=\"board-list-count\">\n        {strings['board.list.count']\n          .replace('{n}', String(rows.length))\n          .replace('{total}', String(total))}\n      </div>\n      <ul class=\"board-list\" role=\"list\">\n        {rows.map((row) => (\n          <BoardRowCard\n            key={row.id}\n            row={row}\n            selected={row.id === selectedId}\n            onPick={onPick}\n            strings={strings}\n          />\n        ))}\n      </ul>\n    </>\n  )\n}\n\nfunction BoardRowCard({\n  row,\n  selected,\n  onPick,\n  strings,\n}: {\n  row: WidgetBoardRow\n  selected: boolean\n  onPick: (row: WidgetBoardRow) => void\n  strings: Record<StringKey, string>\n}) {\n  const author = row.is_mine\n    ? strings['board.list.you']\n    : row.author_label || '—'\n  return (\n    <li>\n      <button\n        type=\"button\"\n        class={`board-row ${selected ? 'is-selected' : ''}`}\n        onClick={() => onPick(row)}\n        aria-pressed={selected}\n      >\n        <div class=\"board-row-badges\">\n          <span class={`badge status-${row.status}`}>\n            {strings[`status.${row.status}` as StringKey] ?? row.status}\n          </span>\n          <span class={`badge severity-${row.severity}`}>\n            {strings[`severity.${row.severity}` as StringKey] ?? row.severity}\n          </span>\n        </div>\n        <div class=\"board-row-description\">{row.description}</div>\n        <div class=\"board-row-meta\">\n          <span class=\"board-row-author\">{author}</span>\n          {row.comment_count > 0 && (\n            <span class=\"board-row-comments\">\n              <CommentIcon /> {row.comment_count}\n            </span>\n          )}\n          <span class=\"board-row-time\">{formatRelative(row.created_at)}</span>\n        </div>\n      </button>\n    </li>\n  )\n}\n\nfunction BoardEmpty({ strings }: { strings: Record<StringKey, string> }) {\n  return (\n    <div class=\"board-empty\">\n      <EmptyIllustration />\n      <h3>{strings['board.list.empty.title']}</h3>\n      <p>{strings['board.list.empty.description']}</p>\n    </div>\n  )\n}\n\nfunction BoardListSkeleton() {\n  return (\n    <ul class=\"board-list board-list-skeleton\" aria-hidden=\"true\">\n      {Array.from({ length: 5 }).map((_, i) => (\n        <li key={i}>\n          <div class=\"board-row skeleton-row\">\n            <div class=\"skeleton-line skeleton-badges\" />\n            <div class=\"skeleton-line skeleton-text\" />\n            <div class=\"skeleton-line skeleton-text short\" />\n          </div>\n        </li>\n      ))}\n    </ul>\n  )\n}\n\n// ---------- icons ---------------------------------------------------------\n\nfunction CommentIcon(): JSX.Element {\n  return (\n    <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n      <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n    </svg>\n  )\n}\n\nfunction CloseIcon(): JSX.Element {\n  return (\n    <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n      <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n      <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n    </svg>\n  )\n}\n\nfunction EmptyIllustration(): JSX.Element {\n  return (\n    <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n      <circle cx=\"32\" cy=\"32\" r=\"28\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" />\n      <path d=\"M22 32h20M32 22v20\" stroke=\"currentColor\" stroke-opacity=\"0.35\" stroke-width=\"2\" stroke-linecap=\"round\" />\n    </svg>\n  )\n}\n\nfunction DetailEmptyIllustration(): JSX.Element {\n  return (\n    <svg width=\"80\" height=\"80\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n      <rect x=\"10\" y=\"12\" width=\"44\" height=\"36\" rx=\"4\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" />\n      <line x1=\"18\" y1=\"22\" x2=\"46\" y2=\"22\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" stroke-linecap=\"round\" />\n      <line x1=\"18\" y1=\"30\" x2=\"38\" y2=\"30\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" stroke-linecap=\"round\" />\n      <line x1=\"18\" y1=\"38\" x2=\"32\" y2=\"38\" stroke=\"currentColor\" stroke-opacity=\"0.14\" stroke-width=\"2\" stroke-linecap=\"round\" />\n    </svg>\n  )\n}\n\n// ---------- helpers -------------------------------------------------------\n\nfunction filtersHash(f: BoardFilters): string {\n  // Stable serialization for the useEffect dep array — avoids re-running\n  // when the BoardView re-renders with a new object identity but the\n  // same filter values.\n  return JSON.stringify({\n    s: f.status?.slice().sort(),\n    t: f.type?.slice().sort(),\n    sv: f.severity?.slice().sort(),\n    q: f.q ?? '',\n    m: Boolean(f.mine),\n    o: f.ordering ?? '',\n  })\n}\n\nfunction formatRelative(iso: string): string {\n  // Tiny \"5m / 3h / 2d / 5w\" formatter — same shape PNR uses. Doesn't\n  // depend on date-fns; keeps the bundle lean.\n  const then = new Date(iso).getTime()\n  if (Number.isNaN(then)) return ''\n  const delta = Math.max(0, Date.now() - then)\n  const m = Math.floor(delta / 60_000)\n  if (m < 1) return 'just now'\n  if (m < 60) return `${m}m`\n  const h = Math.floor(m / 60)\n  if (h < 24) return `${h}h`\n  const d = Math.floor(h / 24)\n  if (d < 7) return `${d}d`\n  const w = Math.floor(d / 7)\n  return `${w}w`\n}\n","import type { BoardFilters } from '../types'\n\n// Per-user persisted Board view (sort + filters). A widget instance is\n// scoped to one project, so the external id is a sufficient key. Best-\n// effort: storage may be disabled or hold a stale/corrupt blob — every\n// path falls back to an empty view rather than throwing into render.\nconst PREFIX = 'mhosaic.boardView.'\n\nexport function loadBoardView(externalId: string): BoardFilters {\n  try {\n    const raw = localStorage.getItem(PREFIX + externalId)\n    if (!raw) return {}\n    const parsed = JSON.parse(raw)\n    return parsed && typeof parsed === 'object' ? (parsed as BoardFilters) : {}\n  } catch {\n    return {}\n  }\n}\n\nexport function saveBoardView(externalId: string, view: BoardFilters): void {\n  try {\n    localStorage.setItem(PREFIX + externalId, JSON.stringify(view))\n  } catch {\n    /* storage disabled / quota — non-fatal, just no persistence */\n  }\n}\n","/**\n * ReportDetailView — selected report's full thread.\n *\n * Renders: status pill + description + screenshot + comment thread +\n * compose box + (when status=awaiting_validation) \"Mark as resolved\"\n * button. Polls every 30 s while open so the user sees an operator's\n * reply without manually refreshing.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type {\n  CapturedContext,\n  ConsoleEntry,\n  ErrorEntry,\n  NetworkEntry,\n  WidgetCommentRow,\n  WidgetReportDetail,\n  WidgetStatusChangeRow,\n} from '../types'\nimport { CommentBubble } from './CommentBubble'\nimport type { StringKey } from './i18n'\nimport { safeExternalHref, truncateUrl } from './screenshot-utils'\n\ninterface ReportDetailViewProps {\n  api: ApiClient\n  externalId: string\n  reportId: string\n  strings: Record<StringKey, string>\n  onBack: () => void\n  /** When false, the self-service \"Confirm resolution\" button is hidden\n   *  even if the report is in awaiting_validation. The Board passes false\n   *  for project-scoped rows that don't belong to the current user. */\n  canModerate?: boolean\n  /** Layout flavor. \"modal\" (default) keeps the original My-reports look —\n   *  scrollable card centered in the modal. \"board\" tightens the padding\n   *  + drops the top \"← back\" because the Board renders the back action\n   *  in its own header. */\n  variant?: 'modal' | 'board'\n}\n\nconst POLL_MS = 30_000\n\nexport function ReportDetailView({\n  api,\n  externalId,\n  reportId,\n  strings,\n  onBack,\n  canModerate = true,\n  variant = 'modal',\n}: ReportDetailViewProps) {\n  const [detail, setDetail] = useState<WidgetReportDetail | null>(null)\n  const [error, setError] = useState<string | null>(null)\n  const [composeBody, setComposeBody] = useState('')\n  const [sending, setSending] = useState(false)\n  const [closing, setClosing] = useState(false)\n  const [reopening, setReopening] = useState(false)\n  const mountedRef = useRef(true)\n\n  const fetchDetail = async () => {\n    try {\n      const next = await api.getReport(reportId, externalId)\n      if (!mountedRef.current) return\n      setDetail(next)\n      setError(null)\n    } catch (err) {\n      // The verbatim `err.message` (e.g. `getReport failed: 404 …`) leaked\n      // straight into the panel pre-v0.15.3. Friendly UI via the\n      // `!detail` empty-state branch below; raw error to console.\n      if (typeof console !== 'undefined') console.warn('[mhosaic] getReport:', err)\n      if (!mountedRef.current) return\n      // `load_failed` is a sentinel — the render branch swaps to the\n      // friendly empty-state UI when `error` is truthy and `detail` is\n      // null. The string itself never reaches the DOM.\n      setError('load_failed')\n    }\n  }\n\n  useEffect(() => {\n    mountedRef.current = true\n    void fetchDetail()\n    const timer = setInterval(() => {\n      void fetchDetail()\n    }, POLL_MS)\n    return () => {\n      mountedRef.current = false\n      clearInterval(timer)\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [reportId, externalId])\n\n  const handleSend = async () => {\n    if (!composeBody.trim() || sending) return\n    setSending(true)\n    try {\n      const nonce = `${reportId}:${Date.now()}`\n      const created = await api.addComment(reportId, externalId, composeBody.trim(), nonce)\n      if (!mountedRef.current) return\n      setComposeBody('')\n      // Optimistically append + then refetch so server-rendered timestamps\n      // and any race with operator replies stay coherent.\n      setDetail((prev) =>\n        prev ? { ...prev, comments: appendComment(prev.comments, created) } : prev,\n      )\n      void fetchDetail()\n    } catch (err) {\n      // Don't surface raw API messages (e.g. `addComment failed: 403 …`).\n      // Friendly fallback in the UI, raw error in the console.\n      if (typeof console !== 'undefined') console.warn('[mhosaic] addComment:', err)\n      if (!mountedRef.current) return\n      setError(strings['detail.send_failed'])\n    } finally {\n      if (mountedRef.current) setSending(false)\n    }\n  }\n\n  const handleClose = async () => {\n    if (closing || reopening) return\n    setClosing(true)\n    try {\n      const next = await api.closeAsResolved(reportId, externalId)\n      if (!mountedRef.current) return\n      setDetail(next)\n    } catch (err) {\n      if (typeof console !== 'undefined')\n        console.warn('[mhosaic] closeAsResolved:', err)\n      if (!mountedRef.current) return\n      setError(strings['detail.close_failed'])\n    } finally {\n      if (mountedRef.current) setClosing(false)\n    }\n  }\n\n  const handleReopen = async () => {\n    if (closing || reopening) return\n    setReopening(true)\n    try {\n      const next = await api.reopenUnresolved(reportId, externalId)\n      if (!mountedRef.current) return\n      setDetail(next)\n    } catch (err) {\n      if (typeof console !== 'undefined')\n        console.warn('[mhosaic] reopenUnresolved:', err)\n      if (!mountedRef.current) return\n      setError(strings['detail.reopen_failed'])\n    } finally {\n      if (mountedRef.current) setReopening(false)\n    }\n  }\n\n  if (!detail && !error) {\n    return <div class=\"mine-loading\">{strings['mine.loading']}</div>\n  }\n\n  if (!detail) {\n    // The raw `error.message` from the api client used to render here as\n    // e.g. `getReport failed: 404 {\"detail\":\"Report not found.\"}` — a\n    // verbatim leak of the backend error. The friendly variant + a Back\n    // button is what end users (and external testers) expect to see.\n    return (\n      <div class=\"report-detail-empty-state\" role=\"alert\">\n        <p class=\"report-detail-empty-state-title\">\n          {strings['detail.load_failed.title']}\n        </p>\n        <p class=\"report-detail-empty-state-body\">\n          {strings['detail.load_failed.body']}\n        </p>\n        <button type=\"button\" class=\"btn\" onClick={onBack}>\n          ← {strings['detail.load_failed.cta']}\n        </button>\n      </div>\n    )\n  }\n\n  // Permission gating. `canModerate` is passed by the parent (Board\n  // passes `is_mine`; My-reports defaults to true). v0.15.3 also reads\n  // `detail.is_mine` from the server when present, so a direct fetch\n  // (no parent context) still gates correctly.\n  const isMine = detail.is_mine ?? canModerate\n  // \"Mark as resolved\" is the only self-service status transition\n  // the widget allows (awaiting_validation → closed).\n  const showCloseCta = isMine && detail.status === 'awaiting_validation'\n  // Reply compose box is submitter-only. Replies are private convo with\n  // the operator team, even when the report itself is project-visible\n  // via `share_reports_with_widget`. Hidden (not just disabled) when\n  // not the submitter, since the action is genuinely unavailable.\n  const showComposeBox = isMine\n\n  return (\n    <div class={`report-detail variant-${variant}`}>\n      {variant === 'modal' && (\n        <div class=\"report-detail-header\">\n          <button type=\"button\" class=\"btn\" onClick={onBack}>\n            ← {strings['detail.back']}\n          </button>\n          <span class={`pill pill-status pill-status--${detail.status}`}>\n            {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n          </span>\n        </div>\n      )}\n      {variant === 'board' && (\n        <div class=\"report-detail-header report-detail-header--board\">\n          <button\n            type=\"button\"\n            class=\"btn btn--ghost report-detail-back\"\n            onClick={onBack}\n            aria-label={strings['board.back']}\n          >\n            ← {strings['board.back']}\n          </button>\n          <span class={`pill pill-status pill-status--${detail.status}`}>\n            {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n          </span>\n        </div>\n      )}\n\n      <div class=\"report-detail-body\">\n        <ContextBlock detail={detail} strings={strings} />\n        <p class=\"report-detail-description\">{detail.description}</p>\n        {detail.screenshot_url && (() => {\n          const safeHref = safeExternalHref(detail.screenshot_url)\n          // No clickable open-in-new-tab for non-http(s) screenshot_url\n          // (defense-in-depth — server returns presigned https URLs, but\n          // we don't trust client-rendered content with the operator's\n          // origin). The inline preview is `&lt;img src&gt;`, which is safe\n          // even if the URL is weird because Preact escapes attrs and\n          // the browser refuses to render non-image content.\n          return safeHref ? (\n            <a\n              class=\"report-detail-screenshot\"\n              href={safeHref}\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n            >\n              <img src={detail.screenshot_url} alt=\"\" loading=\"lazy\" />\n            </a>\n          ) : (\n            <div class=\"report-detail-screenshot\">\n              <img src={detail.screenshot_url} alt=\"\" loading=\"lazy\" />\n            </div>\n          )\n        })()}\n\n        <h3 class=\"report-detail-section\">{strings['detail.thread']}</h3>\n        {detail.comments.length === 0 ? (\n          <p class=\"report-detail-empty\">{strings['detail.no_replies']}</p>\n        ) : (\n          <ul class=\"report-comments\">\n            {detail.comments.map((c) => (\n              <li>\n                <CommentBubble comment={c} strings={strings} />\n              </li>\n            ))}\n          </ul>\n        )}\n\n        {detail.status_history && detail.status_history.length > 0 && (\n          <StatusHistorySection rows={detail.status_history} strings={strings} />\n        )}\n\n        {detail.technical_context && (\n          <TechnicalContextDrawer ctx={detail.technical_context} strings={strings} />\n        )}\n\n        {showComposeBox ? (\n          <div class=\"report-compose\">\n            <textarea\n              value={composeBody}\n              placeholder={strings['detail.compose_placeholder']}\n              onInput={(e) =>\n                setComposeBody((e.target as HTMLTextAreaElement).value)\n              }\n              disabled={sending}\n            />\n            <div class=\"report-compose-actions\">\n              {showCloseCta && (\n                <button\n                  type=\"button\"\n                  class=\"btn\"\n                  onClick={handleClose}\n                  disabled={closing || reopening}\n                >\n                  {closing\n                    ? strings['detail.close_busy']\n                    : strings['detail.close_cta']}\n                </button>\n              )}\n              {showCloseCta && (\n                <button\n                  type=\"button\"\n                  class=\"btn btn--ghost\"\n                  onClick={handleReopen}\n                  disabled={closing || reopening}\n                >\n                  {reopening\n                    ? strings['detail.reopen_busy']\n                    : strings['detail.reopen_cta']}\n                </button>\n              )}\n              <button\n                type=\"button\"\n                class=\"btn btn--primary\"\n                onClick={handleSend}\n                disabled={!composeBody.trim() || sending}\n              >\n                {sending\n                  ? strings['detail.compose_sending']\n                  : strings['detail.compose_send']}\n              </button>\n            </div>\n          </div>\n        ) : (\n          // Teammate is reading a project-wide row. Replies are private\n          // to the submitter, so we hide the compose box entirely (action\n          // is genuinely unavailable) and surface a short notice so the\n          // viewer understands why.\n          <p class=\"report-detail-teammate-notice\" role=\"note\">\n            {strings['detail.teammate_notice']}\n          </p>\n        )}\n\n        {error && <div class=\"error\">{error}</div>}\n      </div>\n    </div>\n  )\n}\n\nfunction appendComment(\n  current: WidgetCommentRow[],\n  next: WidgetCommentRow,\n): WidgetCommentRow[] {\n  if (current.some((c) => c.id === next.id)) return current\n  return [...current, next]\n}\n\ninterface ContextBlockProps {\n  detail: WidgetReportDetail\n  strings: Record<StringKey, string>\n}\n\n/**\n * The original-context block that sits above the description on the\n * detail page. Surfaces what the user originally reported (type +\n * severity at submit time, page they were on, when they sent it,\n * how the screenshot was captured) so they can recall the report\n * without having to re-derive it from the description.\n */\nfunction ContextBlock({ detail, strings }: ContextBlockProps) {\n  const captureKey: StringKey = `detail.context.capture.${detail.capture_method}` as StringKey\n  const captureLabel = strings[captureKey] ?? detail.capture_method\n  return (\n    <div class=\"report-detail-context\">\n      <div class=\"report-detail-context-pills\">\n        <span class=\"pill pill-type\">{strings[`type.${detail.feedback_type}` as StringKey]}</span>\n        <span class={`pill pill-severity pill-severity--${detail.severity}`}>\n          {strings[`severity.${detail.severity}` as StringKey]}\n        </span>\n        <span class=\"pill pill-capture\">{captureLabel}</span>\n      </div>\n      <div class=\"report-detail-context-line\">\n        <span class=\"report-detail-context-label\">{strings['detail.context.submitted_at']}</span>\n        <span>{formatSubmittedAt(detail.created_at)}</span>\n      </div>\n      {detail.page_url && (() => {\n        const safeHref = safeExternalHref(detail.page_url)\n        return (\n          <div class=\"report-detail-context-line\" title={detail.page_url}>\n            <span class=\"report-detail-context-label\">{strings['detail.context.page']}</span>\n            {safeHref ? (\n              <a\n                class=\"report-detail-context-url\"\n                href={safeHref}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n              >\n                {truncateUrl(detail.page_url, 64)}\n              </a>\n            ) : (\n              <span class=\"report-detail-context-url\">{truncateUrl(detail.page_url, 64)}</span>\n            )}\n          </div>\n        )\n      })()}\n    </div>\n  )\n}\n\nfunction formatSubmittedAt(iso: string): string {\n  try {\n    return new Date(iso).toLocaleString(undefined, {\n      dateStyle: 'medium',\n      timeStyle: 'short',\n    })\n  } catch {\n    return iso\n  }\n}\n\ninterface TechnicalContextDrawerProps {\n  ctx: CapturedContext\n  strings: Record<StringKey, string>\n}\n\nconst TECH_CONSOLE_LIMIT = 20\nconst TECH_NETWORK_LIMIT = 15\n\n/**\n * The \"transparency drawer\" — collapsible panel showing the user the\n * exact diagnostic data their browser submitted with the report (console\n * tail, network tail, JS errors, device summary). Closed by default so\n * the regular conversation flow stays uncluttered; one click reveals\n * everything. Borrowed from thePnr's customer-facing `TechnicalContextBlock`.\n */\nfunction TechnicalContextDrawer({ ctx, strings }: TechnicalContextDrawerProps) {\n  const errors = ctx.errors ?? []\n  const consoleLogs = (ctx.consoleLogs ?? []).slice(-TECH_CONSOLE_LIMIT)\n  const network = (ctx.networkRequests ?? []).slice(-TECH_NETWORK_LIMIT)\n  const device = ctx.device\n  const hasAny =\n    !!device || errors.length > 0 || consoleLogs.length > 0 || network.length > 0\n  if (!hasAny) return null\n  const errorCount = errors.length\n  const summary =\n    errorCount > 0\n      ? `${strings['detail.tech.title']} · ${errorCount} ${\n          errorCount === 1 ? strings['detail.tech.errors_one'] : strings['detail.tech.errors_many']\n        }`\n      : strings['detail.tech.title']\n  return (\n    <details class=\"report-detail-tech\">\n      <summary>{summary}</summary>\n      <div class=\"tech-body\">\n        {device && <DeviceSection device={device} strings={strings} />}\n        {errors.length > 0 && <ErrorsSection errors={errors} strings={strings} />}\n        {consoleLogs.length > 0 && <ConsoleSection logs={consoleLogs} strings={strings} />}\n        {network.length > 0 && <NetworkSection rows={network} strings={strings} />}\n      </div>\n    </details>\n  )\n}\n\nfunction DeviceSection({\n  device,\n  strings,\n}: {\n  device: CapturedContext['device']\n  strings: Record<StringKey, string>\n}) {\n  const parts: Array<{ label: string; value: string }> = []\n  if (device?.viewport) {\n    const dpr = device.viewport.dpr ? ` @${device.viewport.dpr}x` : ''\n    parts.push({\n      label: strings['detail.tech.device.viewport'],\n      value: `${device.viewport.w}×${device.viewport.h}${dpr}`,\n    })\n  }\n  if (device?.platform) parts.push({ label: strings['detail.tech.device.platform'], value: device.platform })\n  if (device?.language) parts.push({ label: strings['detail.tech.device.language'], value: device.language })\n  if (device?.timezone) parts.push({ label: strings['detail.tech.device.timezone'], value: device.timezone })\n  if (device?.connection) parts.push({ label: strings['detail.tech.device.connection'], value: device.connection })\n  if (parts.length === 0) return null\n  return (\n    <div class=\"tech-section\">\n      <h4>{strings['detail.tech.device']}</h4>\n      <dl class=\"tech-device\">\n        {parts.map((p) => (\n          <>\n            <dt>{p.label}</dt>\n            <dd>{p.value}</dd>\n          </>\n        ))}\n      </dl>\n    </div>\n  )\n}\n\nfunction ErrorsSection({\n  errors,\n  strings,\n}: {\n  errors: ErrorEntry[]\n  strings: Record<StringKey, string>\n}) {\n  return (\n    <div class=\"tech-section\">\n      <h4>{strings['detail.tech.errors']}</h4>\n      <ul class=\"tech-errors\">\n        {errors.map((e) => (\n          <li>\n            <div class=\"tech-errors-msg\">{e.message}</div>\n            {e.stack && <pre class=\"tech-errors-stack\">{truncateStack(e.stack)}</pre>}\n          </li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nfunction ConsoleSection({\n  logs,\n  strings,\n}: {\n  logs: ConsoleEntry[]\n  strings: Record<StringKey, string>\n}) {\n  return (\n    <div class=\"tech-section\">\n      <h4>{strings['detail.tech.console']}</h4>\n      <ul class=\"tech-console\">\n        {logs.map((l) => (\n          <li class={`tech-console-row tech-console-row--${l.level}`}>\n            <span class=\"tech-console-level\">{l.level}</span>\n            <span class=\"tech-console-msg\">{l.message}</span>\n          </li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nfunction NetworkSection({\n  rows,\n  strings,\n}: {\n  rows: NetworkEntry[]\n  strings: Record<StringKey, string>\n}) {\n  return (\n    <div class=\"tech-section\">\n      <h4>{strings['detail.tech.network']}</h4>\n      <ul class=\"tech-network\">\n        {rows.map((n) => {\n          const failed = n.status >= 400 || n.status === 0\n          return (\n            <li class={`tech-network-row${failed ? ' tech-network-row--fail' : ''}`}>\n              <span class=\"tech-network-status\">{n.status || '—'}</span>\n              <span class=\"tech-network-method\">{n.method}</span>\n              <span class=\"tech-network-url\" title={n.url}>\n                {truncateUrl(n.url, 56)}\n              </span>\n              <span class=\"tech-network-time\">{Math.round(n.durationMs)}ms</span>\n            </li>\n          )\n        })}\n      </ul>\n    </div>\n  )\n}\n\nfunction truncateStack(stack: string): string {\n  // Clip to first 12 frames so the drawer doesn't blow out vertically on\n  // a deep React stack — the user can always click through to a full view\n  // if we add one later.\n  const lines = stack.split('\\n')\n  if (lines.length <= 12) return stack\n  return lines.slice(0, 12).join('\\n') + '\\n  …'\n}\n\ninterface StatusHistorySectionProps {\n  rows: WidgetStatusChangeRow[]\n  strings: Record<StringKey, string>\n}\n\n/**\n * The full audit trail of who changed the report's status, when, and via\n * which surface (operator UI, MCP agent, or system). Read-only — purely\n * a transparency / \"you're being heard by real humans\" gesture borrowed\n * from thePnr's customer-facing detail view.\n */\nfunction StatusHistorySection({ rows, strings }: StatusHistorySectionProps) {\n  return (\n    <div class=\"report-detail-history\">\n      <h3 class=\"report-detail-section\">{strings['detail.history']}</h3>\n      <ol class=\"status-history\">\n        {rows.map((r) => {\n          const from = strings[`status.${r.from_status}` as StringKey] ?? r.from_status\n          const to = strings[`status.${r.to_status}` as StringKey] ?? r.to_status\n          const sourceKey: StringKey =\n            r.changed_by_source === 'mcp'\n              ? 'detail.author.mcp'\n              : r.changed_by_source === 'system'\n              ? 'detail.author.system'\n              : 'detail.author.staff'\n          return (\n            <li class=\"status-history-row\">\n              <span class=\"status-history-time\">{formatSubmittedAt(r.created_at)}</span>\n              <span class=\"status-history-transition\">\n                <span class={`pill pill-status pill-status--${r.from_status}`}>{from}</span>\n                <span class=\"status-history-arrow\" aria-hidden=\"true\">→</span>\n                <span class={`pill pill-status pill-status--${r.to_status}`}>{to}</span>\n              </span>\n              <span class={`status-history-source status-history-source--${r.changed_by_source}`}>\n                {strings[sourceKey]}\n              </span>\n            </li>\n          )\n        })}\n      </ol>\n    </div>\n  )\n}\n","/**\n * CommentBubble — single comment rendered inside the report detail.\n *\n * USER comments float right (the user's own follow-ups). MCP / STAFF /\n * SYSTEM float left and pick up an author label so you can tell at a\n * glance who wrote what. The \"Mhosaic Team\" label for MCP comments\n * matches PNR's pattern; hosts can hide it via a project flag (future).\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetCommentRow } from '../types'\nimport type { StringKey } from './i18n'\n\ninterface CommentBubbleProps {\n  comment: WidgetCommentRow\n  strings: Record<StringKey, string>\n}\n\nexport function CommentBubble({ comment, strings }: CommentBubbleProps) {\n  // The submitter's own follow-ups float right; everything else (operator,\n  // agent, system) floats left with an attribution label. Both submitter\n  // and operator comments arrive as `author_source = USER` from the\n  // backend, so we rely on the server-computed `is_mine` flag to tell\n  // them apart instead of guessing from the source enum.\n  const isMine = comment.is_mine\n  const isAgent = !isMine && comment.author_source === 'mcp'\n  const isSystem = !isMine && comment.author_source === 'system'\n  const variant: 'mcp' | 'system' | 'staff' = isAgent\n    ? 'mcp'\n    : isSystem\n      ? 'system'\n      : 'staff'\n  const labelKey: StringKey =\n    variant === 'mcp'\n      ? 'detail.author.mcp'\n      : variant === 'system'\n        ? 'detail.author.system'\n        : 'detail.author.staff'\n  const label = comment.author_label || strings[labelKey]\n  return (\n    <div class={`comment-bubble ${isMine ? 'is-mine' : 'is-other'}`}>\n      {!isMine && label && (\n        <div class={`comment-author comment-author--${variant}`}>{label}</div>\n      )}\n      <div class=\"comment-body\">{comment.body}</div>\n      <div class=\"comment-time\">{formatTime(comment.created_at)}</div>\n    </div>\n  )\n}\n\nfunction formatTime(iso: string): string {\n  try {\n    return new Date(iso).toLocaleString(undefined, {\n      dateStyle: 'short',\n      timeStyle: 'short',\n    })\n  } catch {\n    return iso\n  }\n}\n","/**\n * Helpers for the manual-screenshot-upload UX (v0.6.0).\n *\n * Kept tiny and dependency-free so they can be unit-tested without DOM/jsdom\n * setup. The interesting work — drag/drop, paste, file picker — lives in\n * Form.tsx.\n */\n\nexport const ALLOWED_IMAGE_TYPES = [\n  'image/png',\n  'image/jpeg',\n  'image/webp',\n] as const\n\nexport const MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024 // 10 MB\n\nexport type ScreenshotValidationError =\n  | { kind: 'type' }\n  | { kind: 'size'; maxMb: number }\n\nexport function validateScreenshotFile(file: File): ScreenshotValidationError | null {\n  if (\n    !ALLOWED_IMAGE_TYPES.includes(\n      file.type as (typeof ALLOWED_IMAGE_TYPES)[number],\n    )\n  ) {\n    return { kind: 'type' }\n  }\n  if (file.size > MAX_SCREENSHOT_BYTES) {\n    return { kind: 'size', maxMb: MAX_SCREENSHOT_BYTES / (1024 * 1024) }\n  }\n  return null\n}\n\n/**\n * Truncate a URL while keeping the start and end visible. Used in the form's\n * \"Vous étiez ici\" footer so the user can verify the URL we'll send without\n * the long path overflowing the modal.\n *\n *   \"https://app.example.com/very/long/deep/path?a=1&b=2\"  (53 chars, max 30)\n *   →  \"https://app.examp…h?a=1&b=2\"\n */\nexport function truncateUrl(url: string, maxLength = 80): string {\n  if (url.length <= maxLength) return url\n  const keepStart = Math.floor((maxLength - 1) / 2)\n  const keepEnd = maxLength - 1 - keepStart\n  return `${url.slice(0, keepStart)}…${url.slice(url.length - keepEnd)}`\n}\n\n/**\n * Return `url` only if its scheme is in the safe-for-href allowlist —\n * `https:` always, `http:` for localhost only. Otherwise return `undefined`,\n * which renders as a non-clickable element (and React/Preact omit the href\n * attribute entirely). This stops a malicious widget user from planting\n * `javascript:`, `data:`, `vbscript:`, or `blob:` URLs in their report's\n * `page_url` / `screenshot_url` and getting an operator's click to execute\n * arbitrary code in the widget host's origin.\n */\nexport function safeExternalHref(url: string | null | undefined): string | undefined {\n  if (!url) return undefined\n  let parsed: URL\n  try {\n    parsed = new URL(url)\n  } catch {\n    return undefined\n  }\n  if (parsed.protocol === 'https:') return parsed.toString()\n  if (\n    parsed.protocol === 'http:' &&\n    (parsed.hostname === 'localhost' ||\n      parsed.hostname === '127.0.0.1' ||\n      parsed.hostname === '[::1]')\n  ) {\n    return parsed.toString()\n  }\n  return undefined\n}\n\n","/**\n * ChangelogList — the \"This week\" tab.\n *\n * Renders the user's resolved reports grouped by ISO week of resolution\n * date, most recent week first. Trust-building surface borrowed from\n * thePnr's `FeedbackChangelogView` (\"see what's been fixed\"). Polls every\n * 30 s while open, same cadence as MineList.\n *\n * Scope is the submitter's own resolved reports only — multi-tenant\n * privacy default. A per-project \"show all-project changelog to\n * customers\" setting can expand the scope in a later release.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetChangelogRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { ReportRow } from './ReportRow'\n\ninterface ChangelogListProps {\n  api: ApiClient\n  externalId: string\n  strings: Record<StringKey, string>\n  onSelect: (report: WidgetChangelogRow) => void\n}\n\nconst POLL_MS = 30_000\n\ninterface WeekGroup {\n  /** ISO date (YYYY-MM-DD) of the Monday anchoring this week. */\n  weekKey: string\n  /** Localized \"Week of {Mon DD, YYYY}\" label, computed once per group. */\n  label: string\n  rows: WidgetChangelogRow[]\n}\n\n/**\n * Week-of-resolution key for a row. Anchored on Monday in UTC so the\n * grouping is consistent across timezones — the user-visible label is\n * localized at render time.\n */\nexport function isoWeekKey(iso: string): string {\n  const d = new Date(iso)\n  if (Number.isNaN(d.getTime())) return ''\n  // JS Sunday=0; we want Monday=0 to anchor weeks the way thePnr does.\n  const day = (d.getUTCDay() + 6) % 7\n  const monday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - day))\n  return monday.toISOString().slice(0, 10)\n}\n\nexport function groupRowsByWeek(rows: WidgetChangelogRow[]): WeekGroup[] {\n  const buckets = new Map<string, WidgetChangelogRow[]>()\n  for (const row of rows) {\n    const key = isoWeekKey(row.resolved_at)\n    if (!key) continue\n    const existing = buckets.get(key)\n    if (existing) existing.push(row)\n    else buckets.set(key, [row])\n  }\n  // Sort buckets by week descending (most recent week first); within each\n  // bucket the server already returned -resolved_at order.\n  return Array.from(buckets.entries())\n    .sort(([a], [b]) => (a < b ? 1 : -1))\n    .map(([weekKey, weekRows]) => ({\n      weekKey,\n      label: formatWeekLabel(weekKey),\n      rows: weekRows,\n    }))\n}\n\nfunction formatWeekLabel(weekKey: string): string {\n  // Render just the date portion (\"May 5, 2026\"); the i18n template\n  // wraps it as \"Week of {date}\".\n  try {\n    return new Date(weekKey).toLocaleDateString(undefined, {\n      year: 'numeric',\n      month: 'short',\n      day: 'numeric',\n    })\n  } catch {\n    return weekKey\n  }\n}\n\nexport function ChangelogList({ api, externalId, strings, onSelect }: ChangelogListProps) {\n  const [rows, setRows] = useState<WidgetChangelogRow[] | null>(null)\n  const [error, setError] = useState<string | null>(null)\n  const [refreshing, setRefreshing] = useState(false)\n  const mountedRef = useRef(true)\n\n  const fetchRows = async () => {\n    setRefreshing(true)\n    setError(null)\n    try {\n      const next = await api.listChangelog(externalId)\n      if (!mountedRef.current) return\n      setRows(next)\n    } catch (err) {\n      // Friendly UI fallback; raw error to console for the developer.\n      if (typeof console !== 'undefined')\n        console.warn('[mhosaic] listChangelog:', err)\n      if (!mountedRef.current) return\n      setError(strings['mine.error'])\n    } finally {\n      if (mountedRef.current) setRefreshing(false)\n    }\n  }\n\n  useEffect(() => {\n    mountedRef.current = true\n    void fetchRows()\n    const timer = setInterval(() => {\n      void fetchRows()\n    }, POLL_MS)\n    return () => {\n      mountedRef.current = false\n      clearInterval(timer)\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [externalId])\n\n  const groups = useMemo(() => (rows ? groupRowsByWeek(rows) : []), [rows])\n  const isLoading = rows === null && !error\n  const isEmpty = rows !== null && rows.length === 0\n\n  return (\n    <div class=\"mine-list\">\n      <div class=\"mine-list-header\">\n        <h2>{strings['tab.changelog']}</h2>\n        <button\n          type=\"button\"\n          class=\"btn\"\n          onClick={() => {\n            void fetchRows()\n          }}\n          disabled={refreshing}\n        >\n          {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n        </button>\n      </div>\n      {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n      {error && <div class=\"error\">{error}</div>}\n      {isEmpty && (\n        <div class=\"mine-empty\">\n          <strong>{strings['changelog.empty.title']}</strong>\n          <p>{strings['changelog.empty.body']}</p>\n        </div>\n      )}\n      {groups.length > 0 && (\n        <div class=\"changelog-groups\">\n          {groups.map((g) => (\n            <section class=\"changelog-group\" key={g.weekKey}>\n              <header class=\"changelog-group-header\">\n                <span class=\"changelog-group-marker\" aria-hidden=\"true\">●</span>\n                <span class=\"changelog-group-label\">\n                  {strings['changelog.week_of'].replace('{date}', g.label)}\n                </span>\n                <span class=\"changelog-group-rule\" aria-hidden=\"true\" />\n                <span class=\"changelog-group-count\">\n                  {strings[\n                    g.rows.length === 1 ? 'changelog.resolved_one' : 'changelog.resolved_many'\n                  ].replace('{count}', String(g.rows.length))}\n                </span>\n              </header>\n              <ul class=\"mine-rows\">\n                {g.rows.map((row) => (\n                  <li>\n                    <ReportRow row={row} strings={strings} onClick={() => onSelect(row)} />\n                  </li>\n                ))}\n              </ul>\n            </section>\n          ))}\n        </div>\n      )}\n    </div>\n  )\n}\n","/**\n * ReportRow — single list item in MineList.\n *\n * Shows status / type / severity badges, a one-line description preview,\n * a relative timestamp, and a \"N replies\" hint pulled from the\n * comment_count annotation on the backend. Whole row is the click\n * target so keyboard nav (Tab + Enter) works.\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\ninterface ReportRowProps {\n  row: WidgetReportRow\n  strings: Record<StringKey, string>\n  onClick: () => void\n}\n\nfunction statusClassName(status: string): string {\n  return `pill pill-status pill-status--${status}`\n}\n\nfunction severityClassName(severity: string): string {\n  return `pill pill-severity pill-severity--${severity}`\n}\n\nfunction typeClassName(): string {\n  return 'pill pill-type'\n}\n\nfunction formatRelative(iso: string): string {\n  const then = Date.parse(iso)\n  if (!Number.isFinite(then)) return ''\n  const seconds = Math.max(1, Math.round((Date.now() - then) / 1000))\n  if (seconds < 60) return `${seconds}s`\n  const minutes = Math.round(seconds / 60)\n  if (minutes < 60) return `${minutes}m`\n  const hours = Math.round(minutes / 60)\n  if (hours < 48) return `${hours}h`\n  const days = Math.round(hours / 24)\n  return `${days}d`\n}\n\nfunction repliesLabel(count: number, strings: Record<StringKey, string>): string {\n  if (count === 1) return strings['mine.replies_one']\n  return strings['mine.replies_many'].replace('{count}', String(count))\n}\n\nexport function ReportRow({ row, strings, onClick }: ReportRowProps) {\n  const preview =\n    row.description.length > 120\n      ? row.description.slice(0, 117) + '…'\n      : row.description\n  return (\n    <button type=\"button\" class=\"mine-row\" onClick={onClick}>\n      <div class=\"mine-row-pills\">\n        <span class={statusClassName(row.status)}>{strings[`status.${row.status}` as StringKey] ?? row.status}</span>\n        <span class={typeClassName()}>{strings[`type.${row.feedback_type}` as StringKey]}</span>\n        <span class={severityClassName(row.severity)}>{strings[`severity.${row.severity}` as StringKey]}</span>\n      </div>\n      <div class=\"mine-row-preview\">{preview}</div>\n      <div class=\"mine-row-meta\">\n        <span>{formatRelative(row.updated_at || row.created_at)}</span>\n        {row.comment_count > 0 && <span>· {repliesLabel(row.comment_count, strings)}</span>}\n      </div>\n    </button>\n  )\n}\n","import { h } from 'preact'\n\ninterface FabProps {\n  label: string\n  onClick: () => void\n}\n\n/**\n * Bug glyph from lucide.dev (`Bug` icon), inlined as a single-color SVG.\n * Sized at 20×20 inside the 48×48 FAB so the icon reads as a confident\n * accent rather than dominating the circle (vs the prior 24/56 ratio,\n * which felt heavy). Picks up `currentColor` from `--mfb-fab-icon`\n * set on the .fab class.\n */\nfunction BugIcon() {\n  return (\n    <svg\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      stroke-width=\"2\"\n      stroke-linecap=\"round\"\n      stroke-linejoin=\"round\"\n      aria-hidden=\"true\"\n      focusable=\"false\"\n    >\n      <path d=\"m8 2 1.88 1.88\" />\n      <path d=\"M14.12 3.88 16 2\" />\n      <path d=\"M9 7.13v-1a3.003 3.003 0 1 1 6 0v1\" />\n      <path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6\" />\n      <path d=\"M12 20v-9\" />\n      <path d=\"M6.53 9C4.6 8.8 3 7.1 3 5\" />\n      <path d=\"M6 13H2\" />\n      <path d=\"M3 21c0-2.1 1.7-3.9 3.8-4\" />\n      <path d=\"M20.97 5c0 2.1-1.6 3.8-3.5 4\" />\n      <path d=\"M22 13h-4\" />\n      <path d=\"M17.2 17c2.1.1 3.8 1.9 3.8 4\" />\n    </svg>\n  )\n}\n\nexport function Fab({ label, onClick }: FabProps) {\n  return (\n    <button type=\"button\" class=\"fab\" aria-label={label} title={label} onClick={onClick}>\n      <BugIcon />\n    </button>\n  )\n}\n","import { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { FeedbackSeverity, FeedbackType } from '../types'\nimport { Annotator } from './Annotator'\nimport type { StringKey } from './i18n'\nimport { truncateUrl, validateScreenshotFile } from './screenshot-utils'\n\nexport type FormStatus = 'idle' | 'submitting' | 'error' | 'success'\n\nexport interface FormValues {\n  description: string\n  feedback_type: FeedbackType\n  severity: FeedbackSeverity\n  /** Optional user-attached screenshot — via upload, paste, drag-drop, or\n   *  the annotator. v0.12 dropped html2canvas-on-submit; v0.7.1 dropped the\n   *  getDisplayMedia \"Capture this page\" path (asked the operator for screen\n   *  share permission on every click, too high a friction for the value). */\n  screenshot?: Blob\n  /** Always \"manual\" when a screenshot is attached — kept as a discriminated\n   *  field so the backend schema (which still accepts the legacy values) can\n   *  evolve independently. Undefined when no screenshot is attached. */\n  capture_method?: 'manual'\n}\n\ninterface FormProps {\n  strings: Record<StringKey, string>\n  onSubmit: (values: FormValues) => void\n  onCancel: () => void\n  status: FormStatus\n  errorMessage?: string\n}\n\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\nexport function Form({ strings, onSubmit, onCancel, status, errorMessage }: FormProps) {\n  const [description, setDescription] = useState('')\n  const [feedbackType, setFeedbackType] = useState<FeedbackType>('bug')\n  const [severity, setSeverity] = useState<FeedbackSeverity>('medium')\n  const [localError, setLocalError] = useState('')\n  const [screenshotBlob, setScreenshotBlob] = useState<Blob | null>(null)\n  const [screenshotPreview, setScreenshotPreview] = useState<string | null>(null)\n  const [isDragOver, setIsDragOver] = useState(false)\n  const [annotatorOpen, setAnnotatorOpen] = useState(false)\n  const fileInputRef = useRef<HTMLInputElement | null>(null)\n  const dropZoneRef = useRef<HTMLDivElement | null>(null)\n\n  const submitting = status === 'submitting'\n  const submitLabel = submitting ? strings['form.submitting'] : strings['form.submit']\n  const pageUrl = typeof window !== 'undefined' ? window.location.href : ''\n\n  // Revoke any object URL when the preview changes or the form unmounts.\n  useEffect(() => {\n    return () => {\n      if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n    }\n  }, [screenshotPreview])\n\n  const acceptFile = (file: File | Blob) => {\n    setLocalError('')\n    if (file instanceof File) {\n      const err = validateScreenshotFile(file)\n      if (err) {\n        setLocalError(\n          err.kind === 'type'\n            ? strings['form.screenshot.error_type']\n            : strings['form.screenshot.error_size'].replace('{max}', String(err.maxMb)),\n        )\n        return\n      }\n    }\n    if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n    setScreenshotBlob(file)\n    setScreenshotPreview(URL.createObjectURL(file))\n  }\n\n  const clearScreenshot = () => {\n    if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n    setScreenshotPreview(null)\n    setScreenshotBlob(null)\n    setLocalError('')\n    if (fileInputRef.current) fileInputRef.current.value = ''\n  }\n\n  const handleFileInputChange = (e: Event) => {\n    const file = (e.target as HTMLInputElement).files?.[0]\n    if (file) acceptFile(file)\n  }\n\n  const handleDragOver = (e: DragEvent) => {\n    e.preventDefault()\n    e.stopPropagation()\n    setIsDragOver(true)\n  }\n  const handleDragLeave = (e: DragEvent) => {\n    e.preventDefault()\n    e.stopPropagation()\n    if (e.currentTarget === e.target) setIsDragOver(false)\n  }\n  const handleDrop = (e: DragEvent) => {\n    e.preventDefault()\n    e.stopPropagation()\n    setIsDragOver(false)\n    const file = e.dataTransfer?.files?.[0]\n    if (file) acceptFile(file)\n  }\n\n  // Clipboard paste, scoped to the dropzone — so we don't hijack paste from\n  // textareas elsewhere in the page or in our own description field.\n  useEffect(() => {\n    const zone = dropZoneRef.current\n    if (!zone) return\n    const onPaste = (e: ClipboardEvent) => {\n      const items = e.clipboardData?.items\n      if (!items) return\n      for (const item of Array.from(items)) {\n        if (item.kind === 'file' && item.type.startsWith('image/')) {\n          const file = item.getAsFile()\n          if (file) {\n            e.preventDefault()\n            acceptFile(file)\n            return\n          }\n        }\n      }\n    }\n    zone.addEventListener('paste', onPaste)\n    return () => zone.removeEventListener('paste', onPaste)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [screenshotPreview])\n\n  const handleAnnotated = (annotated: Blob) => {\n    if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n    setScreenshotBlob(annotated)\n    setScreenshotPreview(URL.createObjectURL(annotated))\n    // Annotation preserves whatever method seeded the source — a\n    // display_media capture that gets annotated stays display_media.\n    setAnnotatorOpen(false)\n  }\n\n  const handleSubmit = (e: Event) => {\n    e.preventDefault()\n    if (!description.trim()) {\n      setLocalError(strings['form.description.required'])\n      return\n    }\n    setLocalError('')\n    const values: FormValues = {\n      description: description.trim(),\n      feedback_type: feedbackType,\n      severity,\n    }\n    if (screenshotBlob) {\n      values.screenshot = screenshotBlob\n      values.capture_method = 'manual'\n    }\n    onSubmit(values)\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <h2>{strings['form.title']}</h2>\n\n      <div class=\"field\">\n        <label for=\"mfb-desc\">{strings['form.description.label']}</label>\n        <textarea\n          id=\"mfb-desc\"\n          value={description}\n          placeholder={strings['form.description.placeholder']}\n          onInput={(e) => setDescription((e.target as HTMLTextAreaElement).value)}\n        />\n      </div>\n\n      <div class=\"row\">\n        <div class=\"field\">\n          <label for=\"mfb-type\">{strings['form.type.label']}</label>\n          <select\n            id=\"mfb-type\"\n            value={feedbackType}\n            onChange={(e) => setFeedbackType((e.target as HTMLSelectElement).value as FeedbackType)}\n          >\n            {TYPES.map((t) => <option value={t}>{strings[`type.${t}` as StringKey]}</option>)}\n          </select>\n        </div>\n        <div class=\"field\">\n          <label for=\"mfb-sev\">{strings['form.severity.label']}</label>\n          <select\n            id=\"mfb-sev\"\n            value={severity}\n            onChange={(e) => setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity)}\n          >\n            {SEVERITIES.map((s) => <option value={s}>{strings[`severity.${s}` as StringKey]}</option>)}\n          </select>\n        </div>\n      </div>\n\n      <div class=\"field\">\n        <label>{strings['form.screenshot.label']}</label>\n        <input\n          ref={fileInputRef}\n          type=\"file\"\n          accept=\"image/png,image/jpeg,image/webp\"\n          class=\"mfb-sr-only\"\n          onChange={handleFileInputChange}\n          aria-hidden=\"true\"\n          tabIndex={-1}\n        />\n        {screenshotPreview ? (\n          <div class=\"screenshot-preview\">\n            <img src={screenshotPreview} alt=\"\" />\n            <div class=\"screenshot-preview-actions\">\n              <button\n                type=\"button\"\n                class=\"btn btn--primary screenshot-annotate\"\n                onClick={() => setAnnotatorOpen(true)}\n              >\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>\n                {strings['form.screenshot.annotate']}\n              </button>\n              <button type=\"button\" class=\"btn\" onClick={clearScreenshot}>\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z\"/></svg>\n                {strings['form.screenshot.remove']}\n              </button>\n            </div>\n          </div>\n        ) : (\n          <div\n            ref={dropZoneRef}\n            class={`screenshot-dropzone ${isDragOver ? 'is-dragover' : ''}`}\n            tabIndex={0}\n            role=\"button\"\n            aria-label={strings['form.screenshot.label']}\n            onClick={() => fileInputRef.current?.click()}\n            onKeyDown={(e) => {\n              if (e.key === 'Enter' || e.key === ' ') {\n                e.preventDefault()\n                fileInputRef.current?.click()\n              }\n            }}\n            onDragOver={handleDragOver}\n            onDragLeave={handleDragLeave}\n            onDrop={handleDrop}\n          >\n            <svg class=\"screenshot-icon\" width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n              <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\n              <polyline points=\"17 8 12 3 7 8\"/>\n              <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>\n            </svg>\n            <div class=\"screenshot-cta\">\n              <strong>{strings['form.screenshot.cta_click']}</strong>\n              {', '}\n              {strings['form.screenshot.cta_rest']}\n            </div>\n            <div class=\"screenshot-formats\">{strings['form.screenshot.formats']}</div>\n          </div>\n        )}\n      </div>\n\n      {pageUrl && (\n        <div class=\"page-context\" title={pageUrl}>\n          <span class=\"page-context-label\">{strings['form.context.label']}</span>\n          <span class=\"page-context-url\">{truncateUrl(pageUrl, 90)}</span>\n        </div>\n      )}\n\n      {localError && <div class=\"error\">{localError}</div>}\n      {status === 'error' && errorMessage && <div class=\"error\">{errorMessage}</div>}\n      {status === 'success' && <div class=\"success\">{strings['form.success']}</div>}\n\n      <div class=\"actions\">\n        <button type=\"button\" class=\"btn\" onClick={onCancel} disabled={submitting}>{strings['form.cancel']}</button>\n        <button type=\"submit\" class=\"btn btn--primary\" disabled={submitting}>{submitLabel}</button>\n      </div>\n\n      {annotatorOpen && screenshotBlob && (\n        <Annotator\n          imageBlob={screenshotBlob}\n          strings={strings}\n          onCancel={() => setAnnotatorOpen(false)}\n          onSave={handleAnnotated}\n        />\n      )}\n    </form>\n  )\n}\n","/**\n * Annotator — screenshot annotation modal.\n *\n * v0.6.0: rectangle, arrow, freehand, text. Custom canvas, no external lib.\n * v0.10.0: + highlight (semi-transparent fill), blur (pixelate sensitive\n * regions), full undo/redo stack, native color picker alongside swatches.\n *\n * Save rasterizes the original image + every shape onto a fresh canvas\n * and returns a PNG Blob. Blurs are always sampled from the source image\n * pixels (not the canvas), so strokes/text/highlights drawn near a blurred\n * region don't get re-blurred by accident.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'\n\nimport type { StringKey } from './i18n'\n\ntype Tool = 'rectangle' | 'arrow' | 'freehand' | 'text' | 'highlight' | 'blur'\n\ninterface BaseShape {\n  color: string\n  lineWidth: number\n}\n\ninterface RectShape extends BaseShape {\n  kind: 'rectangle'\n  x: number\n  y: number\n  w: number\n  h: number\n}\n\ninterface ArrowShape extends BaseShape {\n  kind: 'arrow'\n  x1: number\n  y1: number\n  x2: number\n  y2: number\n}\n\ninterface FreehandShape extends BaseShape {\n  kind: 'freehand'\n  points: Array<{ x: number; y: number }>\n}\n\ninterface TextShape extends BaseShape {\n  kind: 'text'\n  x: number\n  y: number\n  text: string\n  fontSize: number\n}\n\ninterface HighlightShape extends BaseShape {\n  kind: 'highlight'\n  x: number\n  y: number\n  w: number\n  h: number\n}\n\ninterface BlurShape extends BaseShape {\n  kind: 'blur'\n  x: number\n  y: number\n  w: number\n  h: number\n  /** Tile size in source-image pixels. Scales with canvas width. */\n  tile: number\n}\n\ntype Shape =\n  | RectShape\n  | ArrowShape\n  | FreehandShape\n  | TextShape\n  | HighlightShape\n  | BlurShape\n\nconst COLORS: string[] = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#ffffff']\nconst HIGHLIGHT_ALPHA = 0.35\n\n// ---------------------------------------------------------------------------\n// Drawing helpers\n// ---------------------------------------------------------------------------\n\nfunction drawShape(\n  ctx: CanvasRenderingContext2D,\n  shape: Shape,\n  sourceImage: HTMLImageElement | null,\n) {\n  ctx.save()\n  ctx.strokeStyle = shape.color\n  ctx.fillStyle = shape.color\n  ctx.lineWidth = shape.lineWidth\n  ctx.lineCap = 'round'\n  ctx.lineJoin = 'round'\n\n  if (shape.kind === 'rectangle') {\n    ctx.strokeRect(shape.x, shape.y, shape.w, shape.h)\n  } else if (shape.kind === 'arrow') {\n    drawArrow(ctx, shape.x1, shape.y1, shape.x2, shape.y2)\n  } else if (shape.kind === 'freehand') {\n    if (shape.points.length < 2) {\n      ctx.restore()\n      return\n    }\n    ctx.beginPath()\n    ctx.moveTo(shape.points[0]!.x, shape.points[0]!.y)\n    for (let i = 1; i < shape.points.length; i++) {\n      ctx.lineTo(shape.points[i]!.x, shape.points[i]!.y)\n    }\n    ctx.stroke()\n  } else if (shape.kind === 'text') {\n    ctx.font = `bold ${shape.fontSize}px -apple-system, BlinkMacSystemFont, sans-serif`\n    ctx.textBaseline = 'top'\n    const metrics = ctx.measureText(shape.text)\n    const padding = 4\n    const w = metrics.width + padding * 2\n    const hh = shape.fontSize + padding * 2\n    ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'\n    ctx.fillRect(shape.x - padding, shape.y - padding, w, hh)\n    ctx.fillStyle = shape.color\n    ctx.fillText(shape.text, shape.x, shape.y)\n  } else if (shape.kind === 'highlight') {\n    // Semi-transparent filled rect. Multiply-ish blend so the underlying\n    // pixels show through — typical marker-pen look.\n    ctx.globalAlpha = HIGHLIGHT_ALPHA\n    ctx.fillRect(\n      normalizeStart(shape.x, shape.w),\n      normalizeStart(shape.y, shape.h),\n      Math.abs(shape.w),\n      Math.abs(shape.h),\n    )\n  } else if (shape.kind === 'blur') {\n    drawBlur(ctx, shape, sourceImage)\n  }\n  ctx.restore()\n}\n\nfunction normalizeStart(start: number, size: number): number {\n  return size < 0 ? start + size : start\n}\n\nfunction drawArrow(\n  ctx: CanvasRenderingContext2D,\n  x1: number,\n  y1: number,\n  x2: number,\n  y2: number,\n) {\n  const headLen = 14\n  const angle = Math.atan2(y2 - y1, x2 - x1)\n  ctx.beginPath()\n  ctx.moveTo(x1, y1)\n  ctx.lineTo(x2, y2)\n  ctx.stroke()\n  ctx.beginPath()\n  ctx.moveTo(x2, y2)\n  ctx.lineTo(\n    x2 - headLen * Math.cos(angle - Math.PI / 6),\n    y2 - headLen * Math.sin(angle - Math.PI / 6),\n  )\n  ctx.lineTo(\n    x2 - headLen * Math.cos(angle + Math.PI / 6),\n    y2 - headLen * Math.sin(angle + Math.PI / 6),\n  )\n  ctx.closePath()\n  ctx.fill()\n}\n\n/**\n * Pixelate a rectangular region by averaging tiles of the source image.\n * Always samples from the original `<img>` so blurs over text/strokes\n * still hide the underlying pixels, not the marks drawn on top.\n */\nfunction drawBlur(\n  ctx: CanvasRenderingContext2D,\n  shape: BlurShape,\n  sourceImage: HTMLImageElement | null,\n) {\n  if (!sourceImage) return\n  const x = normalizeStart(shape.x, shape.w)\n  const y = normalizeStart(shape.y, shape.h)\n  const w = Math.abs(shape.w)\n  const h = Math.abs(shape.h)\n  if (w < 2 || h < 2) return\n  const tile = Math.max(4, shape.tile)\n  // Drop tiles aligned to the source image — pixelation is at native res\n  // so the result is crisp regardless of CSS scale on the display canvas.\n  for (let yy = y; yy < y + h; yy += tile) {\n    for (let xx = x; xx < x + w; xx += tile) {\n      const tw = Math.min(tile, x + w - xx)\n      const th = Math.min(tile, y + h - yy)\n      ctx.drawImage(\n        sourceImage,\n        xx,\n        yy,\n        tw,\n        th, // source rect from the image\n        xx,\n        yy,\n        tw,\n        th, // dest rect on the canvas (same coords)\n      )\n    }\n  }\n  // Sampling a single tile-sized chunk and re-painting it at tile size\n  // gives the mosaic. To get the visual look, downscale + upscale via\n  // imageSmoothingDisabled.\n  ctx.imageSmoothingEnabled = false\n  for (let yy = y; yy < y + h; yy += tile) {\n    for (let xx = x; xx < x + w; xx += tile) {\n      const tw = Math.min(tile, x + w - xx)\n      const th = Math.min(tile, y + h - yy)\n      // Sample the top-left pixel of this tile from the source and stretch.\n      ctx.drawImage(\n        sourceImage,\n        xx,\n        yy,\n        1,\n        1, // single source pixel\n        xx,\n        yy,\n        tw,\n        th, // stretched dest tile\n      )\n    }\n  }\n  ctx.imageSmoothingEnabled = true\n}\n\n// ---------------------------------------------------------------------------\n// Inline icons\n// ---------------------------------------------------------------------------\n\nconst Icon = {\n  rect: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <rect x=\"2\" y=\"3\" width=\"12\" height=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" />\n    </svg>\n  ),\n  arrow: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M2 8h11M9 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n    </svg>\n  ),\n  pencil: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\" />\n    </svg>\n  ),\n  text: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M3 3h10M8 3v10M5 13h6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n    </svg>\n  ),\n  highlight: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n      <path d=\"M2 14h12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n    </svg>\n  ),\n  blur: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <rect x=\"2\" y=\"2\" width=\"4\" height=\"4\" fill=\"currentColor\" opacity=\"0.85\" />\n      <rect x=\"7\" y=\"2\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.55\" />\n      <rect x=\"11\" y=\"2\" width=\"3\" height=\"4\" fill=\"currentColor\" opacity=\"0.4\" />\n      <rect x=\"2\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.6\" />\n      <rect x=\"6\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n      <rect x=\"10\" y=\"7\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.5\" />\n      <rect x=\"2\" y=\"11\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.4\" />\n      <rect x=\"7\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.65\" />\n      <rect x=\"11\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n    </svg>\n  ),\n  undo: (\n    <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n    </svg>\n  ),\n  redo: (\n    <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n    </svg>\n  ),\n  trash: (\n    <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n    </svg>\n  ),\n  close: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M4 4l8 8M12 4l-8 8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n    </svg>\n  ),\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface AnnotatorProps {\n  imageBlob: Blob\n  strings: Record<StringKey, string>\n  onSave: (annotated: Blob) => void\n  onCancel: () => void\n}\n\nexport function Annotator({ imageBlob, strings, onSave, onCancel }: AnnotatorProps) {\n  const canvasRef = useRef<HTMLCanvasElement | null>(null)\n  const containerRef = useRef<HTMLDivElement | null>(null)\n  const imageRef = useRef<HTMLImageElement | null>(null)\n  const [tool, setTool] = useState<Tool>('rectangle')\n  const [color, setColor] = useState<string>(COLORS[0]!)\n  const [shapes, setShapes] = useState<Shape[]>([])\n  const [past, setPast] = useState<Shape[][]>([])\n  const [future, setFuture] = useState<Shape[][]>([])\n  const isDrawingRef = useRef(false)\n  const [draftShape, setDraftShape] = useState<Shape | null>(null)\n  const [imageLoaded, setImageLoaded] = useState(false)\n  const [saving, setSaving] = useState(false)\n  const scaleRef = useRef(1)\n\n  function addShape(shape: Shape) {\n    setShapes((s) => {\n      // Use the functional setState so we read the most recent shape list\n      // even when this fires from a stale-closure event handler. The past\n      // stack captures `s` here (the truly current state).\n      setPast((p) => [...p, s])\n      return [...s, shape]\n    })\n    setFuture([])\n  }\n\n  function clearShapes() {\n    setShapes((s) => {\n      setPast((p) => [...p, s])\n      return []\n    })\n    setFuture([])\n  }\n\n  function undo() {\n    setPast((p) => {\n      if (p.length === 0) return p\n      const prev = p[p.length - 1]!\n      setShapes((s) => {\n        setFuture((f) => [s, ...f])\n        return prev\n      })\n      return p.slice(0, -1)\n    })\n  }\n\n  function redo() {\n    setFuture((f) => {\n      if (f.length === 0) return f\n      const next = f[0]!\n      setShapes((s) => {\n        setPast((p) => [...p, s])\n        return next\n      })\n      return f.slice(1)\n    })\n  }\n\n  // Esc handler uses useLayoutEffect (not useEffect) so the listener is\n  // attached SYNCHRONOUSLY after DOM commit, BEFORE paint. Tests (and\n  // fast human users) that press Escape the moment the annotator becomes\n  // visible would otherwise hit a brief window where the listener isn't\n  // yet attached. The parent Modal's keydown handler bails when it sees\n  // .annotator-backdrop in the shadow root, so we don't double-fire.\n  useLayoutEffect(() => {\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === 'Escape') {\n        e.stopPropagation()\n        onCancel()\n      }\n    }\n    window.addEventListener('keydown', onKey)\n    return () => window.removeEventListener('keydown', onKey)\n  }, [onCancel])\n\n  // Cmd/Ctrl-Z = undo, Cmd/Ctrl-Shift-Z (or Cmd/Ctrl-Y) = redo. Re-binds\n  // on each state mutation to capture fresh shapes/past/future closures.\n  useEffect(() => {\n    const onKey = (e: KeyboardEvent) => {\n      const mod = e.metaKey || e.ctrlKey\n      if (!mod) return\n      const k = e.key.toLowerCase()\n      if (k === 'z') {\n        e.preventDefault()\n        e.stopPropagation()\n        if (e.shiftKey) redo()\n        else undo()\n      } else if (k === 'y') {\n        e.preventDefault()\n        e.stopPropagation()\n        redo()\n      }\n    }\n    window.addEventListener('keydown', onKey)\n    return () => window.removeEventListener('keydown', onKey)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [past, future, shapes])\n\n  useEffect(() => {\n    const url = URL.createObjectURL(imageBlob)\n    const img = new Image()\n    img.onload = () => {\n      imageRef.current = img\n      setImageLoaded(true)\n    }\n    img.src = url\n    return () => URL.revokeObjectURL(url)\n  }, [imageBlob])\n\n  // useLayoutEffect (not useEffect) so the canvas's width/height/style\n  // are set SYNCHRONOUSLY after Preact's render commit and BEFORE the\n  // browser's paint. Without this, the canvas first mounts at the HTML\n  // default 300×150 and only resizes to the image's dimensions on the\n  // next microtask — a window during which Playwright's\n  // `canvas.boundingBox()` can read the stale layout. Tests then fire\n  // mouse events at coordinates that no longer land inside the canvas,\n  // mousedown is dispatched to the wrap/backdrop instead, no draftShape\n  // is created, and `.annotator-count` stays at 0 → flake.\n  useLayoutEffect(() => {\n    if (\n      !imageLoaded ||\n      !canvasRef.current ||\n      !imageRef.current ||\n      !containerRef.current\n    ) {\n      return\n    }\n    const img = imageRef.current\n    const container = containerRef.current\n    const maxW = container.clientWidth - 16\n    const maxH = window.innerHeight * 0.65\n    const fitScale = Math.min(maxW / img.width, maxH / img.height, 1)\n    scaleRef.current = fitScale\n\n    const canvas = canvasRef.current\n    canvas.width = img.width\n    canvas.height = img.height\n    canvas.style.width = `${img.width * fitScale}px`\n    canvas.style.height = `${img.height * fitScale}px`\n    redraw()\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [imageLoaded])\n\n  useEffect(() => {\n    redraw()\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [shapes, draftShape])\n\n  function redraw() {\n    const canvas = canvasRef.current\n    const img = imageRef.current\n    if (!canvas || !img) return\n    const ctx = canvas.getContext('2d')\n    if (!ctx) return\n    ctx.clearRect(0, 0, canvas.width, canvas.height)\n    ctx.drawImage(img, 0, 0)\n    for (const s of shapes) drawShape(ctx, s, img)\n    if (draftShape) drawShape(ctx, draftShape, img)\n  }\n\n  function getCanvasCoords(e: MouseEvent) {\n    const canvas = canvasRef.current!\n    const rect = canvas.getBoundingClientRect()\n    return {\n      x: (e.clientX - rect.left) / scaleRef.current,\n      y: (e.clientY - rect.top) / scaleRef.current,\n    }\n  }\n\n  const handleMouseDown = (e: MouseEvent) => {\n    if (!imageLoaded) return\n    const { x, y } = getCanvasCoords(e)\n    const canvasW = canvasRef.current!.width\n    const lineWidth = Math.max(3, Math.round(canvasW / 400))\n    const blurTile = Math.max(8, Math.round(canvasW / 80))\n\n    if (tool === 'text') {\n      const text = window.prompt(strings['annotator.text_prompt'])\n      if (text && text.trim()) {\n        addShape({\n          kind: 'text',\n          x,\n          y,\n          text: text.trim(),\n          color,\n          fontSize: Math.max(16, Math.round(canvasW / 50)),\n          lineWidth: 1,\n        })\n      }\n      return\n    }\n\n    isDrawingRef.current = true\n    if (tool === 'rectangle') {\n      setDraftShape({ kind: 'rectangle', x, y, w: 0, h: 0, color, lineWidth })\n    } else if (tool === 'arrow') {\n      setDraftShape({ kind: 'arrow', x1: x, y1: y, x2: x, y2: y, color, lineWidth })\n    } else if (tool === 'freehand') {\n      setDraftShape({ kind: 'freehand', points: [{ x, y }], color, lineWidth })\n    } else if (tool === 'highlight') {\n      setDraftShape({\n        kind: 'highlight',\n        x,\n        y,\n        w: 0,\n        h: 0,\n        color: color === '#ffffff' ? '#fde047' : color,\n        lineWidth: 1,\n      })\n    } else if (tool === 'blur') {\n      setDraftShape({\n        kind: 'blur',\n        x,\n        y,\n        w: 0,\n        h: 0,\n        color: '#000000',\n        lineWidth: 0,\n        tile: blurTile,\n      })\n    }\n  }\n\n  const handleMouseMove = (e: MouseEvent) => {\n    if (!isDrawingRef.current) return\n    const { x, y } = getCanvasCoords(e)\n    // Functional setState: read the most recent draftShape rather than the\n    // closure-captured value. Playwright (and fast humans) can fire\n    // mousedown→mousemove faster than Preact commits the re-render, leaving\n    // the closure's `draftShape` at the previous frame (often null right\n    // after mousedown). audit R6 e2e regression fix.\n    setDraftShape((current) => {\n      if (!current) return current\n      if (\n        current.kind === 'rectangle' ||\n        current.kind === 'highlight' ||\n        current.kind === 'blur'\n      ) {\n        return { ...current, w: x - current.x, h: y - current.y }\n      }\n      if (current.kind === 'arrow') {\n        return { ...current, x2: x, y2: y }\n      }\n      if (current.kind === 'freehand') {\n        return { ...current, points: [...current.points, { x, y }] }\n      }\n      return current\n    })\n  }\n\n  const handleMouseUp = () => {\n    // Same closure-staleness concern as handleMouseMove: read the\n    // committed draft via the functional setter rather than rely on the\n    // capture from this render.\n    setDraftShape((current) => {\n      if (isDrawingRef.current && current) {\n        const isTiny =\n          ((current.kind === 'rectangle' ||\n            current.kind === 'highlight' ||\n            current.kind === 'blur') &&\n            Math.abs(current.w) < 4 &&\n            Math.abs(current.h) < 4) ||\n          (current.kind === 'arrow' &&\n            Math.hypot(\n              current.x2 - current.x1,\n              current.y2 - current.y1,\n            ) < 4) ||\n          (current.kind === 'freehand' && current.points.length < 3)\n        if (!isTiny) {\n          addShape(current)\n        }\n      }\n      return null\n    })\n    isDrawingRef.current = false\n  }\n\n  const handleSave = async () => {\n    const canvas = canvasRef.current\n    if (!canvas) return\n    setSaving(true)\n    try {\n      const blob = await new Promise<Blob | null>((resolve) =>\n        canvas.toBlob(resolve, 'image/png', 0.92),\n      )\n      if (blob) onSave(blob)\n    } finally {\n      setSaving(false)\n    }\n  }\n\n  const tools: Array<{ id: Tool; icon: typeof Icon.rect; label: string }> = [\n    { id: 'rectangle', icon: Icon.rect, label: strings['annotator.tool.rectangle'] },\n    { id: 'arrow', icon: Icon.arrow, label: strings['annotator.tool.arrow'] },\n    { id: 'freehand', icon: Icon.pencil, label: strings['annotator.tool.freehand'] },\n    { id: 'text', icon: Icon.text, label: strings['annotator.tool.text'] },\n    { id: 'highlight', icon: Icon.highlight, label: strings['annotator.tool.highlight'] },\n    { id: 'blur', icon: Icon.blur, label: strings['annotator.tool.blur'] },\n  ]\n\n  return (\n    <div\n      class=\"annotator-backdrop\"\n      role=\"presentation\"\n      onClick={(e) => {\n        if (e.target === e.currentTarget) onCancel()\n      }}\n    >\n      <div class=\"annotator\" role=\"dialog\" aria-modal=\"true\" aria-label={strings['annotator.title']}>\n        <div class=\"annotator-header\">\n          <span>{strings['annotator.title']}</span>\n          <button\n            type=\"button\"\n            class=\"modal-close\"\n            aria-label={strings['form.close']}\n            onClick={onCancel}\n          >\n            {Icon.close}\n          </button>\n        </div>\n\n        <div class=\"annotator-toolbar\">\n          <div class=\"annotator-tools\">\n            {tools.map((t) => (\n              <button\n                key={t.id}\n                type=\"button\"\n                onClick={() => setTool(t.id)}\n                title={t.label}\n                aria-label={t.label}\n                aria-pressed={tool === t.id}\n                class={`annotator-tool ${tool === t.id ? 'is-active' : ''}`}\n              >\n                {t.icon}\n              </button>\n            ))}\n          </div>\n\n          <span class=\"annotator-sep\" />\n\n          <div class=\"annotator-colors\">\n            {COLORS.map((c) => (\n              <button\n                key={c}\n                type=\"button\"\n                onClick={() => setColor(c)}\n                aria-label={c}\n                aria-pressed={color === c}\n                class={`annotator-color ${color === c ? 'is-active' : ''}`}\n                style={{ backgroundColor: c }}\n              />\n            ))}\n            <label class=\"annotator-color annotator-color-picker\" title={strings['annotator.color_picker']}>\n              <input\n                type=\"color\"\n                value={color}\n                onInput={(e) => setColor((e.target as HTMLInputElement).value)}\n                aria-label={strings['annotator.color_picker']}\n              />\n            </label>\n          </div>\n\n          <span class=\"annotator-sep\" />\n\n          <button\n            type=\"button\"\n            class=\"annotator-btn\"\n            onClick={undo}\n            disabled={past.length === 0}\n            title={strings['annotator.undo']}\n          >\n            {Icon.undo}\n            <span>{strings['annotator.undo']}</span>\n          </button>\n          <button\n            type=\"button\"\n            class=\"annotator-btn\"\n            onClick={redo}\n            disabled={future.length === 0}\n            title={strings['annotator.redo']}\n          >\n            {Icon.redo}\n            <span>{strings['annotator.redo']}</span>\n          </button>\n          <button\n            type=\"button\"\n            class=\"annotator-btn\"\n            onClick={clearShapes}\n            disabled={shapes.length === 0}\n          >\n            {Icon.trash}\n            <span>{strings['annotator.clear']}</span>\n          </button>\n\n          <span class=\"annotator-spacer\" />\n\n          <span class=\"annotator-count\">\n            {shapes.length} {strings['annotator.count_suffix']}\n          </span>\n        </div>\n\n        <div ref={containerRef} class=\"annotator-canvas-wrap\">\n          {!imageLoaded ? (\n            <span class=\"annotator-loading\">{strings['annotator.loading']}</span>\n          ) : (\n            <canvas\n              ref={canvasRef}\n              onMouseDown={handleMouseDown}\n              onMouseMove={handleMouseMove}\n              onMouseUp={handleMouseUp}\n              onMouseLeave={handleMouseUp}\n              class=\"annotator-canvas\"\n            />\n          )}\n        </div>\n\n        <div class=\"annotator-footer\">\n          <button type=\"button\" class=\"btn\" onClick={onCancel}>\n            {strings['form.cancel']}\n          </button>\n          <button\n            type=\"button\"\n            class=\"btn btn--primary\"\n            onClick={handleSave}\n            disabled={saving || !imageLoaded}\n          >\n            {saving ? strings['annotator.applying'] : strings['annotator.apply']}\n          </button>\n        </div>\n      </div>\n    </div>\n  )\n}\n","/**\n * MineList — the user's own reports on this project.\n *\n * Polls every 30 s while mounted (Sentry-style staleTime + manual\n * refresh). Empty state when the submitter has no thread yet (404 from\n * the backend is mapped to []). Click a row to open the detail view.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { KpiStrip, type KpiFilter, rowsMatchingFilter } from './KpiStrip'\nimport { ReportRow } from './ReportRow'\n\ninterface MineListProps {\n  api: ApiClient\n  externalId: string\n  strings: Record<StringKey, string>\n  onSelect: (report: WidgetReportRow) => void\n}\n\nconst POLL_MS = 30_000\n\nexport function MineList({ api, externalId, strings, onSelect }: MineListProps) {\n  const [rows, setRows] = useState<WidgetReportRow[] | null>(null)\n  const [error, setError] = useState<string | null>(null)\n  const [refreshing, setRefreshing] = useState(false)\n  const [filter, setFilter] = useState<KpiFilter>('all')\n  const mountedRef = useRef(true)\n\n  const fetchRows = async () => {\n    setRefreshing(true)\n    setError(null)\n    try {\n      const next = await api.listMine(externalId)\n      if (!mountedRef.current) return\n      setRows(next)\n    } catch (err) {\n      // Don't surface raw API error messages to the user — they used to\n      // render as e.g. `listMine failed: 403 {\"detail\":\"Public API key…\"}`\n      // straight into the panel. Friendly fallback in the UI, raw error\n      // in the console for the developer.\n      if (typeof console !== 'undefined') console.warn('[mhosaic] listMine:', err)\n      if (!mountedRef.current) return\n      setError(strings['mine.error'])\n    } finally {\n      if (mountedRef.current) setRefreshing(false)\n    }\n  }\n\n  useEffect(() => {\n    mountedRef.current = true\n    void fetchRows()\n    const timer = setInterval(() => {\n      void fetchRows()\n    }, POLL_MS)\n    return () => {\n      mountedRef.current = false\n      clearInterval(timer)\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [externalId])\n\n  const isEmpty = rows !== null && rows.length === 0\n  const isLoading = rows === null && !error\n  const visibleRows = rows ? rowsMatchingFilter(rows, filter) : null\n  const visibleEmpty = !!rows && rows.length > 0 && (visibleRows?.length ?? 0) === 0\n\n  return (\n    <div class=\"mine-list\">\n      <div class=\"mine-list-header\">\n        <h2>{strings['tab.mine']}</h2>\n        <button\n          type=\"button\"\n          class=\"btn\"\n          onClick={() => {\n            void fetchRows()\n          }}\n          disabled={refreshing}\n        >\n          {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n        </button>\n      </div>\n      {rows && rows.length > 0 && (\n        <KpiStrip rows={rows} filter={filter} onFilter={setFilter} strings={strings} />\n      )}\n      {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n      {error && <div class=\"error\">{error}</div>}\n      {isEmpty && (\n        <div class=\"mine-empty\">\n          <strong>{strings['mine.empty.title']}</strong>\n          <p>{strings['mine.empty.body']}</p>\n        </div>\n      )}\n      {visibleEmpty && (\n        <div class=\"mine-empty\">\n          <p>{strings['mine.filter.empty']}</p>\n        </div>\n      )}\n      {visibleRows && visibleRows.length > 0 && (\n        <ul class=\"mine-rows\">\n          {visibleRows.map((row) => (\n            <li>\n              <ReportRow row={row} strings={strings} onClick={() => onSelect(row)} />\n            </li>\n          ))}\n        </ul>\n      )}\n    </div>\n  )\n}\n","/**\n * KpiStrip — four-pill summary at the top of the user's \"My reports\" tab.\n *\n * Computes counts client-side from the same `WidgetReportRow[]` that\n * MineList already fetches; no extra endpoint. Each pill is clickable\n * and toggles a status filter on the list below — matches thePnr's\n * customer-facing pattern (`FeedbackKPICards.tsx`).\n */\n\nimport { h } from 'preact'\n\nimport type { ReportStatus, WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\nexport type KpiFilter = 'all' | 'new' | 'in_progress' | 'awaiting_validation' | 'resolved'\n\ninterface KpiStripProps {\n  rows: WidgetReportRow[]\n  filter: KpiFilter\n  onFilter: (next: KpiFilter) => void\n  strings: Record<StringKey, string>\n}\n\ninterface Counts {\n  new: number\n  in_progress: number\n  awaiting_validation: number\n  resolved: number\n  total: number\n}\n\nexport function computeKpiCounts(rows: WidgetReportRow[]): Counts {\n  const counts: Counts = {\n    new: 0,\n    in_progress: 0,\n    awaiting_validation: 0,\n    resolved: 0,\n    total: 0,\n  }\n  for (const row of rows) {\n    counts.total += 1\n    switch (row.status) {\n      case 'new':\n        counts.new += 1\n        break\n      case 'in_progress':\n        counts.in_progress += 1\n        break\n      case 'awaiting_validation':\n        counts.awaiting_validation += 1\n        break\n      case 'closed':\n      case 'rejected':\n      case 'duplicate':\n      case 'wontfix':\n        counts.resolved += 1\n        break\n    }\n  }\n  return counts\n}\n\n/**\n * Resolution rate = (awaiting_validation + closed + rejected + duplicate +\n * wontfix) / total. Includes operator-resolved-as-no-action so the\n * percentage reflects \"your reports the team has acted on\" rather than\n * \"your reports that turned into shipped fixes\". Returns null if there\n * are no rows yet — avoids showing \"0%\" before the user has filed\n * anything, which reads as ominous.\n */\nexport function computeResolutionRate(counts: Counts): number | null {\n  if (counts.total === 0) return null\n  return Math.round(\n    ((counts.awaiting_validation + counts.resolved) / counts.total) * 100,\n  )\n}\n\nconst FILTER_FOR_STATUS: Record<ReportStatus, KpiFilter> = {\n  new: 'new',\n  in_progress: 'in_progress',\n  awaiting_validation: 'awaiting_validation',\n  closed: 'resolved',\n  rejected: 'resolved',\n  duplicate: 'resolved',\n  wontfix: 'resolved',\n}\n\nexport function rowsMatchingFilter(\n  rows: WidgetReportRow[],\n  filter: KpiFilter,\n): WidgetReportRow[] {\n  if (filter === 'all') return rows\n  return rows.filter((r) => FILTER_FOR_STATUS[r.status] === filter)\n}\n\nexport function KpiStrip({ rows, filter, onFilter, strings }: KpiStripProps) {\n  const counts = computeKpiCounts(rows)\n  const resolutionRate = computeResolutionRate(counts)\n  const cells: Array<{ key: KpiFilter; label: string; value: string }> = [\n    { key: 'new', label: strings['kpi.new'], value: String(counts.new) },\n    { key: 'in_progress', label: strings['kpi.in_progress'], value: String(counts.in_progress) },\n    {\n      key: 'awaiting_validation',\n      label: strings['kpi.awaiting_validation'],\n      value: String(counts.awaiting_validation),\n    },\n    {\n      key: 'resolved',\n      label: strings['kpi.resolution_rate'],\n      value: resolutionRate === null ? '—' : `${resolutionRate}%`,\n    },\n  ]\n  return (\n    <div class=\"kpi-strip\" role=\"toolbar\">\n      {cells.map((c) => {\n        const active = filter === c.key\n        const toggleTo: KpiFilter = active ? 'all' : c.key\n        return (\n          <button\n            type=\"button\"\n            class={`kpi-cell kpi-cell--${c.key}${active ? ' is-active' : ''}`}\n            onClick={() => onFilter(toggleTo)}\n            aria-pressed={active}\n          >\n            <span class=\"kpi-value\">{c.value}</span>\n            <span class=\"kpi-label\">{c.label}</span>\n          </button>\n        )\n      })}\n    </div>\n  )\n}\n","import { h, type ComponentChildren } from 'preact'\nimport { useEffect, useRef } from 'preact/hooks'\n\ninterface ModalProps {\n  onDismiss: () => void\n  children: ComponentChildren\n  closeLabel?: string\n  /** v0.12: when true, the modal grows toward near-fullscreen on desktop\n   *  and full-screen on mobile. Drives the \"transport\" feel for the Board\n   *  tab — opening the tab feels like clicking through to a real page. */\n  expanded?: boolean\n}\n\nexport function Modal({ onDismiss, children, closeLabel = 'Close', expanded = false }: ModalProps) {\n  const modalRef = useRef<HTMLDivElement>(null)\n  const previouslyFocused = useRef<Element | null>(null)\n\n  // Esc-to-close + focus management. Without this, users couldn't dismiss\n  // with the keyboard (a real-world UX expectation). Listener installed on\n  // window so it works regardless of which element inside the modal has focus.\n  useEffect(() => {\n    previouslyFocused.current = document.activeElement\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key !== 'Escape') return\n      // Ignore Esc when a topmost dialog (e.g. the Annotator) is mounted in\n      // our shadow root — it owns the keystroke. Without this guard the\n      // Esc fires both handlers on the same event and dismisses the form\n      // behind the annotator.\n      const root = modalRef.current?.getRootNode()\n      if (\n        root instanceof ShadowRoot &&\n        root.querySelector('.annotator-backdrop')\n      ) {\n        return\n      }\n      e.stopPropagation()\n      onDismiss()\n    }\n    window.addEventListener('keydown', onKey)\n    // Focus first focusable element inside the modal so the keyboard user\n    // lands inside, not still on the FAB or whatever opened the modal.\n    const first = modalRef.current?.querySelector<HTMLElement>(\n      'textarea, input, select, button',\n    )\n    first?.focus()\n    return () => {\n      window.removeEventListener('keydown', onKey)\n      // Restore focus on dismiss so screen-readers don't lose place.\n      const prev = previouslyFocused.current as HTMLElement | null\n      if (prev && typeof prev.focus === 'function') prev.focus()\n    }\n  }, [onDismiss])\n\n  return (\n    <div\n      class={`backdrop ${expanded ? 'is-expanded' : ''}`}\n      role=\"presentation\"\n      onClick={(e) => {\n        if (e.target === e.currentTarget) onDismiss()\n      }}\n    >\n      <div\n        ref={modalRef}\n        class={`modal ${expanded ? 'is-expanded' : ''}`}\n        role=\"dialog\"\n        aria-modal=\"true\"\n      >\n        <button\n          type=\"button\"\n          class=\"modal-close\"\n          aria-label={closeLabel}\n          onClick={onDismiss}\n        >\n          ×\n        </button>\n        {children}\n      </div>\n    </div>\n  )\n}\n","export const WIDGET_STYLES = `\n:host {\n  --mfb-accent: #3b82f6;\n  --mfb-accent-contrast: #ffffff;\n\n  /* FAB-specific palette — Mhosaic brand (steel blue family, per\n     mhosaic-core's design system v4). Soft-black surface + light\n     steel-blue glyph. Kept separate from --mfb-accent on purpose:\n     the accent is used everywhere inside the panel for submit\n     buttons / focus rings, and we don't want changing the FAB chrome\n     to inadvertently restyle the whole widget. */\n  --mfb-fab-bg: #1c2230;       /* Mhosaic --black (oklch ≈ 0.22 0.025 258) */\n  --mfb-fab-icon: #8aa9e5;     /* Mhosaic --blue-300 (oklch ≈ 0.70 0.13 258) */\n  --mfb-bg: #ffffff;\n  --mfb-surface: #f9fafb;\n  --mfb-surface-2: #f3f4f6;\n  --mfb-text: #0a0a0a;\n  --mfb-text-muted: #6b7280;\n  --mfb-border: #e5e7eb;\n  --mfb-border-strong: #d1d5db;\n  --mfb-radius: 8px;\n  --mfb-radius-lg: 14px;\n  --mfb-font: system-ui, -apple-system, sans-serif;\n  --mfb-z-index: 2147483640;\n\n  /* Spacing scale — 4px base, used everywhere instead of magic numbers. */\n  --mfb-space-1: 4px;\n  --mfb-space-2: 8px;\n  --mfb-space-3: 12px;\n  --mfb-space-4: 16px;\n  --mfb-space-5: 24px;\n  --mfb-space-6: 32px;\n  --mfb-space-7: 48px;\n\n  /* Type scale — fixed sizes, no fluid scaling. Body 14px sits in the\n     \"comfortable on every viewport\" range; headings step up gracefully. */\n  --mfb-text-xs: 11px;\n  --mfb-text-sm: 13px;\n  --mfb-text-base: 14px;\n  --mfb-text-md: 15px;\n  --mfb-text-lg: 17px;\n  --mfb-text-xl: 20px;\n\n  /* Elevation — three tiers for layering. */\n  --mfb-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08);\n  --mfb-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);\n  --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.18), 0 8px 20px rgba(0, 0, 0, 0.10);\n\n  all: initial;\n  font-family: var(--mfb-font);\n  color: var(--mfb-text);\n  position: fixed;\n  z-index: var(--mfb-z-index);\n}\n\n@media (prefers-color-scheme: dark) {\n  :host {\n    --mfb-bg: #0f172a;\n    --mfb-surface: #1e293b;\n    --mfb-surface-2: #253349;\n    --mfb-text: #f8fafc;\n    --mfb-text-muted: #94a3b8;\n    --mfb-border: #334155;\n    --mfb-border-strong: #475569;\n    --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.55), 0 8px 20px rgba(0, 0, 0, 0.40);\n  }\n}\n\n/* FAB — 48px circle (down from Material's 56px) for a more discrete\n   presence on client surfaces (matches PNR's 48px pattern). Mhosaic\n   soft-black background with a light steel-blue lucide-bug glyph;\n   custom SVG inlined (no emoji — emoji renders inconsistently across\n   OSes and can't inherit color). Two-layer elevation, scale-on-press. */\n.fab {\n  position: fixed;\n  bottom: 24px;\n  right: 24px;\n  width: 48px;\n  height: 48px;\n  border-radius: 999px;\n  background: var(--mfb-fab-bg);\n  color: var(--mfb-fab-icon);\n  border: none;\n  cursor: pointer;\n  /* Two-layer elevation: ambient (soft, large) + key (tighter, near). */\n  box-shadow:\n    0 4px 12px rgba(0, 0, 0, 0.08),\n    0 2px 4px rgba(0, 0, 0, 0.12);\n  display: grid;\n  place-items: center;\n  transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1),\n              box-shadow 180ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.fab:hover {\n  transform: translateY(-2px);\n  box-shadow:\n    0 8px 24px rgba(0, 0, 0, 0.12),\n    0 3px 6px rgba(0, 0, 0, 0.16);\n}\n.fab:active {\n  transform: translateY(0) scale(0.96);\n  box-shadow:\n    0 3px 8px rgba(0, 0, 0, 0.10),\n    0 1px 2px rgba(0, 0, 0, 0.14);\n}\n.fab:focus-visible {\n  outline: 2px solid var(--mfb-fab-icon);\n  outline-offset: 3px;\n  box-shadow:\n    0 0 0 4px var(--mfb-fab-bg),\n    0 4px 12px rgba(0, 0, 0, 0.08),\n    0 2px 4px rgba(0, 0, 0, 0.12);\n}\n@media (prefers-color-scheme: dark) {\n  /* Slightly desaturate so the FAB doesn't glow against dark backgrounds. */\n  .fab { box-shadow:\n    0 4px 12px rgba(0, 0, 0, 0.32),\n    0 2px 4px rgba(0, 0, 0, 0.40); }\n}\n@media (prefers-reduced-motion: reduce) {\n  .fab { transition: none; }\n  .fab:hover, .fab:active { transform: none; }\n}\n\n/* Backdrop — fade in with a slight blur for depth. The blur gives the\n   modal that \"page that opens\" weight: the underlying page recedes\n   visually so the widget feels foregrounded, not pasted on. */\n.backdrop {\n  position: fixed;\n  inset: 0;\n  background: rgba(15, 23, 42, 0.55);\n  backdrop-filter: blur(6px);\n  -webkit-backdrop-filter: blur(6px);\n  display: grid;\n  place-items: center;\n  animation: mfb-backdrop-in 220ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n@keyframes mfb-backdrop-in {\n  from { opacity: 0; backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px); }\n  to { opacity: 1; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }\n}\n\n.modal {\n  background: var(--mfb-bg);\n  border-radius: var(--mfb-radius-lg);\n  box-shadow: var(--mfb-shadow-lg);\n  /* 720px is the \"page that opens\" sweet spot — wide enough to feel\n     like a workspace, narrow enough to not dominate the screen. Was\n     440px before; the bump trades a denser modal for a calmer canvas. */\n  width: min(720px, calc(100vw - var(--mfb-space-7)));\n  padding: var(--mfb-space-6);\n  display: flex;\n  flex-direction: column;\n  gap: var(--mfb-space-5);\n  position: relative;\n  /* Cap height with breathing room. The form scrolls internally if\n     the user attaches a tall screenshot. */\n  max-height: min(820px, calc(100vh - var(--mfb-space-7)));\n  overflow-y: auto;\n  /* Entrance: opacity-only. Avoids any geometric shift during the\n     animation so interaction tests that read boundingBox immediately\n     after the modal becomes visible get stable coordinates. 220ms\n     ease-out is snappy enough that human users barely register it. */\n  animation: mfb-modal-in 220ms ease-out;\n  /* Transition lives on the BASE rule with a SHORTER duration so the\n     \"back\" trip (Board → other tab, where .is-expanded is removed)\n     feels snappy. The grow direction overrides this with a slower\n     curve in .modal.is-expanded below — the asymmetry is intentional:\n     a slow grow reads as a transport, a slow shrink reads as\n     hesitation. */\n  transition:\n    width 200ms cubic-bezier(0.4, 0, 0.2, 1),\n    height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n    max-height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n    padding 200ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes mfb-modal-in {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n/* Mobile: full-height sheet that slides up from the bottom. The\n   slide-up gesture matches platform-native sheets users already know. */\n@media (max-width: 640px) {\n  .backdrop { place-items: end center; }\n  .modal {\n    width: 100vw;\n    height: calc(100vh - var(--mfb-space-7));\n    max-height: none;\n    border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n    padding: var(--mfb-space-5) var(--mfb-space-4) var(--mfb-space-4);\n    animation: mfb-sheet-up 280ms cubic-bezier(0.16, 1, 0.3, 1);\n  }\n}\n\n@keyframes mfb-sheet-up {\n  from { transform: translateY(100%); }\n  to { transform: translateY(0); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .backdrop, .modal { animation: none; }\n}\n\n.modal h2 {\n  margin: 0;\n  font-size: var(--mfb-text-xl);\n  font-weight: 600;\n  padding-right: var(--mfb-space-6);\n  letter-spacing: -0.015em;\n  line-height: 1.2;\n}\n\n.modal form {\n  display: flex;\n  flex-direction: column;\n  gap: var(--mfb-space-5);\n  /* Match the entrance motion the Board/MineList/Changelog containers\n   * use so switching tabs (especially Board → Send, where the modal\n   * is mid-shrink) doesn't show a content snap behind the transition.\n   * 40ms delay lets the modal's shrink finish first. */\n  animation: mfb-board-in 220ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n  .modal form { animation: none; }\n}\n\n.modal-close {\n  position: absolute;\n  top: var(--mfb-space-3);\n  right: var(--mfb-space-3);\n  width: 36px;\n  height: 36px;\n  display: grid;\n  place-items: center;\n  background: transparent;\n  border: none;\n  border-radius: var(--mfb-radius);\n  color: var(--mfb-text-muted);\n  font: inherit;\n  font-size: 22px;\n  line-height: 1;\n  cursor: pointer;\n  transition: background 120ms ease, color 120ms ease;\n}\n.modal-close:hover { background: var(--mfb-surface); color: var(--mfb-text); }\n.modal-close:focus-visible { outline: 2px solid var(--mfb-accent); outline-offset: 2px; }\n\n/* Each field group: label + input stacked with breathing room.\n   The 24px modal-level gap separates groups. */\n.field { display: flex; flex-direction: column; gap: var(--mfb-space-2); font-size: var(--mfb-text-sm); }\n\n.field label {\n  color: var(--mfb-text-muted);\n  font-weight: 500;\n  font-size: var(--mfb-text-xs);\n  letter-spacing: 0.03em;\n  text-transform: uppercase;\n}\n\n.field input, .field select, .field textarea {\n  font-family: inherit;\n  font-size: var(--mfb-text-base);\n  color: inherit;\n  padding: 11px 14px;\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  background: var(--mfb-surface);\n  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;\n}\n\n.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--mfb-border-strong); }\n.field input:focus, .field select:focus, .field textarea:focus {\n  outline: none;\n  border-color: var(--mfb-accent);\n  background: var(--mfb-bg);\n  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 22%, transparent);\n}\n\n.field select {\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n  padding-right: 28px;\n  background-image: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/></svg>\");\n  background-repeat: no-repeat;\n  background-position: right 10px center;\n}\n\n.field textarea { min-height: 120px; resize: vertical; line-height: 1.5; }\n\n.row { display: flex; gap: var(--mfb-space-3); }\n.row > * { flex: 1; }\n\n/* Footer: subtle separation via border, slightly more vertical space. */\n.actions {\n  display: flex;\n  gap: var(--mfb-space-2);\n  justify-content: flex-end;\n  padding-top: var(--mfb-space-4);\n  margin-top: var(--mfb-space-1);\n  border-top: 1px solid var(--mfb-border);\n}\n\n.btn {\n  padding: 10px 18px;\n  border-radius: var(--mfb-radius);\n  border: 1px solid var(--mfb-border);\n  background: var(--mfb-bg);\n  color: var(--mfb-text);\n  font: inherit;\n  font-size: var(--mfb-text-sm);\n  font-weight: 500;\n  cursor: pointer;\n  transition: background 120ms ease, border-color 120ms ease, transform 80ms ease;\n}\n.btn:hover { background: var(--mfb-surface); border-color: var(--mfb-border-strong); }\n.btn:active { transform: scale(0.98); }\n\n.btn--primary {\n  background: var(--mfb-accent);\n  color: var(--mfb-accent-contrast);\n  border-color: var(--mfb-accent);\n}\n.btn--primary:hover {\n  background: color-mix(in srgb, var(--mfb-accent) 88%, black);\n  border-color: color-mix(in srgb, var(--mfb-accent) 88%, black);\n}\n\n/* Subdued button — borrows accent text but transparent background. Used\n * for the \"Capture this page\" alt-action that sits below the dropzone,\n * where a full --primary would compete with the form's main submit. */\n.btn--ghost {\n  background: transparent;\n  color: var(--mfb-accent);\n  border-color: color-mix(in srgb, var(--mfb-accent) 40%, transparent);\n}\n.btn--ghost:hover {\n  background: color-mix(in srgb, var(--mfb-accent) 8%, transparent);\n  border-color: var(--mfb-accent);\n}\n\n.btn[disabled] { opacity: 0.6; cursor: not-allowed; }\n\n.error { color: #dc2626; font-size: 13px; }\n.success { color: #059669; font-size: 13px; }\n\n/* ---- v0.6.0: manual screenshot upload + annotator -------------------- */\n\n.mfb-sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n\n/* Dropzone — bigger, more inviting; the icon + heading + sub-line\n   hierarchy makes it feel like a deliberate target, not an afterthought. */\n.screenshot-dropzone {\n  border: 1.5px dashed var(--mfb-border-strong);\n  border-radius: var(--mfb-radius-lg);\n  padding: var(--mfb-space-6) var(--mfb-space-4);\n  text-align: center;\n  cursor: pointer;\n  background: var(--mfb-surface);\n  transition: border-color 160ms ease, background 160ms ease, transform 120ms ease;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: var(--mfb-space-2);\n}\n.screenshot-dropzone:hover {\n  border-color: var(--mfb-accent);\n  background: color-mix(in srgb, var(--mfb-accent) 4%, var(--mfb-surface));\n}\n.screenshot-dropzone.is-dragover {\n  border-color: var(--mfb-accent);\n  border-style: solid;\n  background: color-mix(in srgb, var(--mfb-accent) 10%, var(--mfb-surface));\n  transform: scale(1.005);\n}\n.screenshot-dropzone:focus-visible {\n  outline: 2px solid var(--mfb-accent);\n  outline-offset: 2px;\n}\n.screenshot-icon {\n  color: var(--mfb-text-muted);\n  opacity: 0.7;\n  margin-bottom: var(--mfb-space-1);\n  transition: color 160ms ease, opacity 160ms ease;\n}\n.screenshot-dropzone:hover .screenshot-icon,\n.screenshot-dropzone.is-dragover .screenshot-icon {\n  color: var(--mfb-accent);\n  opacity: 1;\n}\n.screenshot-cta { font-size: var(--mfb-text-base); color: var(--mfb-text); font-weight: 500; }\n.screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }\n.screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }\n\n.screenshot-preview {\n  position: relative;\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  overflow: hidden;\n  background: var(--mfb-surface);\n  display: flex;\n  flex-direction: column;\n}\n.screenshot-preview img {\n  display: block;\n  width: 100%;\n  height: auto;\n  max-height: 280px;\n  object-fit: contain;\n  background: #1a1a1a;\n}\n.screenshot-preview-actions {\n  display: flex;\n  gap: var(--mfb-space-2);\n  padding: var(--mfb-space-2);\n  border-top: 1px solid var(--mfb-border);\n  background: var(--mfb-bg);\n}\n.screenshot-preview-actions .btn {\n  flex: 1;\n  padding: 8px 12px;\n  font-size: var(--mfb-text-sm);\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 6px;\n}\n.screenshot-preview-actions .btn--primary {\n  background: var(--mfb-bg);\n  color: var(--mfb-accent);\n  border-color: var(--mfb-accent);\n}\n.screenshot-preview-actions .btn--primary:hover {\n  background: color-mix(in srgb, var(--mfb-accent) 8%, var(--mfb-bg));\n  border-color: var(--mfb-accent);\n  color: var(--mfb-accent);\n}\n.screenshot-remove {\n  /* Kept for legacy markup that uses the old corner-cross button. */\n  position: absolute;\n  top: 6px;\n  right: 6px;\n  width: 26px;\n  height: 26px;\n  display: grid;\n  place-items: center;\n  background: rgba(255, 255, 255, 0.9);\n  border: 1px solid var(--mfb-border);\n  border-radius: 999px;\n  font-size: 18px;\n  line-height: 1;\n  cursor: pointer;\n  color: #111827;\n}\n.screenshot-remove:hover { background: #fff; }\n.screenshot-annotate {\n  /* Kept for legacy; new markup uses .screenshot-preview-actions. */\n  border-radius: 0;\n  border-width: 0;\n  border-top: 1px solid var(--mfb-border);\n  background: var(--mfb-bg);\n}\n.screenshot-annotate:hover { background: var(--mfb-surface); }\n\n.page-context {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 11px;\n  color: var(--mfb-text-muted);\n}\n.page-context-label {\n  text-transform: uppercase;\n  font-weight: 600;\n  letter-spacing: 0.04em;\n  font-size: 10px;\n}\n.page-context-url {\n  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n  font-size: 11px;\n  color: var(--mfb-text-muted);\n  flex: 1;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n/* Annotator modal — sits above the feedback modal (z-index +1). */\n\n.annotator-backdrop {\n  position: fixed;\n  inset: 0;\n  background: rgba(0, 0, 0, 0.78);\n  display: grid;\n  place-items: center;\n  z-index: 1;\n  padding: 12px;\n}\n.annotator {\n  position: relative;\n  background: var(--mfb-bg);\n  color: var(--mfb-text);\n  border-radius: calc(var(--mfb-radius) * 1.5);\n  width: min(960px, 96vw);\n  max-height: calc(100vh - 24px);\n  display: flex;\n  flex-direction: column;\n  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);\n}\n.annotator-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 10px 14px;\n  border-bottom: 1px solid var(--mfb-border);\n  font-size: 13px;\n  font-weight: 600;\n}\n.annotator-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 8px;\n  padding: 8px 14px;\n  border-bottom: 1px solid var(--mfb-border);\n}\n.annotator-tools, .annotator-colors { display: flex; gap: 4px; }\n.annotator-sep {\n  display: inline-block;\n  width: 1px;\n  height: 18px;\n  background: var(--mfb-border);\n  margin: 0 4px;\n}\n.annotator-spacer { flex: 1; }\n.annotator-tool {\n  width: 30px;\n  height: 30px;\n  display: grid;\n  place-items: center;\n  background: var(--mfb-bg);\n  color: var(--mfb-text);\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  cursor: pointer;\n}\n.annotator-tool:hover { background: var(--mfb-surface); }\n.annotator-tool.is-active {\n  background: var(--mfb-text);\n  color: var(--mfb-bg);\n  border-color: var(--mfb-text);\n}\n.annotator-color {\n  width: 24px;\n  height: 24px;\n  border-radius: 999px;\n  border: 2px solid var(--mfb-border);\n  cursor: pointer;\n  padding: 0;\n  transition: transform 120ms ease;\n  position: relative;\n}\n.annotator-color.is-active {\n  transform: scale(1.12);\n  border-color: var(--mfb-text);\n}\n/* Color-picker swatch: rainbow gradient fill so users see this isn't\n   a preset, plus a tiny native <input type=\"color\"> overlaid invisibly. */\n.annotator-color-picker {\n  display: grid;\n  place-items: center;\n  background: conic-gradient(from 180deg, #ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6, #ec4899, #ef4444);\n  overflow: hidden;\n}\n.annotator-color-picker input[type=\"color\"] {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  border: 0;\n  padding: 0;\n  margin: 0;\n  opacity: 0;\n  cursor: pointer;\n}\n.annotator-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  height: 30px;\n  padding: 0 10px;\n  font: inherit;\n  font-size: 12px;\n  background: var(--mfb-bg);\n  color: var(--mfb-text);\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  cursor: pointer;\n}\n.annotator-btn:hover { background: var(--mfb-surface); }\n.annotator-btn[disabled] { opacity: 0.5; cursor: not-allowed; }\n.annotator-count { font-size: 11px; color: var(--mfb-text-muted); }\n.annotator-canvas-wrap {\n  flex: 1;\n  overflow: auto;\n  background: #1a1a1a;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 12px;\n  min-height: 200px;\n}\n.annotator-canvas {\n  cursor: crosshair;\n  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);\n  background: #fff;\n}\n.annotator-loading {\n  color: rgba(255, 255, 255, 0.7);\n  font-size: 13px;\n}\n.annotator-footer {\n  display: flex;\n  align-items: center;\n  justify-content: flex-end;\n  gap: 8px;\n  padding: 10px 14px;\n  border-top: 1px solid var(--mfb-border);\n}\n\n/* ---- v0.7: tabs + reader UI -------------------------------------- */\n\n.tab-strip {\n  display: flex;\n  gap: var(--mfb-space-1);\n  border-bottom: 1px solid var(--mfb-border);\n  margin: 0;\n  padding: 0;\n}\n.tab-button {\n  appearance: none;\n  background: transparent;\n  border: 0;\n  border-bottom: 2px solid transparent;\n  padding: var(--mfb-space-3) var(--mfb-space-4);\n  margin-bottom: -1px;\n  font: inherit;\n  font-size: var(--mfb-text-sm);\n  font-weight: 500;\n  color: var(--mfb-text-muted);\n  cursor: pointer;\n  transition: color 120ms ease, border-color 120ms ease;\n}\n.tab-button:hover { color: var(--mfb-text); }\n.tab-button.is-active {\n  color: var(--mfb-accent);\n  border-bottom-color: var(--mfb-accent);\n}\n.tab-button[aria-selected=\"true\"] { font-weight: 600; }\n\n.mine-list {\n  display: flex;\n  flex-direction: column;\n  gap: var(--mfb-space-3);\n  /* Matches .board-view's entrance so switching tabs feels like a real\n   * page transition rather than a flicker. */\n  animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n  .mine-list { animation: none; }\n}\n.mine-list-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n.mine-list-header h2 { margin: 0; font-size: var(--mfb-text-lg); font-weight: 600; letter-spacing: -0.01em; }\n.mine-loading { color: var(--mfb-text-muted); font-size: 13px; }\n.mine-empty {\n  text-align: center;\n  padding: 24px 12px;\n  color: var(--mfb-text-muted);\n  font-size: 13px;\n}\n.mine-empty strong { display: block; color: var(--mfb-text); margin-bottom: 4px; }\n\n.mine-rows {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  max-height: 380px;\n  overflow-y: auto;\n}\n.mine-row {\n  appearance: none;\n  text-align: left;\n  background: var(--mfb-surface);\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  padding: 10px 12px;\n  font: inherit;\n  color: inherit;\n  cursor: pointer;\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n  width: 100%;\n  transition:\n    border-color 140ms ease,\n    background 140ms ease,\n    transform 100ms ease,\n    box-shadow 140ms ease;\n}\n.mine-row:hover {\n  border-color: var(--mfb-border-strong);\n  background: var(--mfb-bg);\n  transform: translateY(-1px);\n  box-shadow: var(--mfb-shadow);\n}\n.mine-row:active { transform: scale(0.997); }\n.mine-row:focus-visible {\n  outline: 2px solid var(--mfb-accent);\n  outline-offset: 2px;\n}\n.mine-row-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.mine-row-preview {\n  font-size: 13px;\n  color: var(--mfb-text);\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n.mine-row-meta {\n  display: flex;\n  gap: 6px;\n  font-size: 11px;\n  color: var(--mfb-text-muted);\n}\n\n.pill {\n  display: inline-block;\n  font-size: 10px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  padding: 2px 8px;\n  border-radius: 999px;\n  border: 1px solid transparent;\n}\n.pill-status { background: #eff6ff; color: #1e40af; border-color: #dbeafe; }\n.pill-status--in_progress { background: #fffbeb; color: #92400e; border-color: #fde68a; }\n.pill-status--awaiting_validation { background: #faf5ff; color: #6b21a8; border-color: #e9d5ff; }\n.pill-status--closed { background: #ecfdf5; color: #065f46; border-color: #a7f3d0; }\n.pill-status--rejected, .pill-status--wontfix, .pill-status--duplicate { background: #f3f4f6; color: #374151; border-color: #e5e7eb; }\n.pill-type { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity--blocker { background: #fef2f2; color: #991b1b; border-color: #fecaca; }\n.pill-severity--high { background: #fff7ed; color: #9a3412; border-color: #fed7aa; }\n.pill-severity--medium { background: #fefce8; color: #854d0e; border-color: #fef08a; }\n.pill-severity--low { background: var(--mfb-surface); color: var(--mfb-text-muted); }\n\n.report-detail { display: flex; flex-direction: column; gap: 12px; }\n.report-detail-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 8px;\n}\n.report-detail-body { display: flex; flex-direction: column; gap: 10px; }\n.report-detail-description {\n  font-size: 14px;\n  white-space: pre-wrap;\n  margin: 0;\n}\n.report-detail-screenshot {\n  display: block;\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  overflow: hidden;\n  background: #1a1a1a;\n}\n.report-detail-screenshot img {\n  display: block;\n  width: 100%;\n  max-height: 200px;\n  object-fit: contain;\n}\n.report-detail-section {\n  font-size: 12px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: var(--mfb-text-muted);\n  margin: 8px 0 4px;\n  font-weight: 600;\n}\n\n.report-detail-context {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  padding: 10px 12px;\n  background: var(--mfb-surface);\n  border-radius: var(--mfb-radius);\n  border: 1px solid var(--mfb-border);\n}\n.report-detail-context-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.report-detail-context-line {\n  display: flex;\n  align-items: baseline;\n  gap: 8px;\n  font-size: 12px;\n  color: var(--mfb-text);\n}\n.report-detail-context-label {\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  font-size: 10px;\n  font-weight: 600;\n  color: var(--mfb-text-muted);\n  min-width: 56px;\n}\n.report-detail-context-url {\n  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n  color: var(--mfb-accent);\n  text-decoration: none;\n  font-size: 11px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.report-detail-context-url:hover { text-decoration: underline; }\n.pill-capture { background: var(--mfb-bg); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.report-detail-empty {\n  font-size: 12px;\n  color: var(--mfb-text-muted);\n  margin: 0;\n}\n\n.report-comments {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  max-height: 300px;\n  overflow-y: auto;\n}\n\n.comment-bubble {\n  padding: 8px 10px;\n  border-radius: 12px;\n  max-width: 88%;\n  font-size: 13px;\n  line-height: 1.4;\n}\n.comment-bubble.is-mine {\n  align-self: flex-end;\n  background: var(--mfb-accent);\n  color: var(--mfb-accent-contrast);\n}\n.comment-bubble.is-other {\n  align-self: flex-start;\n  background: var(--mfb-surface);\n  border: 1px solid var(--mfb-border);\n  color: var(--mfb-text);\n}\n.comment-author {\n  font-size: 10px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  font-weight: 600;\n  margin-bottom: 2px;\n}\n.comment-author--mcp { color: #6b21a8; }\n.comment-author--staff { color: #1e40af; }\n.comment-author--system { color: var(--mfb-text-muted); }\n.comment-body { white-space: pre-wrap; }\n.comment-time {\n  font-size: 10px;\n  margin-top: 4px;\n  opacity: 0.7;\n}\n\n.report-compose {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  margin-top: 4px;\n}\n.report-compose textarea {\n  font: inherit;\n  font-size: 13px;\n  padding: 8px 10px;\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  background: var(--mfb-surface);\n  color: inherit;\n  min-height: 64px;\n  resize: vertical;\n}\n.report-compose-actions {\n  display: flex;\n  justify-content: flex-end;\n  gap: 6px;\n}\n\n/* v0.15.3 — teammate-viewing notice. Shown in place of the compose\n * box when a non-submitter opens a project-wide row from the Board\n * tab. Replies are private to the submitter; we surface this once\n * (calm tone, neutral surface) so the viewer understands why no\n * Reply box appears, instead of leaving them wondering. */\n.report-detail-teammate-notice {\n  margin: 0;\n  padding: var(--mfb-space-3) var(--mfb-space-4);\n  background: var(--mfb-surface);\n  border: 1px dashed var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  font-size: var(--mfb-text-sm);\n  color: var(--mfb-text-muted);\n  font-style: italic;\n}\n\n/* v0.15.3 — friendly \"report not available\" empty state. Replaces\n * the raw API error message (e.g. getReport failed: 404 …) that\n * used to leak from setError(err.message) straight into the DOM. */\n.report-detail-empty-state {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: var(--mfb-space-3);\n  padding: var(--mfb-space-5);\n  background: var(--mfb-surface);\n  border-radius: var(--mfb-radius);\n  border: 1px solid var(--mfb-border);\n}\n.report-detail-empty-state-title {\n  margin: 0;\n  font-size: var(--mfb-text-md);\n  font-weight: 600;\n  color: var(--mfb-text);\n}\n.report-detail-empty-state-body {\n  margin: 0;\n  font-size: var(--mfb-text-sm);\n  color: var(--mfb-text-muted);\n}\n\n/* Status history — read-only audit trail visible to the submitter.\n   Timeline of who flipped the status and when (operator vs. agent vs.\n   system). Bordered, neutral surface so it doesn't compete visually\n   with the conversation thread above. */\n.report-detail-history {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  background: var(--mfb-surface);\n  padding: 8px 10px;\n}\n.report-detail-history .report-detail-section {\n  margin: 0;\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: var(--mfb-text-muted);\n}\n.status-history {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n}\n.status-history-row {\n  display: grid;\n  grid-template-columns: auto 1fr auto;\n  gap: 8px;\n  align-items: center;\n  font-size: 11px;\n}\n.status-history-time {\n  color: var(--mfb-text-muted);\n  font-variant-numeric: tabular-nums;\n  white-space: nowrap;\n}\n.status-history-transition {\n  display: inline-flex;\n  gap: 4px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n.status-history-arrow {\n  color: var(--mfb-text-muted);\n  font-size: 11px;\n}\n.status-history-source {\n  font-size: 10px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  font-weight: 600;\n  white-space: nowrap;\n}\n.status-history-source--mcp { color: #6b21a8; }\n.status-history-source--user { color: #1e40af; }\n.status-history-source--system { color: var(--mfb-text-muted); }\n\n/* Transparency drawer — closed by default. The user can click open to\n   verify what their browser sent: device, errors, console tail, network\n   tail. Read-only; purely a trust-building gesture. */\n.report-detail-tech {\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  background: var(--mfb-surface);\n}\n.report-detail-tech > summary {\n  cursor: pointer;\n  padding: 8px 10px;\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: var(--mfb-text-muted);\n  user-select: none;\n  list-style: none;\n}\n.report-detail-tech > summary::-webkit-details-marker { display: none; }\n.report-detail-tech > summary::before {\n  content: '▸';\n  display: inline-block;\n  margin-right: 6px;\n  transition: transform 120ms;\n}\n.report-detail-tech[open] > summary::before { transform: rotate(90deg); }\n.tech-body {\n  padding: 0 10px 10px 10px;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n}\n.tech-section h4 {\n  margin: 0 0 4px 0;\n  font-size: 10px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: var(--mfb-text-muted);\n  font-weight: 600;\n}\n.tech-device {\n  display: grid;\n  grid-template-columns: max-content 1fr;\n  column-gap: 8px;\n  row-gap: 2px;\n  margin: 0;\n  font-size: 11px;\n  font-variant-numeric: tabular-nums;\n}\n.tech-device dt { color: var(--mfb-text-muted); }\n.tech-device dd { margin: 0; }\n.tech-errors, .tech-console, .tech-network {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n  font-size: 11px;\n  font-family: ui-monospace, \"SF Mono\", Menlo, monospace;\n  max-height: 200px;\n  overflow-y: auto;\n}\n.tech-errors li { padding: 4px 0; border-top: 1px solid var(--mfb-border); }\n.tech-errors li:first-child { border-top: 0; }\n.tech-errors-msg { color: #991b1b; font-weight: 600; }\n.tech-errors-stack {\n  margin: 4px 0 0 0;\n  padding: 4px 6px;\n  background: var(--mfb-bg);\n  border-radius: 4px;\n  font-size: 10px;\n  white-space: pre-wrap;\n  color: var(--mfb-text-muted);\n  max-height: 120px;\n  overflow-y: auto;\n}\n.tech-console-row {\n  display: grid;\n  grid-template-columns: 48px 1fr;\n  gap: 6px;\n  padding: 1px 0;\n}\n.tech-console-level {\n  text-transform: uppercase;\n  font-size: 9px;\n  font-weight: 600;\n  align-self: center;\n  color: var(--mfb-text-muted);\n}\n.tech-console-row--warn .tech-console-level { color: #92400e; }\n.tech-console-row--error .tech-console-level { color: #991b1b; }\n.tech-console-row--info .tech-console-level { color: #1e40af; }\n.tech-console-msg { word-break: break-word; }\n.tech-network-row {\n  display: grid;\n  grid-template-columns: 48px 56px 1fr 56px;\n  gap: 6px;\n  padding: 1px 0;\n}\n.tech-network-row--fail .tech-network-status { color: #991b1b; font-weight: 700; }\n.tech-network-status { font-variant-numeric: tabular-nums; }\n.tech-network-method { color: var(--mfb-text-muted); }\n.tech-network-url { word-break: break-all; }\n.tech-network-time { text-align: right; color: var(--mfb-text-muted); font-variant-numeric: tabular-nums; }\n\n/* KPI strip — four pills above the My Reports list. Each is clickable\n   to filter the rows below. Borrowed from thePnr's FeedbackKPICards. */\n.kpi-strip {\n  display: grid;\n  grid-template-columns: repeat(4, 1fr);\n  gap: 6px;\n}\n.kpi-cell {\n  appearance: none;\n  background: var(--mfb-bg);\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  padding: 10px 10px 10px 14px;\n  cursor: pointer;\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 2px;\n  font-family: inherit;\n  color: inherit;\n  position: relative;\n  overflow: hidden;\n  transition:\n    background 140ms ease,\n    border-color 140ms ease,\n    transform 100ms ease,\n    box-shadow 140ms ease;\n}\n.kpi-cell::before {\n  /* Vertical tone accent — matches the Board KPI cards' visual rhythm so\n   * the two surfaces feel like they belong to the same product. v0.12\n   * polish pass. */\n  content: '';\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 3px;\n  height: 100%;\n  background: var(--mfb-border);\n  transition: background 140ms ease;\n}\n.kpi-cell--new::before { background: #3b82f6; }\n.kpi-cell--in_progress::before { background: #f59e0b; }\n.kpi-cell--awaiting_validation::before { background: #a855f7; }\n.kpi-cell--resolved::before { background: #10b981; }\n.kpi-cell:hover {\n  background: var(--mfb-surface);\n  transform: translateY(-1px);\n  box-shadow: var(--mfb-shadow);\n}\n.kpi-cell.is-active {\n  border-color: var(--mfb-accent);\n  background: rgba(59, 130, 246, 0.08);\n}\n.kpi-cell.is-active.kpi-cell--in_progress { border-color: #d97706; background: rgba(251, 191, 36, 0.10); }\n.kpi-cell.is-active.kpi-cell--awaiting_validation { border-color: #9333ea; background: rgba(168, 85, 247, 0.10); }\n.kpi-cell.is-active.kpi-cell--resolved { border-color: #059669; background: rgba(16, 185, 129, 0.10); }\n.kpi-value {\n  font-size: 18px;\n  font-weight: 700;\n  font-variant-numeric: tabular-nums;\n  line-height: 1;\n}\n.kpi-label {\n  font-size: 10px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: var(--mfb-text-muted);\n}\n\n/* Changelog (\"This week\") — week-grouped resolved-report timeline.\n   Borrowed from thePnr's FeedbackChangelogView; the header per group\n   reads \"● Week of {date}  ━━━  {n} resolved\". */\n.changelog-groups {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n  .changelog-groups { animation: none; }\n}\n.changelog-group {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n}\n.changelog-group-header {\n  display: grid;\n  grid-template-columns: auto auto 1fr auto;\n  gap: 6px;\n  align-items: center;\n  font-size: 11px;\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  color: var(--mfb-text-muted);\n}\n.changelog-group-marker {\n  color: var(--mfb-accent);\n  font-size: 14px;\n  line-height: 1;\n}\n.changelog-group-label { font-weight: 600; }\n.changelog-group-rule {\n  height: 1px;\n  background: var(--mfb-border);\n  align-self: center;\n}\n.changelog-group-count {\n  font-variant-numeric: tabular-nums;\n  font-weight: 600;\n}\n\n/* ---- v0.12: Board tab + expanded modal (\"transport\" feel) ------------ */\n\n/* When the Board tab is active, the modal grows toward near-fullscreen\n * on desktop and full-screen on mobile. The CSS transition gives the\n * sense of clicking through to a real page rather than switching tabs.\n * Width/height/max-* are animated together; padding too, since the\n * board's denser layout needs less of the modal's default padding. */\n.modal.is-expanded {\n  /* Margin around the expanded board view. --mfb-space-7 (48px) matches\n   * the breathing room of the default modal — 24px top + 24px bottom +\n   * 24px on each side — so the panel reads as \"near-fullscreen with a\n   * visible frame\" instead of \"the whole viewport, content cut off\".\n   *\n   * The base .modal rule uses the browser default content-box, so\n   * without an override the 24px padding would be ADDED to the\n   * calc(100vh - 48px) height — the rendered box would exactly equal\n   * the viewport again, defeating the margin. Force border-box so\n   * padding lives INSIDE the height we set. */\n  box-sizing: border-box;\n  width: min(1280px, calc(100vw - var(--mfb-space-7)));\n  max-height: calc(100vh - var(--mfb-space-7));\n  height: calc(100vh - var(--mfb-space-7));\n  padding: var(--mfb-space-5);\n  transition:\n    width 320ms cubic-bezier(0.22, 1, 0.36, 1),\n    height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n    max-height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n    padding 320ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n@media (prefers-reduced-motion: reduce) {\n  .modal.is-expanded { transition: none; }\n}\n.backdrop.is-expanded { /* hook for any backdrop-level overrides */ }\n\n@media (max-width: 640px) {\n  /* On mobile the sheet still snaps to the bottom edge and stretches\n   * almost the full viewport — 24px gap from the top is enough to\n   * convey \"this is a panel, not a takeover\", but we don't pull margin\n   * off the sides where users expect the sheet to fill the viewport. */\n  .modal.is-expanded {\n    width: 100vw;\n    height: calc(100vh - var(--mfb-space-5));\n    border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n  }\n}\n\n/* Decorative purple accent on the Board tab button so users discover\n * it's a different surface — subtle, not loud. */\n.tab-button--board.is-active {\n  color: #6b21a8;\n  box-shadow: inset 0 -2px 0 0 #a855f7;\n}\n.tab-button--board:not(.is-active):hover {\n  color: #6b21a8;\n}\n\n/* ---- Board layout ---- */\n\n.board-view {\n  display: flex;\n  flex-direction: column;\n  gap: var(--mfb-space-4);\n  flex: 1 1 auto;\n  min-height: 0; /* allow inner panes to scroll */\n  animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@keyframes mfb-board-in {\n  from { opacity: 0; transform: translateY(8px); }\n  to   { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n  .board-view { animation: none; }\n}\n\n.board-header {\n  display: flex;\n  flex-direction: column;\n  gap: var(--mfb-space-4);\n}\n.board-header-title {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n}\n.board-header-emoji {\n  font-size: 26px;\n  line-height: 1;\n}\n.board-header-h {\n  margin: 0;\n  font-size: var(--mfb-text-2xl);\n  font-weight: 600;\n  letter-spacing: -0.01em;\n}\n.board-header-sub {\n  margin: 2px 0 0 0;\n  color: var(--mfb-text-muted);\n  font-size: var(--mfb-text-sm);\n}\n\n/* KPI strip — 4 cards, equal width, subtle tone-tints by bucket. */\n.board-kpi-strip {\n  display: grid;\n  grid-template-columns: repeat(4, 1fr);\n  gap: 12px;\n}\n.board-kpi-card {\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  padding: 14px 16px;\n  background: var(--mfb-bg);\n  display: flex;\n  flex-direction: column;\n  gap: 4px;\n  position: relative;\n  overflow: hidden;\n  transition: transform 160ms ease, box-shadow 160ms ease;\n}\n.board-kpi-card::after {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 3px;\n  height: 100%;\n  background: var(--mfb-border);\n  transition: background 160ms ease;\n}\n.board-kpi-card:hover {\n  transform: translateY(-1px);\n  box-shadow: var(--mfb-shadow);\n}\n.board-kpi-card.tone-new::after { background: #3b82f6; }\n.board-kpi-card.tone-progress::after { background: #f59e0b; }\n.board-kpi-card.tone-validation::after { background: #a855f7; }\n.board-kpi-card.tone-rate::after { background: #10b981; }\n.board-kpi-value {\n  font-size: 28px;\n  font-weight: 700;\n  line-height: 1;\n  letter-spacing: -0.02em;\n  color: var(--mfb-text);\n  font-variant-numeric: tabular-nums;\n}\n.board-kpi-label {\n  font-size: var(--mfb-text-xs);\n  color: var(--mfb-text-muted);\n  text-transform: uppercase;\n  letter-spacing: 0.04em;\n  font-weight: 500;\n}\n.board-kpi-strip.is-loading .board-kpi-value {\n  opacity: 0.35;\n}\n@media (max-width: 720px) {\n  .board-kpi-strip { grid-template-columns: repeat(2, 1fr); }\n}\n\n/* Filter row */\n.board-filters {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n.board-filter-select,\n.board-filter-search {\n  padding: 6px 10px;\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  background: var(--mfb-bg);\n  color: var(--mfb-text);\n  font: inherit;\n  font-size: var(--mfb-text-sm);\n  height: 32px;\n  transition: border-color 120ms ease, box-shadow 120ms ease;\n}\n.board-filter-select:focus-visible,\n.board-filter-search:focus-visible {\n  outline: none;\n  border-color: var(--mfb-accent);\n  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n.board-filter-search { min-width: 180px; flex: 1 1 220px; }\n.board-filter-toggle {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: var(--mfb-text-sm);\n  color: var(--mfb-text);\n  cursor: pointer;\n  user-select: none;\n}\n.board-filter-toggle input { margin: 0; }\n.board-filter-clear {\n  background: transparent;\n  border: 0;\n  color: var(--mfb-text-muted);\n  font: inherit;\n  font-size: var(--mfb-text-sm);\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  cursor: pointer;\n  padding: 4px 6px;\n  border-radius: var(--mfb-radius);\n  transition: background 120ms ease, color 120ms ease;\n}\n.board-filter-clear:hover {\n  color: var(--mfb-text);\n  background: var(--mfb-surface);\n}\n\n/* Master/detail layout — two-column at >=900px, stacked below. */\n.board-body {\n  display: grid;\n  grid-template-columns: minmax(280px, 360px) 1fr;\n  gap: var(--mfb-space-4);\n  flex: 1 1 auto;\n  min-height: 0;\n}\n@media (max-width: 900px) {\n  .board-body { grid-template-columns: 1fr; }\n  .board-detail-wrap:not(.has-selection) { display: none; }\n}\n\n.board-list-wrap {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  min-height: 0;\n  overflow: hidden;\n}\n.board-list-count {\n  font-size: var(--mfb-text-xs);\n  color: var(--mfb-text-muted);\n  padding: 0 4px;\n}\n.board-list {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n  overflow-y: auto;\n  flex: 1 1 auto;\n  scrollbar-width: thin;\n  scrollbar-gutter: stable;\n}\n\n.board-row {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  width: 100%;\n  text-align: left;\n  padding: 12px 14px;\n  border-radius: var(--mfb-radius);\n  background: var(--mfb-bg);\n  border: 1px solid var(--mfb-border);\n  cursor: pointer;\n  font: inherit;\n  color: inherit;\n  /* Stagger reveal on first paint. */\n  animation: mfb-row-in 280ms ease-out both;\n  transition: border-color 140ms ease, background 140ms ease, transform 80ms ease;\n}\n.board-row:hover {\n  border-color: var(--mfb-border-strong);\n  background: var(--mfb-surface);\n}\n.board-row:active { transform: scale(0.997); }\n.board-row.is-selected {\n  border-color: var(--mfb-accent);\n  background: color-mix(in srgb, var(--mfb-accent) 6%, var(--mfb-bg));\n  box-shadow: 0 0 0 2px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n@keyframes mfb-row-in {\n  from { opacity: 0; transform: translateY(4px); }\n  to   { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n  .board-row { animation: none; }\n}\n/* Stagger first 6 rows so the list reveals top-down. */\n.board-row:nth-child(1) { animation-delay: 0ms; }\n.board-row:nth-child(2) { animation-delay: 40ms; }\n.board-row:nth-child(3) { animation-delay: 80ms; }\n.board-row:nth-child(4) { animation-delay: 120ms; }\n.board-row:nth-child(5) { animation-delay: 160ms; }\n.board-row:nth-child(6) { animation-delay: 200ms; }\n\n.board-row-badges {\n  display: flex;\n  gap: 6px;\n  align-items: center;\n  flex-wrap: wrap;\n}\n.board-row-description {\n  font-size: var(--mfb-text-sm);\n  color: var(--mfb-text);\n  line-height: 1.4;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n.board-row-meta {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  font-size: var(--mfb-text-xs);\n  color: var(--mfb-text-muted);\n  flex-wrap: wrap;\n}\n.board-row-author { font-weight: 500; color: var(--mfb-text); }\n.board-row-comments {\n  display: inline-flex;\n  align-items: center;\n  gap: 3px;\n  font-variant-numeric: tabular-nums;\n}\n.board-row-time { margin-left: auto; font-variant-numeric: tabular-nums; }\n\n/* Status / severity badges shared with the detail panel. The tones\n * borrow PNR's badge palette — calm pastels that don't fight content. */\n.board-row-badges .badge {\n  font-size: 11px;\n  font-weight: 500;\n  letter-spacing: 0.02em;\n  padding: 2px 8px;\n  border-radius: 999px;\n  background: #f3f4f6;\n  color: #374151;\n  text-transform: lowercase;\n}\n.badge.status-new { background: #dbeafe; color: #1e40af; }\n.badge.status-in_progress { background: #fef3c7; color: #92400e; }\n.badge.status-awaiting_validation { background: #ede9fe; color: #6b21a8; }\n.badge.status-closed { background: #d1fae5; color: #065f46; }\n.badge.status-rejected { background: #fee2e2; color: #991b1b; }\n.badge.status-duplicate { background: #f3f4f6; color: #4b5563; }\n.badge.status-wontfix { background: #f3f4f6; color: #4b5563; }\n.badge.severity-blocker { background: #fee2e2; color: #991b1b; }\n.badge.severity-high { background: #fee2e2; color: #b91c1c; }\n.badge.severity-medium { background: #fef3c7; color: #92400e; }\n.badge.severity-low { background: #e0f2fe; color: #075985; }\n\n/* Detail pane (within Board). Inherits from .report-detail.variant-board */\n.board-detail-wrap {\n  border: 1px solid var(--mfb-border);\n  border-radius: var(--mfb-radius);\n  background: var(--mfb-bg);\n  overflow-y: auto;\n  min-height: 0;\n  display: flex;\n  flex-direction: column;\n}\n.board-detail-empty {\n  flex: 1 1 auto;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 12px;\n  color: var(--mfb-text-muted);\n  padding: var(--mfb-space-5);\n  text-align: center;\n  font-size: var(--mfb-text-sm);\n}\n.board-detail-empty svg { opacity: 0.6; }\n\n.report-detail.variant-board { padding: 16px 18px; }\n.report-detail-header--board {\n  align-items: center;\n}\n.report-detail-back {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  height: 28px;\n  padding: 0 10px;\n}\n\n/* Empty + loading + error states */\n.board-empty {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 8px;\n  text-align: center;\n  padding: var(--mfb-space-5);\n  color: var(--mfb-text-muted);\n  flex: 1 1 auto;\n}\n.board-empty h3 {\n  margin: 0;\n  font-size: var(--mfb-text-base);\n  color: var(--mfb-text);\n}\n.board-empty p {\n  margin: 0;\n  font-size: var(--mfb-text-sm);\n}\n.board-error {\n  padding: 12px 14px;\n  border-radius: var(--mfb-radius);\n  background: #fef2f2;\n  color: #991b1b;\n  font-size: var(--mfb-text-sm);\n}\n\n/* Skeleton rows pulse subtly so the user knows something's coming. */\n.board-list-skeleton .skeleton-row {\n  cursor: default;\n  pointer-events: none;\n}\n.skeleton-line {\n  height: 12px;\n  background: linear-gradient(\n    90deg,\n    var(--mfb-surface) 0%,\n    color-mix(in srgb, var(--mfb-surface) 60%, var(--mfb-border)) 50%,\n    var(--mfb-surface) 100%\n  );\n  background-size: 200% 100%;\n  border-radius: 4px;\n  animation: mfb-skeleton 1400ms ease-in-out infinite;\n}\n.skeleton-badges { width: 60%; height: 14px; }\n.skeleton-text { width: 90%; }\n.skeleton-text.short { width: 60%; }\n@keyframes mfb-skeleton {\n  from { background-position: 100% 0; }\n  to   { background-position: -100% 0; }\n}\n@media (prefers-reduced-motion: reduce) {\n  .skeleton-line { animation: none; }\n}\n`\n","import { createApiClient } from './api/client'\nimport { installCapture } from './capture'\nimport { resolveStrings } from './widget/i18n'\nimport { mountWidget } from './widget/mount'\nimport type { FeedbackApi, FeedbackConfig, ReportPayload, ReportTransformer, UserIdentity } from './types'\n\nexport interface InternalConfig extends FeedbackConfig {\n  fetchImpl?: typeof fetch\n}\n\ninterface WindowWithGlobal extends Window {\n  mhosaicFeedback?: FeedbackApi\n}\n\n/** Default offset for the secondary QA FAB: center the 24px QA dot above the\n *  48px feedback FAB (bottom:24 right:24). → { right: 36, bottom: 84 }. */\nfunction defaultQaPosition(): { right: number; bottom: number } {\n  return { right: 24 + (48 - 24) / 2, bottom: 24 + 48 + 12 }\n}\n\nexport function createFeedback(config: InternalConfig): FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } {\n  const env = config.env ?? 'prod'\n  const locale =\n    config.locale ?? (typeof navigator !== 'undefined' ? navigator.language : undefined)\n  const strings = resolveStrings(\n    config.translations ?? {},\n    locale !== undefined ? { locale } : {},\n  )\n  const capture = installCapture({\n    ...(config.sanitizeUrl !== undefined && { sanitizeUrl: config.sanitizeUrl }),\n  })\n  let user: UserIdentity | undefined = config.user\n  let metadata: Record<string, unknown> = config.metadata ?? {}\n\n  const api = createApiClient({\n    apiKey: config.apiKey,\n    endpoint: config.endpoint,\n    ...(config.fetchImpl !== undefined && { fetch: config.fetchImpl }),\n    ...(config.beforeSend !== undefined && { beforeSend: config.beforeSend }),\n    // v0.13: the API client reads this on every request so a late\n    // `identify({userHash, exp, ...})` lights up signed headers\n    // immediately on subsequent calls. Returns null when the host\n    // hasn't supplied a signature (legacy unsigned path).\n    getSignedIdentity: () => {\n      if (!user || !user.userHash || !user.exp || !user.email) return null\n      return {\n        userHash: user.userHash,\n        exp: user.exp,\n        email: user.email,\n      }\n    },\n  })\n  const transformers: ReportTransformer[] = []\n\n  const host = document.createElement('div')\n  host.className = 'mhosaic-feedback'\n  if (config.attachTo) {\n    const attach = typeof config.attachTo === 'string' ? document.querySelector(config.attachTo) : config.attachTo\n    attach?.appendChild(host)\n  } else {\n    document.body.appendChild(host)\n  }\n\n  async function buildAndSubmit(values: {\n    description: string\n    feedback_type?: string\n    severity?: string\n    synthetic?: boolean\n    /** Screenshot the user explicitly attached — via dropzone, paste, file\n     *  picker, or the annotator. v0.12 dropped html2canvas-on-submit;\n     *  v0.7.1 dropped getDisplayMedia. If the user didn't attach anything,\n     *  the report ships without a screenshot. */\n    screenshot?: Blob\n    /** Capture-method label for the payload. Always \"manual\" now (v0.7.1);\n     *  the union type stays open against the backend schema, which still\n     *  recognizes legacy values on existing reports. */\n    capture_method?: 'manual'\n  }) {\n    // Auto-error reports never carry a screenshot — the captured ring\n    // buffer (errors, console, network) carries the signal, and the DOM\n    // is typically in an inconsistent state when a JS error fires anyway.\n    const manualScreenshot = values.synthetic ? undefined : values.screenshot\n    const technical_context = capture.snapshot()\n    // Surface identify()/setMetadata() values on the report. Without this\n    // the host-supplied user identity and metadata sit in closure forever\n    // and never reach the operator — a stack trace from a logged-in user\n    // looks anonymous on the dashboard.\n    //\n    // SECURITY: identify() may carry `userHash` + `exp` (the host's HMAC\n    // envelope used to attest the user's identity to the backend). That\n    // envelope is a bearer-style auth token until it expires; leaking it\n    // into technical_context surfaces it on the operator dashboard and to\n    // anyone who can read the report. Strip it before embedding.\n    if (user) {\n      const { userHash: _hash, exp: _exp, ...safeUser } = user\n      technical_context.user = safeUser\n    }\n    if (metadata && Object.keys(metadata).length > 0) {\n      technical_context.metadata = { ...metadata }\n    }\n    const captureMethod: ReportPayload['capture_method'] = manualScreenshot\n      ? (values.capture_method ?? 'manual')\n      : 'none'\n    const payload: ReportPayload = {\n      description: values.description,\n      feedback_type: (values.feedback_type ?? 'bug') as ReportPayload['feedback_type'],\n      severity: (values.severity ?? 'medium') as ReportPayload['severity'],\n      env,\n      page_url: window.location.href,\n      user_agent: navigator.userAgent,\n      capture_method: captureMethod,\n      technical_context,\n    }\n    // Build-time stamp; tsup's `define` substitutes the package.json\n    // version into __MFB_VERSION__. Conditionally assigned so\n    // exactOptionalPropertyTypes stays happy under vitest (no tsup\n    // pass) where the constant is undefined.\n    if (typeof __MFB_VERSION__ !== 'undefined' && __MFB_VERSION__) {\n      payload.widget_version = __MFB_VERSION__\n    }\n    if (manualScreenshot) payload.screenshot = manualScreenshot\n    if (values.synthetic) payload.synthetic = true\n    // v0.7: lift the host-provided identity to the top level so the\n    // backend can upsert a WidgetUser FK. technical_context.user still\n    // carries it for backward-compat with the operator's tech-context\n    // viewer (and so an audit-log entry knows who was identified at\n    // submission time).\n    if (user?.id !== undefined && user.id !== null && user.id !== '') {\n      payload.user = {\n        // The host can pass `id` as a string or number; the backend\n        // stores it as an opaque string. Coerce here to a stable shape.\n        id: String(user.id),\n        ...(user.email !== undefined && { email: user.email }),\n        ...(user.name !== undefined && { name: user.name }),\n      }\n    }\n    let finalPayload: ReportPayload = payload\n    for (const t of transformers) finalPayload = await t(finalPayload)\n    try {\n      const result = await api.submitReport(finalPayload)\n      config.onSubmitSuccess?.(result)\n      capture.clear()\n      return result\n    } catch (err) {\n      const error = err instanceof Error ? err : new Error(String(err))\n      config.onError?.(error)\n      throw error\n    }\n  }\n\n  const handle = mountWidget({\n    host,\n    strings,\n    showFAB: config.showFAB ?? true,\n    onSubmit: async (values) => { await buildAndSubmit(values) },\n    api,\n    // Keep this a callback (not a snapshot) so the mount picks up identity\n    // changes that happen after createFeedback() — `notifyIdentityChanged()`\n    // is the trigger for a re-render.\n    getExternalId: () => (user?.id !== undefined && user.id !== null && user.id !== '' ? String(user.id) : undefined),\n    // Phase 4: the manifest tells the loader whether this project gates the\n    // widget per end-user; the loader forwards it as `requiresVisibilityCheck`.\n    requiresVisibilityCheck: config.requiresVisibilityCheck ?? false,\n    // Send the identified email alongside the id so an email-based allowlist\n    // can match (the backend matches external_id OR email). Read from `user`\n    // live so identity set after createFeedback() is reflected.\n    checkVisibility: (externalId) => api.checkVisibility(externalId, user?.email),\n  })\n\n  // Opt-in QA Meter second FAB. Lazily imported so the QA bytes stay out of\n  // the main bundle for consumers who don't enable it. The host serves its\n  // own artifact via qaMeter.source.\n  let qaHandle: { dispose(): void } | undefined\n  let qaDisposed = false\n  if (config.qaMeter?.source) {\n    const qa = config.qaMeter\n    const qaLocale: 'fr' | 'en' =\n      qa.locale ?? (String(locale ?? '').toLowerCase().startsWith('fr') ? 'fr' : 'en')\n    void import('./qa-meter').then(({ createQaMeter }) => {\n      if (qaDisposed) return // shutdown() ran before the import resolved\n      qaHandle = createQaMeter({\n        source: qa.source,\n        size: qa.size ?? 'sm',\n        position: qa.position ?? defaultQaPosition(),\n        locale: qaLocale,\n        ...(qa.getCurrentPage && { getCurrentPage: qa.getCurrentPage }),\n      })\n    })\n  }\n\n  const instance: FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } = {\n    show() { handle.open() },\n    hide() { handle.close() },\n    open(opts) { handle.open(); void opts },\n    async submit(partial) {\n      return buildAndSubmit({\n        description: partial.description,\n        ...(partial.feedback_type !== undefined && { feedback_type: partial.feedback_type }),\n        ...(partial.severity !== undefined && { severity: partial.severity }),\n        ...(partial.synthetic !== undefined && { synthetic: partial.synthetic }),\n        ...(partial.screenshot !== undefined && { screenshot: partial.screenshot }),\n      })\n    },\n    identify(u) {\n      user = u\n      // Tell the mount to re-evaluate FAB visibility / Mine tab gating.\n      handle.notifyIdentityChanged()\n    },\n    setMetadata(kv) { metadata = { ...metadata, ...kv } },\n    shutdown() {\n      qaDisposed = true\n      qaHandle?.dispose()\n      handle.dispose()\n      capture.dispose()\n      host.remove()\n      // Only release the global if it still points at *us* — otherwise a\n      // newer instance has taken ownership and we mustn't blow it away.\n      const w = window as unknown as WindowWithGlobal\n      if (w.mhosaicFeedback === instance) {\n        delete w.mhosaicFeedback\n      }\n    },\n    _registerTransformer(fn: ReportTransformer) { transformers.push(fn) },\n  }\n\n  // Expose the instance globally for ad-hoc callers (DevTools, docs pages,\n  // help-widget integrations). The most-recently-created instance wins.\n  ;(window as unknown as WindowWithGlobal).mhosaicFeedback = instance\n\n  return instance\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["useEffect","useState","useEffect","useState","jsx","jsxs","Fragment","jsx","jsxs","POLL_MS","useState","useEffect","h","useEffect","useMemo","useRef","useState","jsx","jsxs","formatRelative","jsx","jsxs","POLL_MS","useState","useRef","useEffect","useMemo","jsx","jsxs","useEffect","useRef","useState","useEffect","useRef","useState","jsx","jsxs","h","jsx","jsxs","TYPES","SEVERITIES","useState","useRef","useEffect","useEffect","useRef","useState","jsx","jsxs","jsx","jsxs","POLL_MS","useState","useRef","useEffect","useEffect","useRef","jsx","jsxs","Fragment","jsx","jsxs","useState","useEffect"]}