@alfadocs/ui-kit-debug 1.12.6 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/_chunks/{actions-cell-renderer-DnSLrgQD.js → actions-cell-renderer-ReurEy5i.js} +2 -2
  2. package/dist/_chunks/{actions-cell-renderer-DnSLrgQD.js.map → actions-cell-renderer-ReurEy5i.js.map} +1 -1
  3. package/dist/_chunks/{address-fields-CZP4Wncx.js → address-fields-CZ0pEdEI.js} +4 -4
  4. package/dist/_chunks/{address-fields-CZP4Wncx.js.map → address-fields-CZ0pEdEI.js.map} +1 -1
  5. package/dist/_chunks/{ai-prompt-input-DnbfEJnr.js → ai-prompt-input-CrHr-5H8.js} +2 -2
  6. package/dist/_chunks/{ai-prompt-input-DnbfEJnr.js.map → ai-prompt-input-CrHr-5H8.js.map} +1 -1
  7. package/dist/_chunks/{anamnesis-summary-rows-BfhxWPO3.js → anamnesis-summary-rows-CRWO8x8g.js} +4 -4
  8. package/dist/_chunks/{anamnesis-summary-rows-BfhxWPO3.js.map → anamnesis-summary-rows-CRWO8x8g.js.map} +1 -1
  9. package/dist/_chunks/{audio-recorder-EWBCNIsd.js → audio-recorder-DXG6LTtO.js} +2 -2
  10. package/dist/_chunks/{audio-recorder-EWBCNIsd.js.map → audio-recorder-DXG6LTtO.js.map} +1 -1
  11. package/dist/_chunks/{autocomplete-BrONKN8l.js → autocomplete-MpBhz-fG.js} +86 -74
  12. package/dist/_chunks/{autocomplete-BrONKN8l.js.map → autocomplete-MpBhz-fG.js.map} +1 -1
  13. package/dist/_chunks/{balance-badge-cell-7L6nzB29.js → balance-badge-cell--iQ_gtmi.js} +4 -4
  14. package/dist/_chunks/{balance-badge-cell-7L6nzB29.js.map → balance-badge-cell--iQ_gtmi.js.map} +1 -1
  15. package/dist/_chunks/{bishop-score-Cnu0xs-z.js → bishop-score-CJUVBfAu.js} +2 -2
  16. package/dist/_chunks/{bishop-score-Cnu0xs-z.js.map → bishop-score-CJUVBfAu.js.map} +1 -1
  17. package/dist/_chunks/{booking-CBHKdsZl.js → booking-DQbo6mQF.js} +609 -591
  18. package/dist/_chunks/booking-DQbo6mQF.js.map +1 -0
  19. package/dist/_chunks/{care-plan-card-aDN4oem9.js → care-plan-card-D-FMfDKH.js} +2 -2
  20. package/dist/_chunks/{care-plan-card-aDN4oem9.js.map → care-plan-card-D-FMfDKH.js.map} +1 -1
  21. package/dist/_chunks/{care-plan-entry-card-BEhANdBE.js → care-plan-entry-card-Bvw_9T-d.js} +2 -2
  22. package/dist/_chunks/{care-plan-entry-card-BEhANdBE.js.map → care-plan-entry-card-Bvw_9T-d.js.map} +1 -1
  23. package/dist/_chunks/{care-plan-header-CkpPyQqG.js → care-plan-header-B5CBE0zu.js} +2 -2
  24. package/dist/_chunks/{care-plan-header-CkpPyQqG.js.map → care-plan-header-B5CBE0zu.js.map} +1 -1
  25. package/dist/_chunks/{city-input-towzBenY.js → city-input-DaQc-Hbj.js} +2 -2
  26. package/dist/_chunks/{city-input-towzBenY.js.map → city-input-DaQc-Hbj.js.map} +1 -1
  27. package/dist/_chunks/{combobox-DiMLItyu.js → combobox-D7_NLE2Z.js} +89 -81
  28. package/dist/_chunks/{combobox-DiMLItyu.js.map → combobox-D7_NLE2Z.js.map} +1 -1
  29. package/dist/_chunks/{contact-profile-card-BMIBr5s9.js → contact-profile-card-BZqi8Hck.js} +38 -36
  30. package/dist/_chunks/{contact-profile-card-BMIBr5s9.js.map → contact-profile-card-BZqi8Hck.js.map} +1 -1
  31. package/dist/_chunks/{country-select-UEF9BEeQ.js → country-select-DzbPFktV.js} +2 -2
  32. package/dist/_chunks/{country-select-UEF9BEeQ.js.map → country-select-DzbPFktV.js.map} +1 -1
  33. package/dist/_chunks/{document-preview-oyx9RhCo.js → document-preview-CDb-n_W7.js} +2 -2
  34. package/dist/_chunks/{document-preview-oyx9RhCo.js.map → document-preview-CDb-n_W7.js.map} +1 -1
  35. package/dist/_chunks/{face-scheme-CZaKm6fG.js → face-scheme-DA2vjqaM.js} +2 -2
  36. package/dist/_chunks/{face-scheme-CZaKm6fG.js.map → face-scheme-DA2vjqaM.js.map} +1 -1
  37. package/dist/_chunks/{file-manager-C0y7HBAN.js → file-manager-UsD8e_AB.js} +2 -2
  38. package/dist/_chunks/{file-manager-C0y7HBAN.js.map → file-manager-UsD8e_AB.js.map} +1 -1
  39. package/dist/_chunks/{gestational-age-calculator-CxZelpu2.js → gestational-age-calculator-CiOQjzgx.js} +2 -2
  40. package/dist/_chunks/{gestational-age-calculator-CxZelpu2.js.map → gestational-age-calculator-CiOQjzgx.js.map} +1 -1
  41. package/dist/_chunks/key-value-pair-BIBdZBLg.js +188 -0
  42. package/dist/_chunks/key-value-pair-BIBdZBLg.js.map +1 -0
  43. package/dist/_chunks/{link-cell-renderer-CenJx-_7.js → link-cell-renderer-CIASXsVM.js} +4 -4
  44. package/dist/_chunks/{link-cell-renderer-CenJx-_7.js.map → link-cell-renderer-CIASXsVM.js.map} +1 -1
  45. package/dist/_chunks/{multi-select-DqL4o0qV.js → multi-select-DYOvx2SJ.js} +56 -48
  46. package/dist/_chunks/{multi-select-DqL4o0qV.js.map → multi-select-DYOvx2SJ.js.map} +1 -1
  47. package/dist/_chunks/{muscle-scheme-yfzP_xoJ.js → muscle-scheme-BSJsI4-I.js} +2 -2
  48. package/dist/_chunks/{muscle-scheme-yfzP_xoJ.js.map → muscle-scheme-BSJsI4-I.js.map} +1 -1
  49. package/dist/_chunks/{optical-prescription-Dj3Eh5Ok.js → optical-prescription-BvwuqTpl.js} +2 -2
  50. package/dist/_chunks/{optical-prescription-Dj3Eh5Ok.js.map → optical-prescription-BvwuqTpl.js.map} +1 -1
  51. package/dist/_chunks/{patient-details-xrqe13az.js → patient-details-BQaUzPqp.js} +5 -5
  52. package/dist/_chunks/{patient-details-xrqe13az.js.map → patient-details-BQaUzPqp.js.map} +1 -1
  53. package/dist/_chunks/{patient-search-D5ufYoRc.js → patient-search-HeEUB-_X.js} +2 -2
  54. package/dist/_chunks/{patient-search-D5ufYoRc.js.map → patient-search-HeEUB-_X.js.map} +1 -1
  55. package/dist/_chunks/{patient-summary-card-CBfuDvJ5.js → patient-summary-card-CvDxhbBR.js} +2 -2
  56. package/dist/_chunks/{patient-summary-card-CBfuDvJ5.js.map → patient-summary-card-CvDxhbBR.js.map} +1 -1
  57. package/dist/_chunks/{payment-request-card-YVCcCZ25.js → payment-request-card-lmKLwsDs.js} +2 -2
  58. package/dist/_chunks/{payment-request-card-YVCcCZ25.js.map → payment-request-card-lmKLwsDs.js.map} +1 -1
  59. package/dist/_chunks/{periodontal-chart-BatZEFar.js → periodontal-chart-B9fphroR.js} +2 -2
  60. package/dist/_chunks/{periodontal-chart-BatZEFar.js.map → periodontal-chart-B9fphroR.js.map} +1 -1
  61. package/dist/_chunks/{periodontal-chart-card-BBpY_rBc.js → periodontal-chart-card-qEZPXj83.js} +2 -2
  62. package/dist/_chunks/{periodontal-chart-card-BBpY_rBc.js.map → periodontal-chart-card-qEZPXj83.js.map} +1 -1
  63. package/dist/_chunks/{pregnancy-dating-DtToyMyc.js → pregnancy-dating-D2wchD11.js} +2 -2
  64. package/dist/_chunks/{pregnancy-dating-DtToyMyc.js.map → pregnancy-dating-D2wchD11.js.map} +1 -1
  65. package/dist/_chunks/{province-select-BDywtmL-.js → province-select-BBc7s7vR.js} +2 -2
  66. package/dist/_chunks/{province-select-BDywtmL-.js.map → province-select-BBc7s7vR.js.map} +1 -1
  67. package/dist/_chunks/{select-BW5ecXlX.js → select-Bi8zril7.js} +165 -136
  68. package/dist/_chunks/select-Bi8zril7.js.map +1 -0
  69. package/dist/_chunks/{signature-request-DVDTKLjM.js → signature-request-CUe903hj.js} +2 -2
  70. package/dist/_chunks/{signature-request-DVDTKLjM.js.map → signature-request-CUe903hj.js.map} +1 -1
  71. package/dist/_chunks/{tabs-CBb2AidH.js → tabs-DqKtTIjZ.js} +2 -2
  72. package/dist/_chunks/{tabs-CBb2AidH.js.map → tabs-DqKtTIjZ.js.map} +1 -1
  73. package/dist/_chunks/{tasks-panel-Do5SFp-j.js → tasks-panel-DPCNaRko.js} +2 -2
  74. package/dist/_chunks/{tasks-panel-Do5SFp-j.js.map → tasks-panel-DPCNaRko.js.map} +1 -1
  75. package/dist/_chunks/{tooth-scheme-WdyVymQ1.js → tooth-scheme-2IsmbE6w.js} +2 -2
  76. package/dist/_chunks/{tooth-scheme-WdyVymQ1.js.map → tooth-scheme-2IsmbE6w.js.map} +1 -1
  77. package/dist/_chunks/{unit-converter-BPumXMA6.js → unit-converter-C6jMjqL9.js} +2 -2
  78. package/dist/_chunks/{unit-converter-BPumXMA6.js.map → unit-converter-C6jMjqL9.js.map} +1 -1
  79. package/dist/_chunks/{workflow-map-DE_ribRM.js → workflow-map-wcw3t6g4.js} +2 -2
  80. package/dist/_chunks/{workflow-map-DE_ribRM.js.map → workflow-map-wcw3t6g4.js.map} +1 -1
  81. package/dist/agent-catalog.json +1 -1
  82. package/dist/components/_shared/option.d.ts +1 -0
  83. package/dist/components/_shared/option.d.ts.map +1 -1
  84. package/dist/components/address-fields/index.js +1 -1
  85. package/dist/components/ai-prompt-input/index.js +1 -1
  86. package/dist/components/audio-recorder/index.js +1 -1
  87. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  88. package/dist/components/autocomplete/index.js +1 -1
  89. package/dist/components/bishop-score/index.js +1 -1
  90. package/dist/components/booking/booking.d.ts.map +1 -1
  91. package/dist/components/booking/index.js +1 -1
  92. package/dist/components/care-plan-card/index.js +1 -1
  93. package/dist/components/care-plan-entry-card/index.js +1 -1
  94. package/dist/components/care-plan-header/index.js +1 -1
  95. package/dist/components/city-input/index.js +1 -1
  96. package/dist/components/combobox/combobox.d.ts.map +1 -1
  97. package/dist/components/combobox/index.js +1 -1
  98. package/dist/components/contact-profile-card/contact-profile-card.d.ts.map +1 -1
  99. package/dist/components/contact-profile-card/index.js +1 -1
  100. package/dist/components/country-select/index.js +1 -1
  101. package/dist/components/data-table/index.js +2 -2
  102. package/dist/components/document-preview/index.js +1 -1
  103. package/dist/components/face-scheme/index.js +1 -1
  104. package/dist/components/file-manager/index.js +1 -1
  105. package/dist/components/gestational-age-calculator/index.js +1 -1
  106. package/dist/components/key-value-pair/index.js +1 -1
  107. package/dist/components/key-value-pair/key-value-pair.d.ts +16 -0
  108. package/dist/components/key-value-pair/key-value-pair.d.ts.map +1 -1
  109. package/dist/components/multi-select/index.js +1 -1
  110. package/dist/components/multi-select/multi-select.d.ts.map +1 -1
  111. package/dist/components/muscle-scheme/index.js +1 -1
  112. package/dist/components/optical-prescription/index.js +1 -1
  113. package/dist/components/patient-details/index.js +1 -1
  114. package/dist/components/patient-search/index.js +1 -1
  115. package/dist/components/patient-summary-card/index.js +1 -1
  116. package/dist/components/patient-table/index.js +1 -1
  117. package/dist/components/payment-card/index.js +1 -1
  118. package/dist/components/periodontal-chart/index.js +1 -1
  119. package/dist/components/periodontal-chart-card/index.js +1 -1
  120. package/dist/components/pregnancy-dating/index.js +1 -1
  121. package/dist/components/province-select/index.js +1 -1
  122. package/dist/components/select/index.js +1 -1
  123. package/dist/components/select/select.d.ts +24 -0
  124. package/dist/components/select/select.d.ts.map +1 -1
  125. package/dist/components/skeleton-scheme/index.js +1 -1
  126. package/dist/components/tabs/index.js +1 -1
  127. package/dist/components/tasks-panel/index.js +1 -1
  128. package/dist/components/tooth-scheme/index.js +1 -1
  129. package/dist/components/unit-converter/index.js +1 -1
  130. package/dist/components/workflow/index.js +1 -1
  131. package/dist/index.js +40 -40
  132. package/dist/patterns/anamnesis/index.js +1 -1
  133. package/dist/patterns/signature-request/index.js +1 -1
  134. package/package.json +1 -1
  135. package/dist/_chunks/booking-CBHKdsZl.js.map +0 -1
  136. package/dist/_chunks/key-value-pair-BubG4fUa.js +0 -144
  137. package/dist/_chunks/key-value-pair-BubG4fUa.js.map +0 -1
  138. package/dist/_chunks/select-BW5ecXlX.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"periodontal-chart-BatZEFar.js","sources":["../../src/components/periodontal-chart/periodontal-chart.agent.ts","../../src/components/periodontal-chart/periodontal-chart-data.ts","../../src/components/periodontal-chart/periodontal-chart.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { PeriodontalChartHandle } from './periodontal-chart';\nimport type {\n PeriodontalMobility,\n PeriodontalSiteId,\n PeriodontalToothState,\n} from './periodontal-chart-data';\n\n/* -------------------------------------------------------------------- */\n/* PeriodontalChart — agent adapter */\n/* */\n/* The editable perio chart. Agents can introspect the derived indices, */\n/* the missing teeth and which teeth carry readings, and can drive */\n/* per-site edits. It NEVER exposes the free-text per-tooth `note` */\n/* (PHI) — neither in the always-on state snapshot nor via get_tooth, */\n/* which returns structural readings with the note stripped. */\n/* -------------------------------------------------------------------- */\n\nconst FDI_SCHEMA = {\n type: 'string',\n description: \"FDI tooth id ('11'–'48' permanent, '51'–'85' primary).\",\n} as const;\nconst SITE_SCHEMA = {\n enum: ['db', 'b', 'mb', 'dl', 'l', 'ml'],\n description:\n 'Probing site: (m/d)b = mesio-/disto-/mid buccal, (m/d)l = mesio-/disto-/mid lingual.',\n} as const;\nconst MM_SCHEMA = {\n type: 'integer',\n description: 'Whole millimetres — perio probing is recorded in integer mm.',\n} as const;\nconst INDICES_SCHEMA = {\n type: 'object',\n properties: {\n meanPocketDepthMm: { type: ['number', 'null'] },\n meanAttachmentLevelMm: { type: ['number', 'null'] },\n plaquePercent: { type: ['number', 'null'] },\n bleedingPercent: { type: ['number', 'null'] },\n scoredSiteCount: { type: 'number' },\n deepPocketCount: { type: 'number' },\n },\n} as const;\nconst FINDINGS_SCHEMA = {\n type: 'array',\n description:\n 'One entry per flagged tooth, in clinical order; each tooth’s pockets are deepest-first.',\n items: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n status: {\n enum: ['present', 'missing', 'implant'],\n description: 'Structural status of the tooth.',\n },\n severity: {\n enum: ['normal', 'moderate', 'severe'],\n description: 'Worst pocket severity band across the tooth.',\n },\n maxPocketDepthMm: { type: ['integer', 'null'] },\n pockets: {\n type: 'array',\n description: 'Sites at/above the moderate threshold, deepest first.',\n items: {\n type: 'object',\n properties: {\n site: SITE_SCHEMA,\n pocketDepthMm: MM_SCHEMA,\n severity: { enum: ['moderate', 'severe'] },\n },\n },\n },\n bleedingSites: { type: 'array', items: SITE_SCHEMA },\n plaqueSites: { type: 'array', items: SITE_SCHEMA },\n suppurationSites: { type: 'array', items: SITE_SCHEMA },\n maxRecessionMm: { type: ['number', 'null'] },\n mobility: {\n type: ['integer', 'null'],\n description: 'Miller mobility grade 1–3, or null when none.',\n },\n furcation: { type: 'array', items: { enum: ['buccal', 'lingual'] } },\n },\n },\n} as const;\n\n/** Structural (PHI-free) view of one tooth returned by get_tooth. */\nfunction sanitizeTooth(\n tooth: PeriodontalToothState | undefined,\n): Record<string, unknown> | null {\n if (!tooth) return null;\n const rest: Record<string, unknown> = { ...tooth };\n delete rest.note;\n return rest;\n}\n\nexport const periodontalChartAgent: AgentAdapter<PeriodontalChartHandle> = {\n id: 'periodontal-chart',\n // pick — focus a measurement input (transient); set_value / edit_inline —\n // record per-site probing readings and per-tooth flags.\n capabilities: ['pick', 'set_value', 'edit_inline'],\n state: {\n indices: {\n type: 'object',\n descriptionKey: 'ui.agent.periodontalChart.state.indices',\n description:\n 'Derived whole-mouth indices: mean probing depth, mean attachment level, plaque %, bleeding %, scored-site and deep-pocket counts. Aggregates only — no per-site or patient data.',\n read: (handle) => handle.getIndices(),\n },\n findings: {\n type: 'object[]',\n descriptionKey: 'ui.agent.periodontalChart.state.findings',\n description:\n 'Narratable per-tooth problem list — one entry per flagged tooth (missing, implant, a pocket at/above the moderate threshold, bleeding, suppuration, mobility ≥ 1, or furcation), in clinical order with each tooth’s pockets deepest-first. Each entry carries fdi, status, severity, maxPocketDepthMm, pockets, bleeding/plaque/suppuration sites, maxRecessionMm, mobility and furcation. Read this to summarise the chart aloud. Structural readings + FDI ids only — never the free-text note or patient identity.',\n read: (handle) => handle.getFindings(),\n },\n missingTeeth: {\n type: 'string[]',\n descriptionKey: 'ui.agent.periodontalChart.state.missingTeeth',\n description: 'FDI ids of every tooth flagged as missing.',\n read: (handle) => handle.missingTeeth(),\n },\n chartedTeeth: {\n type: 'string[]',\n descriptionKey: 'ui.agent.periodontalChart.state.chartedTeeth',\n description:\n 'FDI ids of teeth carrying at least one probing reading or flag. Identifiers only — never patient identity or the free-text note.',\n read: (handle) =>\n Object.entries(handle.getValue())\n .filter(([, s]) => {\n if (s.missing || s.implant || s.mobility != null) return true;\n if (s.furcation && Object.keys(s.furcation).length > 0) return true;\n const sites = s.sites ? Object.values(s.sites) : [];\n return sites.some(\n (site) =>\n site != null &&\n (site.pocketDepthMm != null ||\n site.recessionMm != null ||\n site.bleeding ||\n site.plaque ||\n site.suppuration),\n );\n })\n .map(([fdi]) => fdi),\n },\n },\n actions: {\n focus_site: {\n safety: 'read',\n argsType:\n '{ fdi: string; site: PeriodontalSiteId; field: \"pocketDepthMm\" | \"recessionMm\" }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n site: SITE_SCHEMA,\n field: { enum: ['pocketDepthMm', 'recessionMm'] },\n },\n required: ['fdi', 'site', 'field'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.focusSite',\n description: 'Move focus to a specific probing-measurement input.',\n invoke: (\n handle,\n args: {\n fdi: string;\n site: PeriodontalSiteId;\n field: 'pocketDepthMm' | 'recessionMm';\n },\n ) => {\n handle.focusSite(args.fdi, args.site, args.field);\n },\n },\n get_tooth: {\n safety: 'read',\n argsType: '{ fdi: string }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA },\n required: ['fdi'],\n },\n returns: 'object | null',\n returnsSchema: { type: ['object', 'null'] },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.getTooth',\n description:\n 'Read one tooth’s structural readings (sites, flags, mobility, furcation). The free-text note is never returned.',\n invoke: (handle, args: { fdi: string }) =>\n sanitizeTooth(handle.getTooth(args.fdi)),\n },\n get_indices: {\n safety: 'read',\n argsType: '{}',\n argsSchema: { type: 'object', properties: {} },\n returns: 'PeriodontalIndices',\n returnsSchema: INDICES_SCHEMA,\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.getIndices',\n description: 'Read the derived whole-mouth summary indices.',\n invoke: (handle) => handle.getIndices(),\n },\n get_findings: {\n safety: 'read',\n argsType: '{}',\n argsSchema: { type: 'object', properties: {} },\n returns: 'PeriodontalToothFinding[]',\n returnsSchema: FINDINGS_SCHEMA,\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.getFindings',\n description:\n 'Read the narratable per-tooth problem list (flagged teeth only). Use it to summarise the chart aloud — e.g. “46: 8 mm disto-buccal pocket, bleeding, grade 2 mobility”. Structural readings and FDI ids only; the free-text note is never returned.',\n invoke: (handle) => handle.getFindings(),\n },\n set_depth: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; mm: number }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA, site: SITE_SCHEMA, mm: MM_SCHEMA },\n required: ['fdi', 'site', 'mm'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setDepth',\n description: 'Record the probing pocket depth (mm) at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; mm: number },\n ) => {\n handle.setSite(args.fdi, args.site, {\n pocketDepthMm: Math.max(0, args.mm),\n });\n },\n },\n set_recession: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; mm: number }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA, site: SITE_SCHEMA, mm: MM_SCHEMA },\n required: ['fdi', 'site', 'mm'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setRecession',\n description: 'Record the gingival recession (mm) at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; mm: number },\n ) => {\n handle.setSite(args.fdi, args.site, { recessionMm: args.mm });\n },\n },\n toggle_bleeding: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; value: boolean }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n site: SITE_SCHEMA,\n value: { type: 'boolean' },\n },\n required: ['fdi', 'site', 'value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.toggleBleeding',\n description: 'Set bleeding on probing at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; value: boolean },\n ) => {\n handle.setSite(args.fdi, args.site, { bleeding: args.value });\n },\n },\n toggle_plaque: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; value: boolean }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n site: SITE_SCHEMA,\n value: { type: 'boolean' },\n },\n required: ['fdi', 'site', 'value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.togglePlaque',\n description: 'Set plaque presence at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; value: boolean },\n ) => {\n handle.setSite(args.fdi, args.site, { plaque: args.value });\n },\n },\n set_mobility: {\n safety: 'write',\n argsType: '{ fdi: string; grade: 0 | 1 | 2 | 3 }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n grade: { enum: [0, 1, 2, 3] },\n },\n required: ['fdi', 'grade'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setMobility',\n description: 'Set a tooth’s Miller mobility grade (0–3).',\n invoke: (handle, args: { fdi: string; grade: PeriodontalMobility }) => {\n handle.setTooth(args.fdi, { mobility: args.grade });\n },\n },\n set_missing: {\n safety: 'write',\n argsType: '{ fdi: string; value: boolean }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA, value: { type: 'boolean' } },\n required: ['fdi', 'value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setMissing',\n description: 'Flag a tooth present or absent.',\n invoke: (handle, args: { fdi: string; value: boolean }) => {\n handle.setTooth(args.fdi, { missing: args.value });\n },\n },\n clear_tooth: {\n safety: 'destructive',\n argsType: '{ fdi: string }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA },\n required: ['fdi'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.clearTooth',\n description: 'Clear every reading from a tooth.',\n invoke: (handle, args: { fdi: string }) => {\n handle.clearTooth(args.fdi);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'periodontal-chart',\n description: 'Marks the PeriodontalChart root group.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-fdi',\n description:\n 'Each tooth column header carries its FDI id as `data-fdi`. Every measurement input also carries `data-fdi` + `data-site` + `data-field`. The root `data-mode` exposes interactive vs display.',\n },\n },\n};\n","import {\n PERMANENT_TEETH,\n PRIMARY_TEETH,\n FDI_TO_UNIVERSAL,\n FDI_TO_PALMER,\n type FdiId,\n} from '../tooth-scheme/tooth-data';\nimport {\n PERIODONTAL_SITE_ORDER,\n type PeriodontalSiteId,\n type PeriodontalMobility,\n type PeriodontalSite,\n} from '../periodontal-chart-card/periodontal-chart-card';\n\n/* -------------------------------------------------------------------- */\n/* PeriodontalChart — data model */\n/* */\n/* The editable counterpart to the display-only PeriodontalChartCard. */\n/* Where the card takes a flat `PeriodontalSite[]` for read-only render, */\n/* the chart owns a keyed, mutable value — `Record<FdiId, tooth state>` */\n/* — so per-site probing data can be entered and emitted losslessly (the */\n/* chart always keys by FDI, matching ToothScheme's interchange rule). */\n/* */\n/* Site ids, mobility grades and the flat-site interop type are REUSED */\n/* from the card so the two components share one vocabulary. */\n/* -------------------------------------------------------------------- */\n\nexport type { FdiId, PeriodontalSiteId, PeriodontalMobility, PeriodontalSite };\nexport { PERIODONTAL_SITE_ORDER };\n\n/** The two probing aspects of a tooth. */\nexport type PeriodontalSide = 'buccal' | 'lingual';\n\n/** Buccal (facial) probing sites, in display order. */\nexport const PERIODONTAL_BUCCAL_SITES = ['db', 'b', 'mb'] as const;\n/** Lingual (palatal) probing sites, in display order. */\nexport const PERIODONTAL_LINGUAL_SITES = ['dl', 'l', 'ml'] as const;\n\n/** The three site ids for a given aspect. */\nexport function sitesForSide(\n side: PeriodontalSide,\n): readonly PeriodontalSiteId[] {\n return side === 'buccal'\n ? PERIODONTAL_BUCCAL_SITES\n : PERIODONTAL_LINGUAL_SITES;\n}\n\n/* -------------------------------------------------------------------- */\n/* Public value types */\n/* -------------------------------------------------------------------- */\n\n/** One probing-site reading. Every field is optional — a blank site. */\nexport interface PeriodontalSiteState {\n /** Probing pocket depth (PPD) in millimetres. */\n pocketDepthMm?: number;\n /**\n * Gingival margin / recession in millimetres. May be negative when the\n * margin sits coronal to the CEJ (i.e. gingival overgrowth).\n */\n recessionMm?: number;\n /** Bleeding on probing (BoP) at this site. */\n bleeding?: boolean;\n /** Visible plaque at this site. */\n plaque?: boolean;\n /** Suppuration (pus) at this site. */\n suppuration?: boolean;\n}\n\n/** Full editable state for one tooth. */\nexport interface PeriodontalToothState {\n /** Tooth is absent — hides its probing inputs. */\n missing?: boolean;\n /** Tooth is an implant. */\n implant?: boolean;\n /** Miller mobility grade (0–3). */\n mobility?: PeriodontalMobility;\n /** Free-text clinical note. Treated as PHI — never exposed to agents. */\n note?: string;\n /** Per-site probing readings, keyed by site id. */\n sites?: Partial<Record<PeriodontalSiteId, PeriodontalSiteState>>;\n /**\n * Furcation involvement per aspect. The array length matches the number\n * of anatomical furcation entry points for the tooth on that aspect (see\n * {@link PERIODONTAL_FURCATION_MAP}); each boolean flags one entry.\n */\n furcation?: Partial<Record<PeriodontalSide, boolean[]>>;\n}\n\n/** The whole-mouth editable value, keyed by FDI tooth number. */\nexport type PeriodontalChartValue = Record<string, PeriodontalToothState>;\n\n/* -------------------------------------------------------------------- */\n/* Clinical thresholds */\n/* -------------------------------------------------------------------- */\n\n/** Probing depth (mm) at/above which a site is flagged moderate. */\nexport const MODERATE_POCKET_MM = 4;\n/** Probing depth (mm) at/above which a site is flagged severe. */\nexport const SEVERE_POCKET_MM = 6;\n/** Plaque score (% of sites) above which the index reads elevated. */\nexport const PLAQUE_WARN_PERCENT = 20;\n/** Bleeding score (% of sites) above which the index reads elevated. */\nexport const BLEEDING_WARN_PERCENT = 5;\n\n/** Classify a probing depth into a severity band. */\nexport function pocketSeverity(\n depth: number | undefined,\n moderate = MODERATE_POCKET_MM,\n severe = SEVERE_POCKET_MM,\n): 'normal' | 'moderate' | 'severe' {\n if (depth == null || !Number.isFinite(depth)) return 'normal';\n if (depth >= severe) return 'severe';\n if (depth >= moderate) return 'moderate';\n return 'normal';\n}\n\n/* -------------------------------------------------------------------- */\n/* Furcation anatomy */\n/* */\n/* Number of furcation entry points per aspect. Transcribed verbatim from */\n/* the platform PeriodontalState::getFurcationMap() so the kit chart and */\n/* the legacy chart agree tooth-for-tooth. Teeth absent from the map are */\n/* single-rooted (no furcation) — {@link furcationSlots} returns 0. */\n/* -------------------------------------------------------------------- */\n\nexport const PERIODONTAL_FURCATION_MAP: Record<\n string,\n { buccal: number; lingual: number }\n> = {\n '18': { buccal: 1, lingual: 2 },\n '17': { buccal: 1, lingual: 2 },\n '16': { buccal: 1, lingual: 2 },\n '15': { buccal: 2, lingual: 2 },\n '14': { buccal: 0, lingual: 2 },\n '24': { buccal: 0, lingual: 2 },\n '25': { buccal: 2, lingual: 2 },\n '26': { buccal: 1, lingual: 2 },\n '27': { buccal: 1, lingual: 2 },\n '28': { buccal: 1, lingual: 2 },\n '48': { buccal: 1, lingual: 1 },\n '47': { buccal: 1, lingual: 1 },\n '46': { buccal: 1, lingual: 1 },\n '36': { buccal: 1, lingual: 1 },\n '37': { buccal: 1, lingual: 1 },\n '38': { buccal: 1, lingual: 1 },\n};\n\n/** Number of furcation entry points for a tooth on a given aspect (0–2). */\nexport function furcationSlots(fdi: string, side: PeriodontalSide): number {\n return PERIODONTAL_FURCATION_MAP[fdi]?.[side] ?? 0;\n}\n\n/* -------------------------------------------------------------------- */\n/* Tooth layout */\n/* -------------------------------------------------------------------- */\n\nexport type PeriodontalDentition = 'permanent' | 'primary';\n\n/** Upper then lower arch, each in clinical left-to-right order. */\nexport interface PeriodontalArches {\n upper: FdiId[];\n lower: FdiId[];\n}\n\n/** The 16 permanent teeth of the upper arch (18→11, 21→28). */\nexport const PERIODONTAL_PERMANENT_UPPER = PERMANENT_TEETH.slice(0, 16);\n/** The 16 permanent teeth of the lower arch (48→41, 31→38). */\nexport const PERIODONTAL_PERMANENT_LOWER = PERMANENT_TEETH.slice(16);\n/** The 10 primary teeth of the upper arch. */\nexport const PERIODONTAL_PRIMARY_UPPER = PRIMARY_TEETH.slice(0, 10);\n/** The 10 primary teeth of the lower arch. */\nexport const PERIODONTAL_PRIMARY_LOWER = PRIMARY_TEETH.slice(10);\n\n/** The upper + lower tooth ids for a dentition, in display order. */\nexport function periodontalArches(\n dentition: PeriodontalDentition,\n): PeriodontalArches {\n return dentition === 'primary'\n ? { upper: PERIODONTAL_PRIMARY_UPPER, lower: PERIODONTAL_PRIMARY_LOWER }\n : {\n upper: PERIODONTAL_PERMANENT_UPPER,\n lower: PERIODONTAL_PERMANENT_LOWER,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Numbering */\n/* -------------------------------------------------------------------- */\n\nexport type PeriodontalNumbering = 'fdi' | 'universal' | 'palmer';\n\n/**\n * The visible column label for a tooth. FDI is canonical; Universal (ADA)\n * and Palmer are label-only projections — the value model always keys by\n * FDI regardless of the chosen numbering.\n */\nexport function periodontalToothLabel(\n fdi: string,\n numbering: PeriodontalNumbering,\n): string {\n if (numbering === 'universal') return FDI_TO_UNIVERSAL[fdi] ?? fdi;\n if (numbering === 'palmer') return FDI_TO_PALMER[fdi] ?? fdi;\n return fdi;\n}\n\n/* -------------------------------------------------------------------- */\n/* Indices */\n/* -------------------------------------------------------------------- */\n\n/** Whole-mouth summary indices derived from a chart value. */\nexport interface PeriodontalIndices {\n /** Mean probing depth across scored sites, or null when none scored. */\n meanPocketDepthMm: number | null;\n /** Mean clinical attachment level (PPD + recession), or null. */\n meanAttachmentLevelMm: number | null;\n /** Plaque score as a percentage of scored sites (0–100), or null. */\n plaquePercent: number | null;\n /** Bleeding-on-probing as a percentage of scored sites (0–100), or null. */\n bleedingPercent: number | null;\n /** Number of sites carrying a probing depth (the scored denominator). */\n scoredSiteCount: number;\n /** Number of scored sites at/above the severe threshold. */\n deepPocketCount: number;\n}\n\n/** Round to one decimal place, guarding against `-0`. */\nfunction round1(n: number): number {\n const r = Math.round(n * 10) / 10;\n return r === 0 ? 0 : r;\n}\n\n/**\n * Derive the whole-mouth indices. A site is \"scored\" when it carries a\n * probing depth; percentages are taken over scored sites. Missing teeth\n * are skipped entirely.\n */\nexport function computePeriodontalIndices(\n value: PeriodontalChartValue,\n): PeriodontalIndices {\n let depthSum = 0;\n let attachmentSum = 0;\n let scored = 0;\n let bleeding = 0;\n let plaque = 0;\n let deep = 0;\n\n for (const tooth of Object.values(value)) {\n if (tooth.missing || !tooth.sites) continue;\n for (const site of Object.values(tooth.sites)) {\n if (!site) continue;\n const depth = site.pocketDepthMm;\n if (depth == null || !Number.isFinite(depth)) continue;\n scored += 1;\n depthSum += depth;\n attachmentSum += depth + (site.recessionMm ?? 0);\n if (depth >= SEVERE_POCKET_MM) deep += 1;\n if (site.bleeding) bleeding += 1;\n if (site.plaque) plaque += 1;\n }\n }\n\n if (scored === 0) {\n return {\n meanPocketDepthMm: null,\n meanAttachmentLevelMm: null,\n plaquePercent: null,\n bleedingPercent: null,\n scoredSiteCount: 0,\n deepPocketCount: 0,\n };\n }\n\n return {\n meanPocketDepthMm: round1(depthSum / scored),\n meanAttachmentLevelMm: round1(attachmentSum / scored),\n plaquePercent: round1((plaque / scored) * 100),\n bleedingPercent: round1((bleeding / scored) * 100),\n scoredSiteCount: scored,\n deepPocketCount: deep,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Findings */\n/* */\n/* A per-tooth \"problem list\" derived from a chart value — the shape an */\n/* agent narrates back to a clinician (\"46: 8 mm disto-buccal pocket, */\n/* bleeding, grade 2 mobility\"). PHI-free by construction: the free-text */\n/* note is never read, only structural readings + FDI ids appear. */\n/* -------------------------------------------------------------------- */\n\n/** Structural status worth narrating for a tooth. */\nexport type PeriodontalToothStatus = 'present' | 'missing' | 'implant';\n\n/** One flagged probing site within a tooth finding. */\nexport interface PeriodontalSiteFinding {\n site: PeriodontalSiteId;\n pocketDepthMm: number;\n severity: 'moderate' | 'severe';\n}\n\n/** A single tooth's notable findings — one narratable problem-list entry. */\nexport interface PeriodontalToothFinding {\n /** FDI tooth id. */\n fdi: string;\n /** Structural status. `'present'` teeth appear only when they carry a finding. */\n status: PeriodontalToothStatus;\n /** Worst severity band across the tooth's scored sites. */\n severity: 'normal' | 'moderate' | 'severe';\n /** Deepest probing depth on the tooth (mm), or null when unscored. */\n maxPocketDepthMm: number | null;\n /** Sites at/above the moderate threshold, deepest first. */\n pockets: PeriodontalSiteFinding[];\n /** Sites bleeding on probing. */\n bleedingSites: PeriodontalSiteId[];\n /** Sites with visible plaque. */\n plaqueSites: PeriodontalSiteId[];\n /** Sites with suppuration. */\n suppurationSites: PeriodontalSiteId[];\n /** Greatest *positive* recession on the tooth (mm); ≤ 0 values ignored. Null when none. */\n maxRecessionMm: number | null;\n /** Miller mobility grade when ≥ 1, else null. */\n mobility: PeriodontalMobility | null;\n /** Aspects with any furcation involvement flagged. */\n furcation: PeriodontalSide[];\n}\n\n/** Canonical clinical order (permanent then primary) for narrating findings. */\nconst FINDING_ORDER: readonly string[] = [...PERMANENT_TEETH, ...PRIMARY_TEETH];\n\n/**\n * Derive the narratable per-tooth problem list from a chart value. A tooth is\n * included only when it carries a notable finding — missing, implant, a pocket\n * at/above the moderate threshold, bleeding, suppuration, mobility ≥ 1, or\n * furcation involvement. Entries are returned in clinical (arch) order; each\n * tooth's `pockets` are ordered deepest first. Plaque and shallow recession are\n * reported as detail on already-flagged teeth but never flag a tooth alone.\n */\nexport function computePeriodontalFindings(\n value: PeriodontalChartValue,\n options?: { moderate?: number; severe?: number },\n): PeriodontalToothFinding[] {\n const moderate = options?.moderate ?? MODERATE_POCKET_MM;\n const severe = options?.severe ?? SEVERE_POCKET_MM;\n\n const rank = new Map<string, number>();\n FINDING_ORDER.forEach((fdi, i) => rank.set(fdi, i));\n const orderedFdis = Object.keys(value).sort(\n (a, b) =>\n (rank.get(a) ?? Number.MAX_SAFE_INTEGER) -\n (rank.get(b) ?? Number.MAX_SAFE_INTEGER),\n );\n\n const findings: PeriodontalToothFinding[] = [];\n\n for (const fdi of orderedFdis) {\n const tooth = value[fdi];\n if (!tooth) continue;\n\n // Missing teeth are a structural finding on their own.\n if (tooth.missing) {\n findings.push({\n fdi,\n status: 'missing',\n severity: 'normal',\n maxPocketDepthMm: null,\n pockets: [],\n bleedingSites: [],\n plaqueSites: [],\n suppurationSites: [],\n maxRecessionMm: null,\n mobility: null,\n furcation: [],\n });\n continue;\n }\n\n const status: PeriodontalToothStatus = tooth.implant\n ? 'implant'\n : 'present';\n const pockets: PeriodontalSiteFinding[] = [];\n const bleedingSites: PeriodontalSiteId[] = [];\n const plaqueSites: PeriodontalSiteId[] = [];\n const suppurationSites: PeriodontalSiteId[] = [];\n let maxPocketDepthMm: number | null = null;\n let maxRecessionMm: number | null = null;\n\n for (const site of PERIODONTAL_SITE_ORDER) {\n const reading = tooth.sites?.[site];\n if (!reading) continue;\n const { pocketDepthMm, recessionMm, bleeding, plaque, suppuration } =\n reading;\n if (pocketDepthMm != null && Number.isFinite(pocketDepthMm)) {\n maxPocketDepthMm =\n maxPocketDepthMm == null\n ? pocketDepthMm\n : Math.max(maxPocketDepthMm, pocketDepthMm);\n const sev = pocketSeverity(pocketDepthMm, moderate, severe);\n if (sev !== 'normal')\n pockets.push({ site, pocketDepthMm, severity: sev });\n }\n if (\n recessionMm != null &&\n Number.isFinite(recessionMm) &&\n recessionMm > 0\n ) {\n maxRecessionMm =\n maxRecessionMm == null\n ? recessionMm\n : Math.max(maxRecessionMm, recessionMm);\n }\n if (bleeding) bleedingSites.push(site);\n if (plaque) plaqueSites.push(site);\n if (suppuration) suppurationSites.push(site);\n }\n\n pockets.sort((a, b) => b.pocketDepthMm - a.pocketDepthMm);\n\n const mobility =\n tooth.mobility != null && tooth.mobility >= 1 ? tooth.mobility : null;\n\n const furcation: PeriodontalSide[] = [];\n if (tooth.furcation) {\n for (const side of ['buccal', 'lingual'] as const) {\n if (tooth.furcation[side]?.some(Boolean)) furcation.push(side);\n }\n }\n\n const severity: 'normal' | 'moderate' | 'severe' = pockets.some(\n (p) => p.severity === 'severe',\n )\n ? 'severe'\n : pockets.length > 0\n ? 'moderate'\n : 'normal';\n\n const notable =\n status === 'implant' ||\n pockets.length > 0 ||\n bleedingSites.length > 0 ||\n suppurationSites.length > 0 ||\n mobility != null ||\n furcation.length > 0;\n\n if (!notable) continue;\n\n findings.push({\n fdi,\n status,\n severity,\n maxPocketDepthMm,\n pockets,\n bleedingSites,\n plaqueSites,\n suppurationSites,\n maxRecessionMm,\n mobility,\n furcation,\n });\n }\n\n return findings;\n}\n\n/* -------------------------------------------------------------------- */\n/* Interop + construction */\n/* -------------------------------------------------------------------- */\n\n/** An empty chart with every tooth present but unscored. */\nexport function emptyPeriodontalChart(\n dentition: PeriodontalDentition = 'permanent',\n): PeriodontalChartValue {\n const { upper, lower } = periodontalArches(dentition);\n const out: PeriodontalChartValue = {};\n for (const fdi of [...upper, ...lower]) out[fdi] = {};\n return out;\n}\n\n/**\n * Flatten a chart value into the display-only card's `PeriodontalSite[]`\n * so a captured chart can be handed straight to `PeriodontalChartCard`.\n */\nexport function toPeriodontalSiteList(\n value: PeriodontalChartValue,\n): PeriodontalSite[] {\n const out: PeriodontalSite[] = [];\n for (const [tooth, state] of Object.entries(value)) {\n if (state.missing || !state.sites) continue;\n for (const site of PERIODONTAL_SITE_ORDER) {\n const reading = state.sites[site];\n if (!reading) continue;\n const { pocketDepthMm, recessionMm, bleeding, suppuration } = reading;\n if (\n pocketDepthMm == null &&\n recessionMm == null &&\n !bleeding &&\n !suppuration\n ) {\n continue;\n }\n out.push({\n tooth,\n site,\n pocketDepthMm,\n recessionMm,\n bleeding,\n suppuration,\n });\n }\n }\n return out;\n}\n\n/** Per-tooth mobility map (FDI → grade) for handing to the card. */\nexport function toMobilityMap(\n value: PeriodontalChartValue,\n): Record<string, PeriodontalMobility> {\n const out: Record<string, PeriodontalMobility> = {};\n for (const [tooth, state] of Object.entries(value)) {\n if (state.mobility != null) out[tooth] = state.mobility;\n }\n return out;\n}\n","import {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { FDI_TO_META, toothImageUrl } from '../tooth-scheme/tooth-data';\nimport { periodontalChartAgent } from './periodontal-chart.agent';\nimport {\n computePeriodontalFindings,\n computePeriodontalIndices,\n furcationSlots,\n periodontalArches,\n periodontalToothLabel,\n pocketSeverity,\n sitesForSide,\n BLEEDING_WARN_PERCENT,\n MODERATE_POCKET_MM,\n PERIODONTAL_BUCCAL_SITES,\n PLAQUE_WARN_PERCENT,\n SEVERE_POCKET_MM,\n type FdiId,\n type PeriodontalChartValue,\n type PeriodontalDentition,\n type PeriodontalIndices,\n type PeriodontalMobility,\n type PeriodontalNumbering,\n type PeriodontalSide,\n type PeriodontalSiteId,\n type PeriodontalSiteState,\n type PeriodontalToothFinding,\n type PeriodontalToothState,\n} from './periodontal-chart-data';\n\n/* -------------------------------------------------------------------- */\n/* PeriodontalChart */\n/* */\n/* The editable full-mouth periodontal chart — the perio counterpart of */\n/* ToothScheme. It records six probing sites per tooth (buccal + lingual */\n/* × mesial/mid/distal): pocket depth, recession, bleeding, plaque, plus */\n/* per-tooth mobility / missing / implant / furcation / note. It renders */\n/* one real <table> per arch (semantic <th scope> headers + caption — the */\n/* accessible source of truth), a decorative depth/recession curve per */\n/* arch, and a whole-mouth index strip (mean PD / CAL, plaque %, BoP %). */\n/* */\n/* The value keys by FDI and always emits FDI, so it interchanges */\n/* losslessly with ToothScheme, PeriodontalChartCard, and the platform */\n/* chart. Every visible string flows through `useTranslation()` (default */\n/* namespace `ui`) under the `periodontalChart.*` keys. */\n/* */\n/* Two conscious deviations, both driven by density (32 teeth × 6 sites): */\n/* - Native <input>/<select>/<label> rather than the kit Checkbox/Select */\n/* Radix primitives — a perio chart is hundreds of tiny inline cells; */\n/* the kit primitives carry chrome the wrong shape/weight here. Cells */\n/* stay tokenised and explicitly labelled. */\n/* - Interactive targets are 24px (the WCAG 2.5.8 AA floor) not the kit's */\n/* 44/48px --min-target-size; full-size targets would make the grid */\n/* unusably large. Documented dense-data-grid exception. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n 'ds:w-full ds:flex ds:flex-col ds:gap-[var(--spacing-lg)] ds:text-[color:var(--foreground)]',\n {\n variants: {\n // Disabled is conveyed by the inputs' own `disabled` state (native\n // disabled controls are excluded from the axe contrast check). We must\n // NOT dim the whole root with opacity — that would drop the legend,\n // captions and headers below the AA contrast floor.\n disabled: { true: 'ds:cursor-not-allowed', false: '' },\n },\n defaultVariants: { disabled: false },\n },\n);\n\n// Numeric probing-depth / recession input. Depth severity is paired with a\n// weight bump and a border shift so the flag survives greyscale /\n// forced-colors — colour is never the sole cue (an sr-only worded suffix\n// carries the same signal for screen-reader users).\nconst numericInputVariants = cva(\n 'ds:h-6 ds:w-[3.5ch] ds:rounded-[var(--radius-sm)] ds:border ds:bg-[var(--background)] ds:text-center ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n {\n variants: {\n severity: {\n normal:\n 'ds:border-[color:var(--input)] ds:text-[color:var(--foreground)]',\n moderate:\n 'ds:border-[color:var(--warning)] ds:font-semibold ds:text-[color:var(--warning-foreground)]',\n severe:\n 'ds:border-[color:var(--destructive)] ds:font-bold ds:text-[color:var(--destructive)]',\n },\n },\n defaultVariants: { severity: 'normal' },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Imperative handle — structural state + guarded edits, never PHI notes. */\nexport interface PeriodontalChartHandle {\n /** The current whole-mouth value. */\n getValue: () => PeriodontalChartValue;\n /** The recorded state of one tooth, if any. */\n getTooth: (fdi: string) => PeriodontalToothState | undefined;\n /** Derived whole-mouth indices (mean PD/CAL, plaque %, BoP %). */\n getIndices: () => PeriodontalIndices;\n /** Narratable per-tooth problem list — flagged teeth only, never the note. */\n getFindings: () => PeriodontalToothFinding[];\n /** FDI ids of every tooth flagged missing. */\n missingTeeth: () => string[];\n /** Patch one probing site. */\n setSite: (\n fdi: string,\n site: PeriodontalSiteId,\n patch: Partial<PeriodontalSiteState>,\n ) => void;\n /** Patch tooth-level state (mobility / missing / implant / note). */\n setTooth: (fdi: string, patch: Partial<PeriodontalToothState>) => void;\n /** Clear every reading from a tooth. */\n clearTooth: (fdi: string) => void;\n /** Focus a specific measurement input. */\n focusSite: (\n fdi: string,\n site: PeriodontalSiteId,\n field: NumericField,\n ) => void;\n}\n\n/** The two numeric per-site measurements. */\ntype NumericField = 'pocketDepthMm' | 'recessionMm';\n/** The two per-site boolean flags rendered as toggles. */\ntype ToggleField = 'bleeding' | 'plaque';\n\nexport interface PeriodontalChartProps extends Omit<\n ComponentPropsWithoutRef<'div'>,\n 'aria-label' | 'children' | 'onChange' | 'defaultValue'\n> {\n /** Controlled value — keyed by FDI tooth number. */\n value?: PeriodontalChartValue;\n /** Uncontrolled initial value. */\n defaultValue?: PeriodontalChartValue;\n /** Fired with the full next value whenever any reading changes. */\n onChange?: (value: PeriodontalChartValue) => void;\n\n /** `'interactive'` (default) renders inputs; `'display'` renders read-only. */\n mode?: 'interactive' | 'display';\n /** Disable every input while keeping the chart interactive-styled. */\n disabled?: boolean;\n\n /** Which dentition to lay out. Drives the tooth columns. */\n dentition?: PeriodontalDentition;\n /** Tooth-number system for the column labels (value always keys FDI). */\n numbering?: PeriodontalNumbering;\n\n /** Show a photoreal tooth graphic per column. Default `true`. */\n showTeeth?: boolean;\n /**\n * Base URL for the photoreal tooth images, matching ToothScheme's tree —\n * `${assetBaseUrl}/side/{default|implant}/{top|bottom}/{fdi}.webp`. Defaults\n * to the kit's bundled static path.\n */\n assetBaseUrl?: string;\n /** Show the decorative per-arch depth/recession curve. Default `true`. */\n showCurves?: boolean;\n /** Show the whole-mouth index strip. Default `true`. */\n showIndices?: boolean;\n\n /** Probing depth (mm) at/above which a site flags moderate. Default 4. */\n moderatePocketMm?: number;\n /** Probing depth (mm) at/above which a site flags severe. Default 6. */\n severePocketMm?: number;\n\n /** Locale for number formatting. Falls back to `i18n.language`. */\n locale?: string;\n\n /** Override the default group aria-label. */\n 'aria-label'?: string;\n /** Agent-readiness consumer id, surfaced as `data-component-id`. */\n id?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\n/** Immutably patch one tooth of a chart value. */\nfunction patchTooth(\n value: PeriodontalChartValue,\n fdi: string,\n patch: Partial<PeriodontalToothState>,\n): PeriodontalChartValue {\n return { ...value, [fdi]: { ...value[fdi], ...patch } };\n}\n\n/** Immutably patch one site of one tooth. */\nfunction patchSite(\n value: PeriodontalChartValue,\n fdi: string,\n site: PeriodontalSiteId,\n patch: Partial<PeriodontalSiteState>,\n): PeriodontalChartValue {\n const tooth = value[fdi] ?? {};\n const sites = tooth.sites ?? {};\n return {\n ...value,\n [fdi]: {\n ...tooth,\n sites: { ...sites, [site]: { ...sites[site], ...patch } },\n },\n };\n}\n\n/** Read-only worded severity suffix for screen readers. */\nfunction severityTextClass(severity: 'normal' | 'moderate' | 'severe'): string {\n if (severity === 'severe')\n return 'ds:font-bold ds:text-[color:var(--destructive)]';\n if (severity === 'moderate')\n return 'ds:font-semibold ds:text-[color:var(--warning-foreground)]';\n return '';\n}\n\n/** English fallback labels for the six probing sites (belt-and-braces). */\nconst SITE_DEFAULT: Record<PeriodontalSiteId, string> = {\n db: 'disto-buccal',\n b: 'buccal',\n mb: 'mesio-buccal',\n dl: 'disto-lingual',\n l: 'lingual',\n ml: 'mesio-lingual',\n};\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const PeriodontalChart = forwardRef<\n PeriodontalChartHandle,\n PeriodontalChartProps\n>(\n (\n {\n value,\n defaultValue,\n onChange,\n mode = 'interactive',\n disabled = false,\n dentition = 'permanent',\n numbering = 'fdi',\n showTeeth = true,\n assetBaseUrl = '/toothscheme/teeth',\n showCurves = true,\n showIndices = true,\n moderatePocketMm = MODERATE_POCKET_MM,\n severePocketMm = SEVERE_POCKET_MM,\n locale,\n 'aria-label': ariaLabel,\n id,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const resolvedLocale = locale ?? i18n.language ?? 'en';\n const rootRef = useRef<HTMLDivElement>(null);\n\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<PeriodontalChartValue>(\n () => defaultValue ?? {},\n );\n const resolved = isControlled ? value : internal;\n const readOnly = mode === 'display';\n\n // Latest-value ref so the imperative + agent handles read fresh state\n // without being rebuilt on every keystroke. Re-synced from `resolved` each\n // render, and — crucially — advanced inside `commit` so two edits in the\n // same tick (an agent batch, or two handle calls) compose instead of the\n // second read clobbering the first (last-write-wins).\n const valueRef = useRef(resolved);\n valueRef.current = resolved;\n\n const commit = useCallback(\n (next: PeriodontalChartValue) => {\n valueRef.current = next;\n if (!isControlled) setInternal(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n const setSite = useCallback(\n (\n fdi: string,\n site: PeriodontalSiteId,\n patch: Partial<PeriodontalSiteState>,\n ) => commit(patchSite(valueRef.current, fdi, site, patch)),\n [commit],\n );\n const setTooth = useCallback(\n (fdi: string, patch: Partial<PeriodontalToothState>) =>\n commit(patchTooth(valueRef.current, fdi, patch)),\n [commit],\n );\n const clearTooth = useCallback(\n (fdi: string) => {\n const next = { ...valueRef.current };\n delete next[fdi];\n commit(next);\n },\n [commit],\n );\n const focusSite = useCallback(\n (fdi: string, site: PeriodontalSiteId, field: NumericField) => {\n try {\n rootRef.current\n ?.querySelector<HTMLInputElement>(\n `[data-perio-input][data-fdi=\"${fdi}\"][data-site=\"${site}\"][data-field=\"${field}\"]`,\n )\n ?.focus();\n } catch {\n // A malformed agent-supplied id must not throw an uncaught SyntaxError.\n }\n },\n [],\n );\n\n const indices = useMemo(\n () => computePeriodontalIndices(resolved),\n [resolved],\n );\n\n const handle = useMemo<PeriodontalChartHandle>(\n () => ({\n getValue: () => valueRef.current,\n getTooth: (fdi) => valueRef.current[fdi],\n getIndices: () => computePeriodontalIndices(valueRef.current),\n getFindings: () =>\n computePeriodontalFindings(valueRef.current, {\n moderate: moderatePocketMm,\n severe: severePocketMm,\n }),\n missingTeeth: () =>\n Object.entries(valueRef.current)\n .filter(([, s]) => s.missing)\n .map(([fdi]) => fdi),\n setSite,\n setTooth,\n clearTooth,\n focusSite,\n }),\n [\n setSite,\n setTooth,\n clearTooth,\n focusSite,\n moderatePocketMm,\n severePocketMm,\n ],\n );\n\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(periodontalChartAgent, handle, id);\n\n const arches = periodontalArches(dentition);\n const resolvedAriaLabel =\n ariaLabel ?? t('periodontalChart.ariaLabel', 'Periodontal chart');\n\n const formatNumber = useCallback(\n (n: number): string => {\n try {\n return new Intl.NumberFormat(resolvedLocale, {\n maximumFractionDigits: 1,\n }).format(n);\n } catch {\n return String(n);\n }\n },\n [resolvedLocale],\n );\n\n const siteLabel = useCallback(\n (site: PeriodontalSiteId): string =>\n t(`periodontalChart.site.${site}`, {\n defaultValue: SITE_DEFAULT[site],\n }),\n [t],\n );\n\n // The tooth identifier announced to screen readers follows the DISPLAYED\n // numbering, so the audible label matches the visible column header (data-fdi\n // stays FDI for interchange). For the default `fdi` numbering this is a no-op.\n const toothName = useCallback(\n (fdi: string): string => periodontalToothLabel(fdi, numbering),\n [numbering],\n );\n\n const dash = (\n <span\n aria-hidden=\"true\"\n className=\"ds:text-[color:var(--muted-foreground)]\"\n >\n {t('periodontalChart.cell.empty', '–')}\n </span>\n );\n\n /* ---------------------------------------------------------------- */\n /* Keyboard navigation between numeric inputs */\n /* ---------------------------------------------------------------- */\n\n const onKeyDown = useCallback((e: ReactKeyboardEvent<HTMLDivElement>) => {\n const el = e.target as HTMLElement;\n if (!el.matches?.('[data-perio-input]')) return;\n const key = e.key;\n if (\n key !== 'ArrowLeft' &&\n key !== 'ArrowRight' &&\n key !== 'ArrowUp' &&\n key !== 'ArrowDown'\n ) {\n return;\n }\n e.preventDefault();\n const root = rootRef.current;\n if (!root) return;\n\n if (key === 'ArrowUp' || key === 'ArrowDown') {\n // Flip between the pocket-depth and recession input of the same site.\n const fdi = el.getAttribute('data-fdi');\n const site = el.getAttribute('data-site');\n const field = el.getAttribute('data-field');\n const other =\n field === 'pocketDepthMm' ? 'recessionMm' : 'pocketDepthMm';\n const target = root.querySelector<HTMLInputElement>(\n `[data-perio-input][data-fdi=\"${fdi}\"][data-site=\"${site}\"][data-field=\"${other}\"]`,\n );\n target?.focus();\n target?.select();\n return;\n }\n\n // Left / right walk the numeric inputs in document order.\n const inputs = Array.from(\n root.querySelectorAll<HTMLInputElement>('[data-perio-input]'),\n );\n const idx = inputs.indexOf(el as HTMLInputElement);\n if (idx === -1) return;\n const delta = key === 'ArrowRight' ? 1 : -1;\n const target = inputs[idx + delta];\n target?.focus();\n target?.select();\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Cell renderers */\n /* ---------------------------------------------------------------- */\n\n const cellClass =\n 'ds:px-[var(--spacing-2xs)] ds:py-[var(--spacing-2xs)] ds:text-center ds:border-b ds:border-[color:var(--border)]';\n const rowHeaderClass =\n 'ds:px-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)] ds:text-start ds:font-medium ds:border-b ds:border-[color:var(--border)] ds:whitespace-nowrap';\n\n const renderNumericInput = (\n fdi: string,\n site: PeriodontalSiteId,\n field: NumericField,\n ): ReactNode => {\n const raw = resolved[fdi]?.sites?.[site]?.[field];\n const severity =\n field === 'pocketDepthMm'\n ? pocketSeverity(raw, moderatePocketMm, severePocketMm)\n : 'normal';\n const label = t(`periodontalChart.input.${field}`, {\n tooth: toothName(fdi),\n site: siteLabel(site),\n defaultValue:\n field === 'pocketDepthMm'\n ? 'Tooth {{tooth}} {{site}} pocket depth, millimetres'\n : 'Tooth {{tooth}} {{site}} recession, millimetres',\n });\n\n // Worded severity cue announced to screen readers in BOTH display and\n // edit modes (the visual cue is colour + font-weight + border — never\n // colour alone).\n const severityWord =\n severity === 'severe'\n ? t('periodontalChart.flag.severe', 'severe pocket')\n : severity === 'moderate'\n ? t('periodontalChart.flag.moderate', 'moderate pocket')\n : '';\n\n if (readOnly) {\n if (raw == null || !Number.isFinite(raw)) {\n return <span className=\"ds:inline-block ds:w-[3.5ch]\">{dash}</span>;\n }\n return (\n <span\n className={`ds:inline-block ds:w-[3.5ch] ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ${severityTextClass(\n severity,\n )}`}\n >\n <span>{formatNumber(raw)}</span>\n {severityWord ? (\n <span className=\"ds:sr-only\"> {severityWord}</span>\n ) : null}\n </span>\n );\n }\n\n return (\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n disabled={disabled}\n aria-label={severityWord ? `${label}, ${severityWord}` : label}\n data-perio-input=\"\"\n data-fdi={fdi}\n data-site={site}\n data-field={field}\n value={raw == null ? '' : String(raw)}\n onFocus={(e) => e.currentTarget.select()}\n onChange={(e) => {\n const text = e.target.value.trim();\n let next: number | undefined;\n if (text === '' || text === '-') {\n next = undefined;\n } else {\n const n = Number(text);\n if (!Number.isFinite(n)) return;\n // Depth is non-negative; recession may be negative (overgrowth).\n next = field === 'pocketDepthMm' ? Math.max(0, n) : n;\n }\n setSite(\n fdi,\n site,\n field === 'pocketDepthMm'\n ? { pocketDepthMm: next }\n : { recessionMm: next },\n );\n }}\n className={numericInputVariants({ severity })}\n />\n );\n };\n\n const renderToggle = (\n fdi: string,\n site: PeriodontalSiteId,\n field: ToggleField,\n ): ReactNode => {\n const checked = !!resolved[fdi]?.sites?.[site]?.[field];\n const label = t(`periodontalChart.toggle.${field}`, {\n tooth: toothName(fdi),\n site: siteLabel(site),\n defaultValue:\n field === 'bleeding'\n ? 'Tooth {{tooth}} {{site}} bleeding on probing'\n : 'Tooth {{tooth}} {{site}} plaque',\n });\n const accent =\n field === 'bleeding'\n ? 'ds:accent-[var(--destructive)]'\n : 'ds:accent-[var(--warning)]';\n\n if (readOnly) {\n if (!checked)\n return <span className=\"ds:inline-block ds:size-6\">{dash}</span>;\n return (\n <span\n role=\"img\"\n aria-label={label}\n className={`ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${\n field === 'bleeding'\n ? 'ds:bg-[var(--destructive)]'\n : 'ds:bg-[var(--warning)]'\n }`}\n />\n );\n }\n return (\n <label className=\"ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center\">\n <span className=\"ds:sr-only\">{label}</span>\n <input\n type=\"checkbox\"\n disabled={disabled}\n checked={checked}\n onChange={(e) =>\n setSite(\n fdi,\n site,\n field === 'bleeding'\n ? { bleeding: e.target.checked }\n : { plaque: e.target.checked },\n )\n }\n className={`ds:size-4 ${accent} ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50`}\n />\n </label>\n );\n };\n\n const renderFurcationCell = (\n fdi: string,\n side: PeriodontalSide,\n ): ReactNode => {\n const tooth = resolved[fdi];\n const slots = furcationSlots(fdi, side);\n if (tooth?.missing || slots === 0) return dash;\n const flags = tooth?.furcation?.[side] ?? [];\n return (\n <span className=\"ds:inline-flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]\">\n {Array.from({ length: slots }, (_, i) => {\n const checked = !!flags[i];\n const label = t('periodontalChart.toggle.furcation', {\n tooth: toothName(fdi),\n side: t(`periodontalChart.side.${side}`, {\n defaultValue: side === 'buccal' ? 'buccal' : 'lingual',\n }),\n n: i + 1,\n defaultValue: 'Tooth {{tooth}} {{side}} furcation {{n}}',\n });\n if (readOnly) {\n return checked ? (\n <span\n key={i}\n role=\"img\"\n aria-label={label}\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--primary)]\"\n />\n ) : (\n <span\n key={i}\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:border ds:border-[color:var(--border)]\"\n />\n );\n }\n return (\n <label\n key={i}\n className=\"ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center\"\n >\n <span className=\"ds:sr-only\">{label}</span>\n <input\n type=\"checkbox\"\n disabled={disabled}\n checked={checked}\n onChange={(e) => {\n const next = [...(tooth?.furcation?.[side] ?? [])];\n while (next.length < slots) next.push(false);\n next[i] = e.target.checked;\n setTooth(fdi, {\n furcation: { ...tooth?.furcation, [side]: next },\n });\n }}\n className=\"ds:size-4 ds:accent-[var(--primary)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n />\n </label>\n );\n })}\n </span>\n );\n };\n\n /* One measurement row (PD or REC) across an arch, for one aspect. */\n const renderNumericRow = (\n teeth: FdiId[],\n labelKey: string,\n defaultLabel: string,\n side: PeriodontalSide,\n field: NumericField,\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t(labelKey, defaultLabel)}\n </th>\n {teeth.map((fdi) => (\n <td key={fdi} className={cellClass}>\n {resolved[fdi]?.missing ? (\n dash\n ) : (\n <span className=\"ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]\">\n {sitesForSide(side).map((site) => (\n <span key={site}>{renderNumericInput(fdi, site, field)}</span>\n ))}\n </span>\n )}\n </td>\n ))}\n </tr>\n );\n\n /* One toggle row (BOP or plaque) across an arch, for one aspect. */\n const renderToggleRow = (\n teeth: FdiId[],\n labelKey: string,\n defaultLabel: string,\n side: PeriodontalSide,\n field: ToggleField,\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t(labelKey, defaultLabel)}\n </th>\n {teeth.map((fdi) => (\n <td key={fdi} className={cellClass}>\n {resolved[fdi]?.missing ? (\n dash\n ) : (\n <span className=\"ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]\">\n {sitesForSide(side).map((site) => (\n <span key={site}>{renderToggle(fdi, site, field)}</span>\n ))}\n </span>\n )}\n </td>\n ))}\n </tr>\n );\n\n const renderFurcationRow = (\n teeth: FdiId[],\n side: PeriodontalSide,\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.furcation', 'Furcation')}\n </th>\n {teeth.map((fdi) => (\n <td key={fdi} className={cellClass}>\n {renderFurcationCell(fdi, side)}\n </td>\n ))}\n </tr>\n );\n\n const renderFlagRow = (\n teeth: FdiId[],\n labelKey: string,\n defaultLabel: string,\n field: 'missing' | 'implant',\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t(labelKey, defaultLabel)}\n </th>\n {teeth.map((fdi) => {\n // Implant is meaningless on an absent tooth; missing is always\n // togglable so a tooth can be marked present/absent.\n if (field === 'implant' && resolved[fdi]?.missing) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const checked = !!resolved[fdi]?.[field];\n const label = t(`periodontalChart.flag.${field}Label`, {\n tooth: toothName(fdi),\n defaultValue:\n field === 'missing'\n ? 'Tooth {{tooth}} missing'\n : 'Tooth {{tooth}} implant',\n });\n return (\n <td key={fdi} className={cellClass}>\n {readOnly ? (\n checked ? (\n <span\n role=\"img\"\n aria-label={label}\n className=\"type-meta ds:font-semibold\"\n >\n {field === 'missing'\n ? t('periodontalChart.flag.missingShort', '×')\n : t('periodontalChart.flag.implantShort', 'I')}\n </span>\n ) : (\n dash\n )\n ) : (\n <label className=\"ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center\">\n <span className=\"ds:sr-only\">{label}</span>\n <input\n type=\"checkbox\"\n disabled={disabled}\n checked={checked}\n onChange={(e) =>\n setTooth(\n fdi,\n field === 'missing'\n ? { missing: e.target.checked }\n : { implant: e.target.checked },\n )\n }\n className=\"ds:size-4 ds:accent-[var(--primary)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n />\n </label>\n )}\n </td>\n );\n })}\n </tr>\n );\n\n const renderMobilityRow = (teeth: FdiId[]): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.mobility', 'Mobility')}\n </th>\n {teeth.map((fdi) => {\n if (resolved[fdi]?.missing) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const grade = resolved[fdi]?.mobility;\n const label = t('periodontalChart.mobility.label', {\n tooth: toothName(fdi),\n defaultValue: 'Tooth {{tooth}} mobility grade',\n });\n if (readOnly) {\n return (\n <td key={fdi} className={cellClass}>\n {grade == null ? (\n dash\n ) : (\n <span role=\"img\" aria-label={label} className=\"type-meta\">\n {grade}\n </span>\n )}\n </td>\n );\n }\n return (\n <td key={fdi} className={cellClass}>\n <select\n disabled={disabled}\n aria-label={label}\n value={grade == null ? '' : String(grade)}\n onChange={(e) =>\n setTooth(fdi, {\n mobility:\n e.target.value === ''\n ? undefined\n : (Number(e.target.value) as PeriodontalMobility),\n })\n }\n className=\"ds:h-6 ds:rounded-[var(--radius-sm)] ds:border ds:border-[color:var(--input)] ds:bg-[var(--background)] ds:text-[color:var(--foreground)] ds:text-center ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n >\n <option value=\"\">\n {t('periodontalChart.mobility.none', '–')}\n </option>\n <option value=\"0\">0</option>\n <option value=\"1\">1</option>\n <option value=\"2\">2</option>\n <option value=\"3\">3</option>\n </select>\n </td>\n );\n })}\n </tr>\n );\n\n const renderNoteRow = (teeth: FdiId[]): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.note', 'Note')}\n </th>\n {teeth.map((fdi) => {\n if (resolved[fdi]?.missing) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const note = resolved[fdi]?.note ?? '';\n const label = t('periodontalChart.note.label', {\n tooth: toothName(fdi),\n defaultValue: 'Tooth {{tooth}} note',\n });\n if (readOnly) {\n return (\n <td key={fdi} className={`${cellClass} ds:text-start`}>\n {note ? <span className=\"type-meta\">{note}</span> : dash}\n </td>\n );\n }\n return (\n <td key={fdi} className={cellClass}>\n <input\n type=\"text\"\n disabled={disabled}\n aria-label={label}\n value={note}\n onChange={(e) => setTooth(fdi, { note: e.target.value })}\n className=\"ds:h-6 ds:w-[6ch] ds:rounded-[var(--radius-sm)] ds:border ds:border-[color:var(--input)] ds:bg-[var(--background)] ds:text-[color:var(--foreground)] ds:px-[var(--spacing-2xs)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n />\n </td>\n );\n })}\n </tr>\n );\n\n /* One row of photoreal tooth graphics for an arch (facial / side view),\n reusing ToothScheme's asset tree so the odontogram and perio chart show\n the same teeth. Decorative — the FDI column header names each tooth. */\n const renderToothRow = (teeth: FdiId[]): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.tooth', 'Tooth')}\n </th>\n {teeth.map((fdi) => {\n const tooth = resolved[fdi];\n const meta = FDI_TO_META[fdi];\n if (tooth?.missing || !meta) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const symbol = tooth?.implant ? 'implant' : 'default';\n return (\n <td key={fdi} className={cellClass}>\n <img\n src={toothImageUrl(meta, assetBaseUrl, symbol, 'side')}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"lazy\"\n className=\"ds:mx-auto ds:h-12 ds:w-auto ds:max-w-full ds:object-contain\"\n />\n </td>\n );\n })}\n </tr>\n );\n\n /* ---------------------------------------------------------------- */\n /* Decorative depth / recession curve (buccal aspect) */\n /* ---------------------------------------------------------------- */\n\n const renderCurve = (teeth: FdiId[]): ReactNode => {\n const SCALE = 6; // px per mm\n const STEP = 24; // px between sites\n const REC_MM = 5; // recession region above the gum line\n const DEP_MM = 13; // pocket-depth region below the gum line\n const MID = 8 + REC_MM * SCALE; // y of the 0 mm gingival-margin line\n const HEIGHT = MID + DEP_MM * SCALE + 8;\n const depthY = (mm: number) =>\n MID + Math.min(Math.max(0, mm), DEP_MM) * SCALE;\n const recY = (mm: number) =>\n MID - Math.min(Math.max(0, mm), REC_MM) * SCALE;\n\n // Collect readings as CONTIGUOUS runs — a gap (missing tooth or an\n // unrecorded site) breaks the run so the curve never bridges absent data.\n type Pt = { x: number; y: number };\n const depthRuns: Pt[][] = [];\n const marginRuns: Pt[][] = [];\n let depthRun: Pt[] = [];\n let marginRun: Pt[] = [];\n const flush = () => {\n if (depthRun.length) depthRuns.push(depthRun);\n if (marginRun.length) marginRuns.push(marginRun);\n depthRun = [];\n marginRun = [];\n };\n let x = STEP / 2;\n for (const fdi of teeth) {\n const missing = resolved[fdi]?.missing;\n for (const site of PERIODONTAL_BUCCAL_SITES) {\n const reading = missing ? undefined : resolved[fdi]?.sites?.[site];\n const pd = reading?.pocketDepthMm;\n const rec = reading?.recessionMm;\n if (pd != null && Number.isFinite(pd))\n depthRun.push({ x, y: depthY(pd) });\n else if (depthRun.length) {\n depthRuns.push(depthRun);\n depthRun = [];\n }\n if (rec != null && Number.isFinite(rec))\n marginRun.push({ x, y: recY(rec) });\n else if (marginRun.length) {\n marginRuns.push(marginRun);\n marginRun = [];\n }\n x += STEP;\n }\n }\n flush();\n\n const width = teeth.length * PERIODONTAL_BUCCAL_SITES.length * STEP;\n const toPts = (a: Pt[]) => a.map((p) => `${p.x},${p.y}`).join(' ');\n const depthPts = depthRuns.flat();\n const marginPts = marginRuns.flat();\n\n return (\n <svg\n viewBox={`0 0 ${width} ${HEIGHT}`}\n className=\"ds:h-[132px] ds:w-full\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {/* Depth gridlines (dashed) below the gum line */}\n {[3, 6, 9, 12].map((mm) => (\n <line\n key={mm}\n x1={0}\n x2={width}\n y1={depthY(mm)}\n y2={depthY(mm)}\n stroke=\"var(--border)\"\n strokeWidth={1}\n strokeDasharray=\"2 4\"\n />\n ))}\n {/* The 0 mm gingival-margin reference line (solid) */}\n <line\n x1={0}\n x2={width}\n y1={MID}\n y2={MID}\n stroke=\"var(--border)\"\n strokeWidth={1.5}\n />\n {/* One filled area + line per contiguous depth run */}\n {depthRuns.map((run, i) =>\n run.length >= 2 ? (\n <polygon\n key={`fill-${i}`}\n points={`${run[0].x},${MID} ${toPts(run)} ${run[run.length - 1].x},${MID}`}\n fill=\"var(--success)\"\n fillOpacity={0.14}\n />\n ) : null,\n )}\n {depthRuns.map((run, i) =>\n run.length >= 2 ? (\n <polyline\n key={`depth-${i}`}\n points={toPts(run)}\n fill=\"none\"\n stroke=\"var(--success)\"\n strokeWidth={2.5}\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n ) : null,\n )}\n {marginRuns.map((run, i) =>\n run.length >= 2 ? (\n <polyline\n key={`margin-${i}`}\n points={toPts(run)}\n fill=\"none\"\n stroke=\"var(--accent)\"\n strokeWidth={2.5}\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n ) : null,\n )}\n {/* Data points — so isolated readings (single-site runs) still show */}\n {depthPts.map((p, i) => (\n <circle\n key={`dp-${i}`}\n cx={p.x}\n cy={p.y}\n r={2}\n fill=\"var(--success)\"\n />\n ))}\n {marginPts.map((p, i) => (\n <circle\n key={`mp-${i}`}\n cx={p.x}\n cy={p.y}\n r={2}\n fill=\"var(--accent)\"\n />\n ))}\n </svg>\n );\n };\n\n /* ---------------------------------------------------------------- */\n /* One arch */\n /* ---------------------------------------------------------------- */\n\n const renderArch = (\n teeth: FdiId[],\n captionKey: string,\n defaultCaption: string,\n ): ReactNode => (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {/* dir=\"ltr\" pins the tooth-column order to clinical convention\n regardless of layout direction — mirroring the arch in RTL would\n swap the patient's left and right teeth (a patient-safety hazard),\n the same fixed-orientation rule ToothScheme follows. It also keeps\n the ← → arrow-key nav aligned with the visual column order. */}\n <div\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- keyboard-focusable horizontal scroll region; the data table is the accessible source of truth\n tabIndex={0}\n role=\"group\"\n dir=\"ltr\"\n aria-label={t(captionKey, defaultCaption)}\n className=\"ds:overflow-x-auto ds:rounded-[var(--radius-sm)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]\"\n >\n <table className=\"type-body-sm ds:w-full ds:border-collapse\">\n <caption className=\"type-label ds:text-start ds:mb-[var(--spacing-2xs)] ds:text-[color:var(--muted-foreground)]\">\n {t(captionKey, defaultCaption)}\n </caption>\n <thead>\n <tr>\n <th\n scope=\"col\"\n className=\"ds:px-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)] ds:text-start ds:border-b ds:border-[color:var(--border)]\"\n >\n {t('periodontalChart.column.measure', 'Measurement')}\n </th>\n {teeth.map((fdi) => (\n <th\n key={fdi}\n scope=\"col\"\n data-fdi={fdi}\n className=\"ds:px-[var(--spacing-2xs)] ds:py-[var(--spacing-2xs)] ds:text-center ds:border-b ds:border-[color:var(--border)] ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)]\"\n >\n <span className=\"ds:sr-only\">\n {t('periodontalChart.toothHeader', {\n tooth: toothName(fdi),\n defaultValue: 'Tooth {{tooth}}',\n })}\n </span>\n <span aria-hidden=\"true\">\n {periodontalToothLabel(fdi, numbering)}\n </span>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {renderFlagRow(\n teeth,\n 'periodontalChart.row.missing',\n 'Missing',\n 'missing',\n )}\n {renderFlagRow(\n teeth,\n 'periodontalChart.row.implant',\n 'Implant',\n 'implant',\n )}\n {renderMobilityRow(teeth)}\n {/* Buccal aspect */}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.pocketDepthBuccal',\n 'Pocket depth — buccal',\n 'buccal',\n 'pocketDepthMm',\n )}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.recessionBuccal',\n 'Recession — buccal',\n 'buccal',\n 'recessionMm',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.bleedingBuccal',\n 'Bleeding — buccal',\n 'buccal',\n 'bleeding',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.plaqueBuccal',\n 'Plaque — buccal',\n 'buccal',\n 'plaque',\n )}\n {renderFurcationRow(teeth, 'buccal')}\n {/* Tooth graphics sit between the buccal and lingual aspects */}\n {showTeeth ? renderToothRow(teeth) : null}\n {/* Lingual aspect */}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.pocketDepthLingual',\n 'Pocket depth — lingual',\n 'lingual',\n 'pocketDepthMm',\n )}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.recessionLingual',\n 'Recession — lingual',\n 'lingual',\n 'recessionMm',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.bleedingLingual',\n 'Bleeding — lingual',\n 'lingual',\n 'bleeding',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.plaqueLingual',\n 'Plaque — lingual',\n 'lingual',\n 'plaque',\n )}\n {renderFurcationRow(teeth, 'lingual')}\n {renderNoteRow(teeth)}\n </tbody>\n </table>\n {showCurves ? (\n <figure\n className=\"ds:m-0 ds:mt-[var(--spacing-2xs)]\"\n aria-label={t('periodontalChart.curve.label', {\n arch: t(captionKey, defaultCaption),\n defaultValue: '{{arch}} probing depth and recession profile',\n })}\n >\n {renderCurve(teeth)}\n </figure>\n ) : null}\n </div>\n </div>\n );\n\n /* ---------------------------------------------------------------- */\n /* Index strip */\n /* ---------------------------------------------------------------- */\n\n const renderIndices = (): ReactNode => {\n const items: Array<{\n key: string;\n label: string;\n value: string;\n elevated: boolean;\n }> = [\n {\n key: 'meanPd',\n label: t('periodontalChart.index.meanPocketDepth', 'Mean PD'),\n value:\n indices.meanPocketDepthMm == null\n ? '–'\n : t('periodontalChart.unit.mm', {\n value: formatNumber(indices.meanPocketDepthMm),\n defaultValue: '{{value}} mm',\n }),\n elevated: false,\n },\n {\n key: 'meanCal',\n label: t('periodontalChart.index.meanAttachment', 'Mean CAL'),\n value:\n indices.meanAttachmentLevelMm == null\n ? '–'\n : t('periodontalChart.unit.mm', {\n value: formatNumber(indices.meanAttachmentLevelMm),\n defaultValue: '{{value}} mm',\n }),\n elevated: false,\n },\n {\n key: 'plaque',\n label: t('periodontalChart.index.plaque', 'Plaque'),\n value:\n indices.plaquePercent == null\n ? '–'\n : t('periodontalChart.unit.percent', {\n value: formatNumber(indices.plaquePercent),\n defaultValue: '{{value}}%',\n }),\n elevated:\n indices.plaquePercent != null &&\n indices.plaquePercent > PLAQUE_WARN_PERCENT,\n },\n {\n key: 'bleeding',\n label: t('periodontalChart.index.bleeding', 'BoP'),\n value:\n indices.bleedingPercent == null\n ? '–'\n : t('periodontalChart.unit.percent', {\n value: formatNumber(indices.bleedingPercent),\n defaultValue: '{{value}}%',\n }),\n elevated:\n indices.bleedingPercent != null &&\n indices.bleedingPercent > BLEEDING_WARN_PERCENT,\n },\n ];\n return (\n <dl\n className=\"type-body-sm ds:flex ds:flex-wrap ds:gap-[var(--spacing-lg)] ds:m-0\"\n aria-label={t('periodontalChart.index.label', 'Summary indices')}\n >\n {items.map((item) => (\n <div\n key={item.key}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n <dt className=\"type-meta ds:text-[color:var(--muted-foreground)]\">\n {item.label}\n </dt>\n <dd\n className={`ds:m-0 ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ${\n item.elevated\n ? 'ds:font-bold ds:text-[color:var(--destructive)]'\n : 'ds:text-[color:var(--foreground)]'\n }`}\n >\n <span>{item.value}</span>\n {item.elevated ? (\n <span className=\"ds:sr-only\">\n {' '}\n {t('periodontalChart.index.elevated', 'elevated')}\n </span>\n ) : null}\n </dd>\n </div>\n ))}\n </dl>\n );\n };\n\n /* ---------------------------------------------------------------- */\n /* Legend */\n /* ---------------------------------------------------------------- */\n\n const legend = (\n <div\n role=\"group\"\n aria-label={t('periodontalChart.legend.label', 'Chart legend')}\n className=\"type-meta ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-md)] ds:text-[color:var(--muted-foreground)]\"\n >\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:h-[2px] ds:w-4 ds:bg-[var(--success)]\"\n />\n {t('periodontalChart.legend.depth', 'Pocket depth')}\n </span>\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:h-[2px] ds:w-4 ds:bg-[var(--accent)]\"\n />\n {t('periodontalChart.legend.recession', 'Recession')}\n </span>\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--destructive)]\"\n />\n {t('periodontalChart.legend.bleeding', 'Bleeding on probing')}\n </span>\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--warning)]\"\n />\n {t('periodontalChart.legend.plaque', 'Plaque')}\n </span>\n </div>\n );\n\n return (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- roving-tabindex arrow-key nav across the chart group; each input is individually focusable and labelled\n <div\n ref={rootRef}\n role=\"group\"\n aria-label={resolvedAriaLabel}\n data-component=\"periodontal-chart\"\n data-component-id={id}\n data-mode={mode}\n id={id}\n {...rest}\n onKeyDown={onKeyDown}\n className={rootVariants({ disabled, className })}\n >\n {showIndices ? renderIndices() : null}\n {renderArch(arches.upper, 'periodontalChart.arch.upper', 'Upper arch')}\n {renderArch(arches.lower, 'periodontalChart.arch.lower', 'Lower arch')}\n {legend}\n </div>\n );\n },\n);\n\nPeriodontalChart.displayName = 'PeriodontalChart';\n\nexport type PeriodontalChartDisabled = NonNullable<\n VariantProps<typeof rootVariants>['disabled']\n>;\n"],"names":["FDI_SCHEMA","SITE_SCHEMA","MM_SCHEMA","INDICES_SCHEMA","FINDINGS_SCHEMA","sanitizeTooth","tooth","rest","periodontalChartAgent","handle","s","site","fdi","args","PERIODONTAL_BUCCAL_SITES","PERIODONTAL_LINGUAL_SITES","sitesForSide","side","MODERATE_POCKET_MM","SEVERE_POCKET_MM","PLAQUE_WARN_PERCENT","BLEEDING_WARN_PERCENT","pocketSeverity","depth","moderate","severe","PERIODONTAL_FURCATION_MAP","furcationSlots","_a","PERIODONTAL_PERMANENT_UPPER","PERMANENT_TEETH","PERIODONTAL_PERMANENT_LOWER","PERIODONTAL_PRIMARY_UPPER","PRIMARY_TEETH","PERIODONTAL_PRIMARY_LOWER","periodontalArches","dentition","periodontalToothLabel","numbering","FDI_TO_UNIVERSAL","FDI_TO_PALMER","round1","n","r","computePeriodontalIndices","value","depthSum","attachmentSum","scored","bleeding","plaque","deep","FINDING_ORDER","computePeriodontalFindings","options","rank","i","orderedFdis","a","b","findings","status","pockets","bleedingSites","plaqueSites","suppurationSites","maxPocketDepthMm","maxRecessionMm","PERIODONTAL_SITE_ORDER","reading","pocketDepthMm","recessionMm","suppuration","sev","mobility","furcation","_b","severity","p","emptyPeriodontalChart","upper","lower","out","toPeriodontalSiteList","state","toMobilityMap","rootVariants","cva","numericInputVariants","patchTooth","patch","patchSite","sites","severityTextClass","SITE_DEFAULT","PeriodontalChart","forwardRef","defaultValue","onChange","mode","disabled","showTeeth","assetBaseUrl","showCurves","showIndices","moderatePocketMm","severePocketMm","locale","ariaLabel","id","className","ref","t","i18n","useTranslation","resolvedLocale","rootRef","useRef","isControlled","internal","setInternal","useState","resolved","readOnly","valueRef","commit","useCallback","next","setSite","setTooth","clearTooth","focusSite","field","indices","useMemo","useImperativeHandle","useAgentRegistration","arches","resolvedAriaLabel","formatNumber","siteLabel","toothName","dash","jsx","onKeyDown","el","key","root","other","target","inputs","idx","cellClass","rowHeaderClass","renderNumericInput","raw","_c","label","severityWord","jsxs","e","text","renderToggle","checked","accent","renderFurcationCell","slots","flags","_","renderNumericRow","teeth","labelKey","defaultLabel","renderToggleRow","renderFurcationRow","renderFlagRow","renderMobilityRow","grade","renderNoteRow","note","renderToothRow","meta","FDI_TO_META","symbol","toothImageUrl","renderCurve","depthY","mm","recY","depthRuns","marginRuns","depthRun","marginRun","flush","x","missing","pd","rec","width","toPts","depthPts","marginPts","run","renderArch","captionKey","defaultCaption","renderIndices","items","item","legend"],"mappings":"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"}
1
+ {"version":3,"file":"periodontal-chart-B9fphroR.js","sources":["../../src/components/periodontal-chart/periodontal-chart.agent.ts","../../src/components/periodontal-chart/periodontal-chart-data.ts","../../src/components/periodontal-chart/periodontal-chart.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { PeriodontalChartHandle } from './periodontal-chart';\nimport type {\n PeriodontalMobility,\n PeriodontalSiteId,\n PeriodontalToothState,\n} from './periodontal-chart-data';\n\n/* -------------------------------------------------------------------- */\n/* PeriodontalChart — agent adapter */\n/* */\n/* The editable perio chart. Agents can introspect the derived indices, */\n/* the missing teeth and which teeth carry readings, and can drive */\n/* per-site edits. It NEVER exposes the free-text per-tooth `note` */\n/* (PHI) — neither in the always-on state snapshot nor via get_tooth, */\n/* which returns structural readings with the note stripped. */\n/* -------------------------------------------------------------------- */\n\nconst FDI_SCHEMA = {\n type: 'string',\n description: \"FDI tooth id ('11'–'48' permanent, '51'–'85' primary).\",\n} as const;\nconst SITE_SCHEMA = {\n enum: ['db', 'b', 'mb', 'dl', 'l', 'ml'],\n description:\n 'Probing site: (m/d)b = mesio-/disto-/mid buccal, (m/d)l = mesio-/disto-/mid lingual.',\n} as const;\nconst MM_SCHEMA = {\n type: 'integer',\n description: 'Whole millimetres — perio probing is recorded in integer mm.',\n} as const;\nconst INDICES_SCHEMA = {\n type: 'object',\n properties: {\n meanPocketDepthMm: { type: ['number', 'null'] },\n meanAttachmentLevelMm: { type: ['number', 'null'] },\n plaquePercent: { type: ['number', 'null'] },\n bleedingPercent: { type: ['number', 'null'] },\n scoredSiteCount: { type: 'number' },\n deepPocketCount: { type: 'number' },\n },\n} as const;\nconst FINDINGS_SCHEMA = {\n type: 'array',\n description:\n 'One entry per flagged tooth, in clinical order; each tooth’s pockets are deepest-first.',\n items: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n status: {\n enum: ['present', 'missing', 'implant'],\n description: 'Structural status of the tooth.',\n },\n severity: {\n enum: ['normal', 'moderate', 'severe'],\n description: 'Worst pocket severity band across the tooth.',\n },\n maxPocketDepthMm: { type: ['integer', 'null'] },\n pockets: {\n type: 'array',\n description: 'Sites at/above the moderate threshold, deepest first.',\n items: {\n type: 'object',\n properties: {\n site: SITE_SCHEMA,\n pocketDepthMm: MM_SCHEMA,\n severity: { enum: ['moderate', 'severe'] },\n },\n },\n },\n bleedingSites: { type: 'array', items: SITE_SCHEMA },\n plaqueSites: { type: 'array', items: SITE_SCHEMA },\n suppurationSites: { type: 'array', items: SITE_SCHEMA },\n maxRecessionMm: { type: ['number', 'null'] },\n mobility: {\n type: ['integer', 'null'],\n description: 'Miller mobility grade 1–3, or null when none.',\n },\n furcation: { type: 'array', items: { enum: ['buccal', 'lingual'] } },\n },\n },\n} as const;\n\n/** Structural (PHI-free) view of one tooth returned by get_tooth. */\nfunction sanitizeTooth(\n tooth: PeriodontalToothState | undefined,\n): Record<string, unknown> | null {\n if (!tooth) return null;\n const rest: Record<string, unknown> = { ...tooth };\n delete rest.note;\n return rest;\n}\n\nexport const periodontalChartAgent: AgentAdapter<PeriodontalChartHandle> = {\n id: 'periodontal-chart',\n // pick — focus a measurement input (transient); set_value / edit_inline —\n // record per-site probing readings and per-tooth flags.\n capabilities: ['pick', 'set_value', 'edit_inline'],\n state: {\n indices: {\n type: 'object',\n descriptionKey: 'ui.agent.periodontalChart.state.indices',\n description:\n 'Derived whole-mouth indices: mean probing depth, mean attachment level, plaque %, bleeding %, scored-site and deep-pocket counts. Aggregates only — no per-site or patient data.',\n read: (handle) => handle.getIndices(),\n },\n findings: {\n type: 'object[]',\n descriptionKey: 'ui.agent.periodontalChart.state.findings',\n description:\n 'Narratable per-tooth problem list — one entry per flagged tooth (missing, implant, a pocket at/above the moderate threshold, bleeding, suppuration, mobility ≥ 1, or furcation), in clinical order with each tooth’s pockets deepest-first. Each entry carries fdi, status, severity, maxPocketDepthMm, pockets, bleeding/plaque/suppuration sites, maxRecessionMm, mobility and furcation. Read this to summarise the chart aloud. Structural readings + FDI ids only — never the free-text note or patient identity.',\n read: (handle) => handle.getFindings(),\n },\n missingTeeth: {\n type: 'string[]',\n descriptionKey: 'ui.agent.periodontalChart.state.missingTeeth',\n description: 'FDI ids of every tooth flagged as missing.',\n read: (handle) => handle.missingTeeth(),\n },\n chartedTeeth: {\n type: 'string[]',\n descriptionKey: 'ui.agent.periodontalChart.state.chartedTeeth',\n description:\n 'FDI ids of teeth carrying at least one probing reading or flag. Identifiers only — never patient identity or the free-text note.',\n read: (handle) =>\n Object.entries(handle.getValue())\n .filter(([, s]) => {\n if (s.missing || s.implant || s.mobility != null) return true;\n if (s.furcation && Object.keys(s.furcation).length > 0) return true;\n const sites = s.sites ? Object.values(s.sites) : [];\n return sites.some(\n (site) =>\n site != null &&\n (site.pocketDepthMm != null ||\n site.recessionMm != null ||\n site.bleeding ||\n site.plaque ||\n site.suppuration),\n );\n })\n .map(([fdi]) => fdi),\n },\n },\n actions: {\n focus_site: {\n safety: 'read',\n argsType:\n '{ fdi: string; site: PeriodontalSiteId; field: \"pocketDepthMm\" | \"recessionMm\" }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n site: SITE_SCHEMA,\n field: { enum: ['pocketDepthMm', 'recessionMm'] },\n },\n required: ['fdi', 'site', 'field'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.focusSite',\n description: 'Move focus to a specific probing-measurement input.',\n invoke: (\n handle,\n args: {\n fdi: string;\n site: PeriodontalSiteId;\n field: 'pocketDepthMm' | 'recessionMm';\n },\n ) => {\n handle.focusSite(args.fdi, args.site, args.field);\n },\n },\n get_tooth: {\n safety: 'read',\n argsType: '{ fdi: string }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA },\n required: ['fdi'],\n },\n returns: 'object | null',\n returnsSchema: { type: ['object', 'null'] },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.getTooth',\n description:\n 'Read one tooth’s structural readings (sites, flags, mobility, furcation). The free-text note is never returned.',\n invoke: (handle, args: { fdi: string }) =>\n sanitizeTooth(handle.getTooth(args.fdi)),\n },\n get_indices: {\n safety: 'read',\n argsType: '{}',\n argsSchema: { type: 'object', properties: {} },\n returns: 'PeriodontalIndices',\n returnsSchema: INDICES_SCHEMA,\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.getIndices',\n description: 'Read the derived whole-mouth summary indices.',\n invoke: (handle) => handle.getIndices(),\n },\n get_findings: {\n safety: 'read',\n argsType: '{}',\n argsSchema: { type: 'object', properties: {} },\n returns: 'PeriodontalToothFinding[]',\n returnsSchema: FINDINGS_SCHEMA,\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.getFindings',\n description:\n 'Read the narratable per-tooth problem list (flagged teeth only). Use it to summarise the chart aloud — e.g. “46: 8 mm disto-buccal pocket, bleeding, grade 2 mobility”. Structural readings and FDI ids only; the free-text note is never returned.',\n invoke: (handle) => handle.getFindings(),\n },\n set_depth: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; mm: number }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA, site: SITE_SCHEMA, mm: MM_SCHEMA },\n required: ['fdi', 'site', 'mm'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setDepth',\n description: 'Record the probing pocket depth (mm) at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; mm: number },\n ) => {\n handle.setSite(args.fdi, args.site, {\n pocketDepthMm: Math.max(0, args.mm),\n });\n },\n },\n set_recession: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; mm: number }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA, site: SITE_SCHEMA, mm: MM_SCHEMA },\n required: ['fdi', 'site', 'mm'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setRecession',\n description: 'Record the gingival recession (mm) at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; mm: number },\n ) => {\n handle.setSite(args.fdi, args.site, { recessionMm: args.mm });\n },\n },\n toggle_bleeding: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; value: boolean }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n site: SITE_SCHEMA,\n value: { type: 'boolean' },\n },\n required: ['fdi', 'site', 'value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.toggleBleeding',\n description: 'Set bleeding on probing at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; value: boolean },\n ) => {\n handle.setSite(args.fdi, args.site, { bleeding: args.value });\n },\n },\n toggle_plaque: {\n safety: 'write',\n argsType: '{ fdi: string; site: PeriodontalSiteId; value: boolean }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n site: SITE_SCHEMA,\n value: { type: 'boolean' },\n },\n required: ['fdi', 'site', 'value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.togglePlaque',\n description: 'Set plaque presence at a site.',\n invoke: (\n handle,\n args: { fdi: string; site: PeriodontalSiteId; value: boolean },\n ) => {\n handle.setSite(args.fdi, args.site, { plaque: args.value });\n },\n },\n set_mobility: {\n safety: 'write',\n argsType: '{ fdi: string; grade: 0 | 1 | 2 | 3 }',\n argsSchema: {\n type: 'object',\n properties: {\n fdi: FDI_SCHEMA,\n grade: { enum: [0, 1, 2, 3] },\n },\n required: ['fdi', 'grade'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setMobility',\n description: 'Set a tooth’s Miller mobility grade (0–3).',\n invoke: (handle, args: { fdi: string; grade: PeriodontalMobility }) => {\n handle.setTooth(args.fdi, { mobility: args.grade });\n },\n },\n set_missing: {\n safety: 'write',\n argsType: '{ fdi: string; value: boolean }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA, value: { type: 'boolean' } },\n required: ['fdi', 'value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.setMissing',\n description: 'Flag a tooth present or absent.',\n invoke: (handle, args: { fdi: string; value: boolean }) => {\n handle.setTooth(args.fdi, { missing: args.value });\n },\n },\n clear_tooth: {\n safety: 'destructive',\n argsType: '{ fdi: string }',\n argsSchema: {\n type: 'object',\n properties: { fdi: FDI_SCHEMA },\n required: ['fdi'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.periodontalChart.actions.clearTooth',\n description: 'Clear every reading from a tooth.',\n invoke: (handle, args: { fdi: string }) => {\n handle.clearTooth(args.fdi);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'periodontal-chart',\n description: 'Marks the PeriodontalChart root group.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-fdi',\n description:\n 'Each tooth column header carries its FDI id as `data-fdi`. Every measurement input also carries `data-fdi` + `data-site` + `data-field`. The root `data-mode` exposes interactive vs display.',\n },\n },\n};\n","import {\n PERMANENT_TEETH,\n PRIMARY_TEETH,\n FDI_TO_UNIVERSAL,\n FDI_TO_PALMER,\n type FdiId,\n} from '../tooth-scheme/tooth-data';\nimport {\n PERIODONTAL_SITE_ORDER,\n type PeriodontalSiteId,\n type PeriodontalMobility,\n type PeriodontalSite,\n} from '../periodontal-chart-card/periodontal-chart-card';\n\n/* -------------------------------------------------------------------- */\n/* PeriodontalChart — data model */\n/* */\n/* The editable counterpart to the display-only PeriodontalChartCard. */\n/* Where the card takes a flat `PeriodontalSite[]` for read-only render, */\n/* the chart owns a keyed, mutable value — `Record<FdiId, tooth state>` */\n/* — so per-site probing data can be entered and emitted losslessly (the */\n/* chart always keys by FDI, matching ToothScheme's interchange rule). */\n/* */\n/* Site ids, mobility grades and the flat-site interop type are REUSED */\n/* from the card so the two components share one vocabulary. */\n/* -------------------------------------------------------------------- */\n\nexport type { FdiId, PeriodontalSiteId, PeriodontalMobility, PeriodontalSite };\nexport { PERIODONTAL_SITE_ORDER };\n\n/** The two probing aspects of a tooth. */\nexport type PeriodontalSide = 'buccal' | 'lingual';\n\n/** Buccal (facial) probing sites, in display order. */\nexport const PERIODONTAL_BUCCAL_SITES = ['db', 'b', 'mb'] as const;\n/** Lingual (palatal) probing sites, in display order. */\nexport const PERIODONTAL_LINGUAL_SITES = ['dl', 'l', 'ml'] as const;\n\n/** The three site ids for a given aspect. */\nexport function sitesForSide(\n side: PeriodontalSide,\n): readonly PeriodontalSiteId[] {\n return side === 'buccal'\n ? PERIODONTAL_BUCCAL_SITES\n : PERIODONTAL_LINGUAL_SITES;\n}\n\n/* -------------------------------------------------------------------- */\n/* Public value types */\n/* -------------------------------------------------------------------- */\n\n/** One probing-site reading. Every field is optional — a blank site. */\nexport interface PeriodontalSiteState {\n /** Probing pocket depth (PPD) in millimetres. */\n pocketDepthMm?: number;\n /**\n * Gingival margin / recession in millimetres. May be negative when the\n * margin sits coronal to the CEJ (i.e. gingival overgrowth).\n */\n recessionMm?: number;\n /** Bleeding on probing (BoP) at this site. */\n bleeding?: boolean;\n /** Visible plaque at this site. */\n plaque?: boolean;\n /** Suppuration (pus) at this site. */\n suppuration?: boolean;\n}\n\n/** Full editable state for one tooth. */\nexport interface PeriodontalToothState {\n /** Tooth is absent — hides its probing inputs. */\n missing?: boolean;\n /** Tooth is an implant. */\n implant?: boolean;\n /** Miller mobility grade (0–3). */\n mobility?: PeriodontalMobility;\n /** Free-text clinical note. Treated as PHI — never exposed to agents. */\n note?: string;\n /** Per-site probing readings, keyed by site id. */\n sites?: Partial<Record<PeriodontalSiteId, PeriodontalSiteState>>;\n /**\n * Furcation involvement per aspect. The array length matches the number\n * of anatomical furcation entry points for the tooth on that aspect (see\n * {@link PERIODONTAL_FURCATION_MAP}); each boolean flags one entry.\n */\n furcation?: Partial<Record<PeriodontalSide, boolean[]>>;\n}\n\n/** The whole-mouth editable value, keyed by FDI tooth number. */\nexport type PeriodontalChartValue = Record<string, PeriodontalToothState>;\n\n/* -------------------------------------------------------------------- */\n/* Clinical thresholds */\n/* -------------------------------------------------------------------- */\n\n/** Probing depth (mm) at/above which a site is flagged moderate. */\nexport const MODERATE_POCKET_MM = 4;\n/** Probing depth (mm) at/above which a site is flagged severe. */\nexport const SEVERE_POCKET_MM = 6;\n/** Plaque score (% of sites) above which the index reads elevated. */\nexport const PLAQUE_WARN_PERCENT = 20;\n/** Bleeding score (% of sites) above which the index reads elevated. */\nexport const BLEEDING_WARN_PERCENT = 5;\n\n/** Classify a probing depth into a severity band. */\nexport function pocketSeverity(\n depth: number | undefined,\n moderate = MODERATE_POCKET_MM,\n severe = SEVERE_POCKET_MM,\n): 'normal' | 'moderate' | 'severe' {\n if (depth == null || !Number.isFinite(depth)) return 'normal';\n if (depth >= severe) return 'severe';\n if (depth >= moderate) return 'moderate';\n return 'normal';\n}\n\n/* -------------------------------------------------------------------- */\n/* Furcation anatomy */\n/* */\n/* Number of furcation entry points per aspect. Transcribed verbatim from */\n/* the platform PeriodontalState::getFurcationMap() so the kit chart and */\n/* the legacy chart agree tooth-for-tooth. Teeth absent from the map are */\n/* single-rooted (no furcation) — {@link furcationSlots} returns 0. */\n/* -------------------------------------------------------------------- */\n\nexport const PERIODONTAL_FURCATION_MAP: Record<\n string,\n { buccal: number; lingual: number }\n> = {\n '18': { buccal: 1, lingual: 2 },\n '17': { buccal: 1, lingual: 2 },\n '16': { buccal: 1, lingual: 2 },\n '15': { buccal: 2, lingual: 2 },\n '14': { buccal: 0, lingual: 2 },\n '24': { buccal: 0, lingual: 2 },\n '25': { buccal: 2, lingual: 2 },\n '26': { buccal: 1, lingual: 2 },\n '27': { buccal: 1, lingual: 2 },\n '28': { buccal: 1, lingual: 2 },\n '48': { buccal: 1, lingual: 1 },\n '47': { buccal: 1, lingual: 1 },\n '46': { buccal: 1, lingual: 1 },\n '36': { buccal: 1, lingual: 1 },\n '37': { buccal: 1, lingual: 1 },\n '38': { buccal: 1, lingual: 1 },\n};\n\n/** Number of furcation entry points for a tooth on a given aspect (0–2). */\nexport function furcationSlots(fdi: string, side: PeriodontalSide): number {\n return PERIODONTAL_FURCATION_MAP[fdi]?.[side] ?? 0;\n}\n\n/* -------------------------------------------------------------------- */\n/* Tooth layout */\n/* -------------------------------------------------------------------- */\n\nexport type PeriodontalDentition = 'permanent' | 'primary';\n\n/** Upper then lower arch, each in clinical left-to-right order. */\nexport interface PeriodontalArches {\n upper: FdiId[];\n lower: FdiId[];\n}\n\n/** The 16 permanent teeth of the upper arch (18→11, 21→28). */\nexport const PERIODONTAL_PERMANENT_UPPER = PERMANENT_TEETH.slice(0, 16);\n/** The 16 permanent teeth of the lower arch (48→41, 31→38). */\nexport const PERIODONTAL_PERMANENT_LOWER = PERMANENT_TEETH.slice(16);\n/** The 10 primary teeth of the upper arch. */\nexport const PERIODONTAL_PRIMARY_UPPER = PRIMARY_TEETH.slice(0, 10);\n/** The 10 primary teeth of the lower arch. */\nexport const PERIODONTAL_PRIMARY_LOWER = PRIMARY_TEETH.slice(10);\n\n/** The upper + lower tooth ids for a dentition, in display order. */\nexport function periodontalArches(\n dentition: PeriodontalDentition,\n): PeriodontalArches {\n return dentition === 'primary'\n ? { upper: PERIODONTAL_PRIMARY_UPPER, lower: PERIODONTAL_PRIMARY_LOWER }\n : {\n upper: PERIODONTAL_PERMANENT_UPPER,\n lower: PERIODONTAL_PERMANENT_LOWER,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Numbering */\n/* -------------------------------------------------------------------- */\n\nexport type PeriodontalNumbering = 'fdi' | 'universal' | 'palmer';\n\n/**\n * The visible column label for a tooth. FDI is canonical; Universal (ADA)\n * and Palmer are label-only projections — the value model always keys by\n * FDI regardless of the chosen numbering.\n */\nexport function periodontalToothLabel(\n fdi: string,\n numbering: PeriodontalNumbering,\n): string {\n if (numbering === 'universal') return FDI_TO_UNIVERSAL[fdi] ?? fdi;\n if (numbering === 'palmer') return FDI_TO_PALMER[fdi] ?? fdi;\n return fdi;\n}\n\n/* -------------------------------------------------------------------- */\n/* Indices */\n/* -------------------------------------------------------------------- */\n\n/** Whole-mouth summary indices derived from a chart value. */\nexport interface PeriodontalIndices {\n /** Mean probing depth across scored sites, or null when none scored. */\n meanPocketDepthMm: number | null;\n /** Mean clinical attachment level (PPD + recession), or null. */\n meanAttachmentLevelMm: number | null;\n /** Plaque score as a percentage of scored sites (0–100), or null. */\n plaquePercent: number | null;\n /** Bleeding-on-probing as a percentage of scored sites (0–100), or null. */\n bleedingPercent: number | null;\n /** Number of sites carrying a probing depth (the scored denominator). */\n scoredSiteCount: number;\n /** Number of scored sites at/above the severe threshold. */\n deepPocketCount: number;\n}\n\n/** Round to one decimal place, guarding against `-0`. */\nfunction round1(n: number): number {\n const r = Math.round(n * 10) / 10;\n return r === 0 ? 0 : r;\n}\n\n/**\n * Derive the whole-mouth indices. A site is \"scored\" when it carries a\n * probing depth; percentages are taken over scored sites. Missing teeth\n * are skipped entirely.\n */\nexport function computePeriodontalIndices(\n value: PeriodontalChartValue,\n): PeriodontalIndices {\n let depthSum = 0;\n let attachmentSum = 0;\n let scored = 0;\n let bleeding = 0;\n let plaque = 0;\n let deep = 0;\n\n for (const tooth of Object.values(value)) {\n if (tooth.missing || !tooth.sites) continue;\n for (const site of Object.values(tooth.sites)) {\n if (!site) continue;\n const depth = site.pocketDepthMm;\n if (depth == null || !Number.isFinite(depth)) continue;\n scored += 1;\n depthSum += depth;\n attachmentSum += depth + (site.recessionMm ?? 0);\n if (depth >= SEVERE_POCKET_MM) deep += 1;\n if (site.bleeding) bleeding += 1;\n if (site.plaque) plaque += 1;\n }\n }\n\n if (scored === 0) {\n return {\n meanPocketDepthMm: null,\n meanAttachmentLevelMm: null,\n plaquePercent: null,\n bleedingPercent: null,\n scoredSiteCount: 0,\n deepPocketCount: 0,\n };\n }\n\n return {\n meanPocketDepthMm: round1(depthSum / scored),\n meanAttachmentLevelMm: round1(attachmentSum / scored),\n plaquePercent: round1((plaque / scored) * 100),\n bleedingPercent: round1((bleeding / scored) * 100),\n scoredSiteCount: scored,\n deepPocketCount: deep,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Findings */\n/* */\n/* A per-tooth \"problem list\" derived from a chart value — the shape an */\n/* agent narrates back to a clinician (\"46: 8 mm disto-buccal pocket, */\n/* bleeding, grade 2 mobility\"). PHI-free by construction: the free-text */\n/* note is never read, only structural readings + FDI ids appear. */\n/* -------------------------------------------------------------------- */\n\n/** Structural status worth narrating for a tooth. */\nexport type PeriodontalToothStatus = 'present' | 'missing' | 'implant';\n\n/** One flagged probing site within a tooth finding. */\nexport interface PeriodontalSiteFinding {\n site: PeriodontalSiteId;\n pocketDepthMm: number;\n severity: 'moderate' | 'severe';\n}\n\n/** A single tooth's notable findings — one narratable problem-list entry. */\nexport interface PeriodontalToothFinding {\n /** FDI tooth id. */\n fdi: string;\n /** Structural status. `'present'` teeth appear only when they carry a finding. */\n status: PeriodontalToothStatus;\n /** Worst severity band across the tooth's scored sites. */\n severity: 'normal' | 'moderate' | 'severe';\n /** Deepest probing depth on the tooth (mm), or null when unscored. */\n maxPocketDepthMm: number | null;\n /** Sites at/above the moderate threshold, deepest first. */\n pockets: PeriodontalSiteFinding[];\n /** Sites bleeding on probing. */\n bleedingSites: PeriodontalSiteId[];\n /** Sites with visible plaque. */\n plaqueSites: PeriodontalSiteId[];\n /** Sites with suppuration. */\n suppurationSites: PeriodontalSiteId[];\n /** Greatest *positive* recession on the tooth (mm); ≤ 0 values ignored. Null when none. */\n maxRecessionMm: number | null;\n /** Miller mobility grade when ≥ 1, else null. */\n mobility: PeriodontalMobility | null;\n /** Aspects with any furcation involvement flagged. */\n furcation: PeriodontalSide[];\n}\n\n/** Canonical clinical order (permanent then primary) for narrating findings. */\nconst FINDING_ORDER: readonly string[] = [...PERMANENT_TEETH, ...PRIMARY_TEETH];\n\n/**\n * Derive the narratable per-tooth problem list from a chart value. A tooth is\n * included only when it carries a notable finding — missing, implant, a pocket\n * at/above the moderate threshold, bleeding, suppuration, mobility ≥ 1, or\n * furcation involvement. Entries are returned in clinical (arch) order; each\n * tooth's `pockets` are ordered deepest first. Plaque and shallow recession are\n * reported as detail on already-flagged teeth but never flag a tooth alone.\n */\nexport function computePeriodontalFindings(\n value: PeriodontalChartValue,\n options?: { moderate?: number; severe?: number },\n): PeriodontalToothFinding[] {\n const moderate = options?.moderate ?? MODERATE_POCKET_MM;\n const severe = options?.severe ?? SEVERE_POCKET_MM;\n\n const rank = new Map<string, number>();\n FINDING_ORDER.forEach((fdi, i) => rank.set(fdi, i));\n const orderedFdis = Object.keys(value).sort(\n (a, b) =>\n (rank.get(a) ?? Number.MAX_SAFE_INTEGER) -\n (rank.get(b) ?? Number.MAX_SAFE_INTEGER),\n );\n\n const findings: PeriodontalToothFinding[] = [];\n\n for (const fdi of orderedFdis) {\n const tooth = value[fdi];\n if (!tooth) continue;\n\n // Missing teeth are a structural finding on their own.\n if (tooth.missing) {\n findings.push({\n fdi,\n status: 'missing',\n severity: 'normal',\n maxPocketDepthMm: null,\n pockets: [],\n bleedingSites: [],\n plaqueSites: [],\n suppurationSites: [],\n maxRecessionMm: null,\n mobility: null,\n furcation: [],\n });\n continue;\n }\n\n const status: PeriodontalToothStatus = tooth.implant\n ? 'implant'\n : 'present';\n const pockets: PeriodontalSiteFinding[] = [];\n const bleedingSites: PeriodontalSiteId[] = [];\n const plaqueSites: PeriodontalSiteId[] = [];\n const suppurationSites: PeriodontalSiteId[] = [];\n let maxPocketDepthMm: number | null = null;\n let maxRecessionMm: number | null = null;\n\n for (const site of PERIODONTAL_SITE_ORDER) {\n const reading = tooth.sites?.[site];\n if (!reading) continue;\n const { pocketDepthMm, recessionMm, bleeding, plaque, suppuration } =\n reading;\n if (pocketDepthMm != null && Number.isFinite(pocketDepthMm)) {\n maxPocketDepthMm =\n maxPocketDepthMm == null\n ? pocketDepthMm\n : Math.max(maxPocketDepthMm, pocketDepthMm);\n const sev = pocketSeverity(pocketDepthMm, moderate, severe);\n if (sev !== 'normal')\n pockets.push({ site, pocketDepthMm, severity: sev });\n }\n if (\n recessionMm != null &&\n Number.isFinite(recessionMm) &&\n recessionMm > 0\n ) {\n maxRecessionMm =\n maxRecessionMm == null\n ? recessionMm\n : Math.max(maxRecessionMm, recessionMm);\n }\n if (bleeding) bleedingSites.push(site);\n if (plaque) plaqueSites.push(site);\n if (suppuration) suppurationSites.push(site);\n }\n\n pockets.sort((a, b) => b.pocketDepthMm - a.pocketDepthMm);\n\n const mobility =\n tooth.mobility != null && tooth.mobility >= 1 ? tooth.mobility : null;\n\n const furcation: PeriodontalSide[] = [];\n if (tooth.furcation) {\n for (const side of ['buccal', 'lingual'] as const) {\n if (tooth.furcation[side]?.some(Boolean)) furcation.push(side);\n }\n }\n\n const severity: 'normal' | 'moderate' | 'severe' = pockets.some(\n (p) => p.severity === 'severe',\n )\n ? 'severe'\n : pockets.length > 0\n ? 'moderate'\n : 'normal';\n\n const notable =\n status === 'implant' ||\n pockets.length > 0 ||\n bleedingSites.length > 0 ||\n suppurationSites.length > 0 ||\n mobility != null ||\n furcation.length > 0;\n\n if (!notable) continue;\n\n findings.push({\n fdi,\n status,\n severity,\n maxPocketDepthMm,\n pockets,\n bleedingSites,\n plaqueSites,\n suppurationSites,\n maxRecessionMm,\n mobility,\n furcation,\n });\n }\n\n return findings;\n}\n\n/* -------------------------------------------------------------------- */\n/* Interop + construction */\n/* -------------------------------------------------------------------- */\n\n/** An empty chart with every tooth present but unscored. */\nexport function emptyPeriodontalChart(\n dentition: PeriodontalDentition = 'permanent',\n): PeriodontalChartValue {\n const { upper, lower } = periodontalArches(dentition);\n const out: PeriodontalChartValue = {};\n for (const fdi of [...upper, ...lower]) out[fdi] = {};\n return out;\n}\n\n/**\n * Flatten a chart value into the display-only card's `PeriodontalSite[]`\n * so a captured chart can be handed straight to `PeriodontalChartCard`.\n */\nexport function toPeriodontalSiteList(\n value: PeriodontalChartValue,\n): PeriodontalSite[] {\n const out: PeriodontalSite[] = [];\n for (const [tooth, state] of Object.entries(value)) {\n if (state.missing || !state.sites) continue;\n for (const site of PERIODONTAL_SITE_ORDER) {\n const reading = state.sites[site];\n if (!reading) continue;\n const { pocketDepthMm, recessionMm, bleeding, suppuration } = reading;\n if (\n pocketDepthMm == null &&\n recessionMm == null &&\n !bleeding &&\n !suppuration\n ) {\n continue;\n }\n out.push({\n tooth,\n site,\n pocketDepthMm,\n recessionMm,\n bleeding,\n suppuration,\n });\n }\n }\n return out;\n}\n\n/** Per-tooth mobility map (FDI → grade) for handing to the card. */\nexport function toMobilityMap(\n value: PeriodontalChartValue,\n): Record<string, PeriodontalMobility> {\n const out: Record<string, PeriodontalMobility> = {};\n for (const [tooth, state] of Object.entries(value)) {\n if (state.mobility != null) out[tooth] = state.mobility;\n }\n return out;\n}\n","import {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { FDI_TO_META, toothImageUrl } from '../tooth-scheme/tooth-data';\nimport { periodontalChartAgent } from './periodontal-chart.agent';\nimport {\n computePeriodontalFindings,\n computePeriodontalIndices,\n furcationSlots,\n periodontalArches,\n periodontalToothLabel,\n pocketSeverity,\n sitesForSide,\n BLEEDING_WARN_PERCENT,\n MODERATE_POCKET_MM,\n PERIODONTAL_BUCCAL_SITES,\n PLAQUE_WARN_PERCENT,\n SEVERE_POCKET_MM,\n type FdiId,\n type PeriodontalChartValue,\n type PeriodontalDentition,\n type PeriodontalIndices,\n type PeriodontalMobility,\n type PeriodontalNumbering,\n type PeriodontalSide,\n type PeriodontalSiteId,\n type PeriodontalSiteState,\n type PeriodontalToothFinding,\n type PeriodontalToothState,\n} from './periodontal-chart-data';\n\n/* -------------------------------------------------------------------- */\n/* PeriodontalChart */\n/* */\n/* The editable full-mouth periodontal chart — the perio counterpart of */\n/* ToothScheme. It records six probing sites per tooth (buccal + lingual */\n/* × mesial/mid/distal): pocket depth, recession, bleeding, plaque, plus */\n/* per-tooth mobility / missing / implant / furcation / note. It renders */\n/* one real <table> per arch (semantic <th scope> headers + caption — the */\n/* accessible source of truth), a decorative depth/recession curve per */\n/* arch, and a whole-mouth index strip (mean PD / CAL, plaque %, BoP %). */\n/* */\n/* The value keys by FDI and always emits FDI, so it interchanges */\n/* losslessly with ToothScheme, PeriodontalChartCard, and the platform */\n/* chart. Every visible string flows through `useTranslation()` (default */\n/* namespace `ui`) under the `periodontalChart.*` keys. */\n/* */\n/* Two conscious deviations, both driven by density (32 teeth × 6 sites): */\n/* - Native <input>/<select>/<label> rather than the kit Checkbox/Select */\n/* Radix primitives — a perio chart is hundreds of tiny inline cells; */\n/* the kit primitives carry chrome the wrong shape/weight here. Cells */\n/* stay tokenised and explicitly labelled. */\n/* - Interactive targets are 24px (the WCAG 2.5.8 AA floor) not the kit's */\n/* 44/48px --min-target-size; full-size targets would make the grid */\n/* unusably large. Documented dense-data-grid exception. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n 'ds:w-full ds:flex ds:flex-col ds:gap-[var(--spacing-lg)] ds:text-[color:var(--foreground)]',\n {\n variants: {\n // Disabled is conveyed by the inputs' own `disabled` state (native\n // disabled controls are excluded from the axe contrast check). We must\n // NOT dim the whole root with opacity — that would drop the legend,\n // captions and headers below the AA contrast floor.\n disabled: { true: 'ds:cursor-not-allowed', false: '' },\n },\n defaultVariants: { disabled: false },\n },\n);\n\n// Numeric probing-depth / recession input. Depth severity is paired with a\n// weight bump and a border shift so the flag survives greyscale /\n// forced-colors — colour is never the sole cue (an sr-only worded suffix\n// carries the same signal for screen-reader users).\nconst numericInputVariants = cva(\n 'ds:h-6 ds:w-[3.5ch] ds:rounded-[var(--radius-sm)] ds:border ds:bg-[var(--background)] ds:text-center ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n {\n variants: {\n severity: {\n normal:\n 'ds:border-[color:var(--input)] ds:text-[color:var(--foreground)]',\n moderate:\n 'ds:border-[color:var(--warning)] ds:font-semibold ds:text-[color:var(--warning-foreground)]',\n severe:\n 'ds:border-[color:var(--destructive)] ds:font-bold ds:text-[color:var(--destructive)]',\n },\n },\n defaultVariants: { severity: 'normal' },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Imperative handle — structural state + guarded edits, never PHI notes. */\nexport interface PeriodontalChartHandle {\n /** The current whole-mouth value. */\n getValue: () => PeriodontalChartValue;\n /** The recorded state of one tooth, if any. */\n getTooth: (fdi: string) => PeriodontalToothState | undefined;\n /** Derived whole-mouth indices (mean PD/CAL, plaque %, BoP %). */\n getIndices: () => PeriodontalIndices;\n /** Narratable per-tooth problem list — flagged teeth only, never the note. */\n getFindings: () => PeriodontalToothFinding[];\n /** FDI ids of every tooth flagged missing. */\n missingTeeth: () => string[];\n /** Patch one probing site. */\n setSite: (\n fdi: string,\n site: PeriodontalSiteId,\n patch: Partial<PeriodontalSiteState>,\n ) => void;\n /** Patch tooth-level state (mobility / missing / implant / note). */\n setTooth: (fdi: string, patch: Partial<PeriodontalToothState>) => void;\n /** Clear every reading from a tooth. */\n clearTooth: (fdi: string) => void;\n /** Focus a specific measurement input. */\n focusSite: (\n fdi: string,\n site: PeriodontalSiteId,\n field: NumericField,\n ) => void;\n}\n\n/** The two numeric per-site measurements. */\ntype NumericField = 'pocketDepthMm' | 'recessionMm';\n/** The two per-site boolean flags rendered as toggles. */\ntype ToggleField = 'bleeding' | 'plaque';\n\nexport interface PeriodontalChartProps extends Omit<\n ComponentPropsWithoutRef<'div'>,\n 'aria-label' | 'children' | 'onChange' | 'defaultValue'\n> {\n /** Controlled value — keyed by FDI tooth number. */\n value?: PeriodontalChartValue;\n /** Uncontrolled initial value. */\n defaultValue?: PeriodontalChartValue;\n /** Fired with the full next value whenever any reading changes. */\n onChange?: (value: PeriodontalChartValue) => void;\n\n /** `'interactive'` (default) renders inputs; `'display'` renders read-only. */\n mode?: 'interactive' | 'display';\n /** Disable every input while keeping the chart interactive-styled. */\n disabled?: boolean;\n\n /** Which dentition to lay out. Drives the tooth columns. */\n dentition?: PeriodontalDentition;\n /** Tooth-number system for the column labels (value always keys FDI). */\n numbering?: PeriodontalNumbering;\n\n /** Show a photoreal tooth graphic per column. Default `true`. */\n showTeeth?: boolean;\n /**\n * Base URL for the photoreal tooth images, matching ToothScheme's tree —\n * `${assetBaseUrl}/side/{default|implant}/{top|bottom}/{fdi}.webp`. Defaults\n * to the kit's bundled static path.\n */\n assetBaseUrl?: string;\n /** Show the decorative per-arch depth/recession curve. Default `true`. */\n showCurves?: boolean;\n /** Show the whole-mouth index strip. Default `true`. */\n showIndices?: boolean;\n\n /** Probing depth (mm) at/above which a site flags moderate. Default 4. */\n moderatePocketMm?: number;\n /** Probing depth (mm) at/above which a site flags severe. Default 6. */\n severePocketMm?: number;\n\n /** Locale for number formatting. Falls back to `i18n.language`. */\n locale?: string;\n\n /** Override the default group aria-label. */\n 'aria-label'?: string;\n /** Agent-readiness consumer id, surfaced as `data-component-id`. */\n id?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\n/** Immutably patch one tooth of a chart value. */\nfunction patchTooth(\n value: PeriodontalChartValue,\n fdi: string,\n patch: Partial<PeriodontalToothState>,\n): PeriodontalChartValue {\n return { ...value, [fdi]: { ...value[fdi], ...patch } };\n}\n\n/** Immutably patch one site of one tooth. */\nfunction patchSite(\n value: PeriodontalChartValue,\n fdi: string,\n site: PeriodontalSiteId,\n patch: Partial<PeriodontalSiteState>,\n): PeriodontalChartValue {\n const tooth = value[fdi] ?? {};\n const sites = tooth.sites ?? {};\n return {\n ...value,\n [fdi]: {\n ...tooth,\n sites: { ...sites, [site]: { ...sites[site], ...patch } },\n },\n };\n}\n\n/** Read-only worded severity suffix for screen readers. */\nfunction severityTextClass(severity: 'normal' | 'moderate' | 'severe'): string {\n if (severity === 'severe')\n return 'ds:font-bold ds:text-[color:var(--destructive)]';\n if (severity === 'moderate')\n return 'ds:font-semibold ds:text-[color:var(--warning-foreground)]';\n return '';\n}\n\n/** English fallback labels for the six probing sites (belt-and-braces). */\nconst SITE_DEFAULT: Record<PeriodontalSiteId, string> = {\n db: 'disto-buccal',\n b: 'buccal',\n mb: 'mesio-buccal',\n dl: 'disto-lingual',\n l: 'lingual',\n ml: 'mesio-lingual',\n};\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const PeriodontalChart = forwardRef<\n PeriodontalChartHandle,\n PeriodontalChartProps\n>(\n (\n {\n value,\n defaultValue,\n onChange,\n mode = 'interactive',\n disabled = false,\n dentition = 'permanent',\n numbering = 'fdi',\n showTeeth = true,\n assetBaseUrl = '/toothscheme/teeth',\n showCurves = true,\n showIndices = true,\n moderatePocketMm = MODERATE_POCKET_MM,\n severePocketMm = SEVERE_POCKET_MM,\n locale,\n 'aria-label': ariaLabel,\n id,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const resolvedLocale = locale ?? i18n.language ?? 'en';\n const rootRef = useRef<HTMLDivElement>(null);\n\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<PeriodontalChartValue>(\n () => defaultValue ?? {},\n );\n const resolved = isControlled ? value : internal;\n const readOnly = mode === 'display';\n\n // Latest-value ref so the imperative + agent handles read fresh state\n // without being rebuilt on every keystroke. Re-synced from `resolved` each\n // render, and — crucially — advanced inside `commit` so two edits in the\n // same tick (an agent batch, or two handle calls) compose instead of the\n // second read clobbering the first (last-write-wins).\n const valueRef = useRef(resolved);\n valueRef.current = resolved;\n\n const commit = useCallback(\n (next: PeriodontalChartValue) => {\n valueRef.current = next;\n if (!isControlled) setInternal(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n const setSite = useCallback(\n (\n fdi: string,\n site: PeriodontalSiteId,\n patch: Partial<PeriodontalSiteState>,\n ) => commit(patchSite(valueRef.current, fdi, site, patch)),\n [commit],\n );\n const setTooth = useCallback(\n (fdi: string, patch: Partial<PeriodontalToothState>) =>\n commit(patchTooth(valueRef.current, fdi, patch)),\n [commit],\n );\n const clearTooth = useCallback(\n (fdi: string) => {\n const next = { ...valueRef.current };\n delete next[fdi];\n commit(next);\n },\n [commit],\n );\n const focusSite = useCallback(\n (fdi: string, site: PeriodontalSiteId, field: NumericField) => {\n try {\n rootRef.current\n ?.querySelector<HTMLInputElement>(\n `[data-perio-input][data-fdi=\"${fdi}\"][data-site=\"${site}\"][data-field=\"${field}\"]`,\n )\n ?.focus();\n } catch {\n // A malformed agent-supplied id must not throw an uncaught SyntaxError.\n }\n },\n [],\n );\n\n const indices = useMemo(\n () => computePeriodontalIndices(resolved),\n [resolved],\n );\n\n const handle = useMemo<PeriodontalChartHandle>(\n () => ({\n getValue: () => valueRef.current,\n getTooth: (fdi) => valueRef.current[fdi],\n getIndices: () => computePeriodontalIndices(valueRef.current),\n getFindings: () =>\n computePeriodontalFindings(valueRef.current, {\n moderate: moderatePocketMm,\n severe: severePocketMm,\n }),\n missingTeeth: () =>\n Object.entries(valueRef.current)\n .filter(([, s]) => s.missing)\n .map(([fdi]) => fdi),\n setSite,\n setTooth,\n clearTooth,\n focusSite,\n }),\n [\n setSite,\n setTooth,\n clearTooth,\n focusSite,\n moderatePocketMm,\n severePocketMm,\n ],\n );\n\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(periodontalChartAgent, handle, id);\n\n const arches = periodontalArches(dentition);\n const resolvedAriaLabel =\n ariaLabel ?? t('periodontalChart.ariaLabel', 'Periodontal chart');\n\n const formatNumber = useCallback(\n (n: number): string => {\n try {\n return new Intl.NumberFormat(resolvedLocale, {\n maximumFractionDigits: 1,\n }).format(n);\n } catch {\n return String(n);\n }\n },\n [resolvedLocale],\n );\n\n const siteLabel = useCallback(\n (site: PeriodontalSiteId): string =>\n t(`periodontalChart.site.${site}`, {\n defaultValue: SITE_DEFAULT[site],\n }),\n [t],\n );\n\n // The tooth identifier announced to screen readers follows the DISPLAYED\n // numbering, so the audible label matches the visible column header (data-fdi\n // stays FDI for interchange). For the default `fdi` numbering this is a no-op.\n const toothName = useCallback(\n (fdi: string): string => periodontalToothLabel(fdi, numbering),\n [numbering],\n );\n\n const dash = (\n <span\n aria-hidden=\"true\"\n className=\"ds:text-[color:var(--muted-foreground)]\"\n >\n {t('periodontalChart.cell.empty', '–')}\n </span>\n );\n\n /* ---------------------------------------------------------------- */\n /* Keyboard navigation between numeric inputs */\n /* ---------------------------------------------------------------- */\n\n const onKeyDown = useCallback((e: ReactKeyboardEvent<HTMLDivElement>) => {\n const el = e.target as HTMLElement;\n if (!el.matches?.('[data-perio-input]')) return;\n const key = e.key;\n if (\n key !== 'ArrowLeft' &&\n key !== 'ArrowRight' &&\n key !== 'ArrowUp' &&\n key !== 'ArrowDown'\n ) {\n return;\n }\n e.preventDefault();\n const root = rootRef.current;\n if (!root) return;\n\n if (key === 'ArrowUp' || key === 'ArrowDown') {\n // Flip between the pocket-depth and recession input of the same site.\n const fdi = el.getAttribute('data-fdi');\n const site = el.getAttribute('data-site');\n const field = el.getAttribute('data-field');\n const other =\n field === 'pocketDepthMm' ? 'recessionMm' : 'pocketDepthMm';\n const target = root.querySelector<HTMLInputElement>(\n `[data-perio-input][data-fdi=\"${fdi}\"][data-site=\"${site}\"][data-field=\"${other}\"]`,\n );\n target?.focus();\n target?.select();\n return;\n }\n\n // Left / right walk the numeric inputs in document order.\n const inputs = Array.from(\n root.querySelectorAll<HTMLInputElement>('[data-perio-input]'),\n );\n const idx = inputs.indexOf(el as HTMLInputElement);\n if (idx === -1) return;\n const delta = key === 'ArrowRight' ? 1 : -1;\n const target = inputs[idx + delta];\n target?.focus();\n target?.select();\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Cell renderers */\n /* ---------------------------------------------------------------- */\n\n const cellClass =\n 'ds:px-[var(--spacing-2xs)] ds:py-[var(--spacing-2xs)] ds:text-center ds:border-b ds:border-[color:var(--border)]';\n const rowHeaderClass =\n 'ds:px-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)] ds:text-start ds:font-medium ds:border-b ds:border-[color:var(--border)] ds:whitespace-nowrap';\n\n const renderNumericInput = (\n fdi: string,\n site: PeriodontalSiteId,\n field: NumericField,\n ): ReactNode => {\n const raw = resolved[fdi]?.sites?.[site]?.[field];\n const severity =\n field === 'pocketDepthMm'\n ? pocketSeverity(raw, moderatePocketMm, severePocketMm)\n : 'normal';\n const label = t(`periodontalChart.input.${field}`, {\n tooth: toothName(fdi),\n site: siteLabel(site),\n defaultValue:\n field === 'pocketDepthMm'\n ? 'Tooth {{tooth}} {{site}} pocket depth, millimetres'\n : 'Tooth {{tooth}} {{site}} recession, millimetres',\n });\n\n // Worded severity cue announced to screen readers in BOTH display and\n // edit modes (the visual cue is colour + font-weight + border — never\n // colour alone).\n const severityWord =\n severity === 'severe'\n ? t('periodontalChart.flag.severe', 'severe pocket')\n : severity === 'moderate'\n ? t('periodontalChart.flag.moderate', 'moderate pocket')\n : '';\n\n if (readOnly) {\n if (raw == null || !Number.isFinite(raw)) {\n return <span className=\"ds:inline-block ds:w-[3.5ch]\">{dash}</span>;\n }\n return (\n <span\n className={`ds:inline-block ds:w-[3.5ch] ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ${severityTextClass(\n severity,\n )}`}\n >\n <span>{formatNumber(raw)}</span>\n {severityWord ? (\n <span className=\"ds:sr-only\"> {severityWord}</span>\n ) : null}\n </span>\n );\n }\n\n return (\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n disabled={disabled}\n aria-label={severityWord ? `${label}, ${severityWord}` : label}\n data-perio-input=\"\"\n data-fdi={fdi}\n data-site={site}\n data-field={field}\n value={raw == null ? '' : String(raw)}\n onFocus={(e) => e.currentTarget.select()}\n onChange={(e) => {\n const text = e.target.value.trim();\n let next: number | undefined;\n if (text === '' || text === '-') {\n next = undefined;\n } else {\n const n = Number(text);\n if (!Number.isFinite(n)) return;\n // Depth is non-negative; recession may be negative (overgrowth).\n next = field === 'pocketDepthMm' ? Math.max(0, n) : n;\n }\n setSite(\n fdi,\n site,\n field === 'pocketDepthMm'\n ? { pocketDepthMm: next }\n : { recessionMm: next },\n );\n }}\n className={numericInputVariants({ severity })}\n />\n );\n };\n\n const renderToggle = (\n fdi: string,\n site: PeriodontalSiteId,\n field: ToggleField,\n ): ReactNode => {\n const checked = !!resolved[fdi]?.sites?.[site]?.[field];\n const label = t(`periodontalChart.toggle.${field}`, {\n tooth: toothName(fdi),\n site: siteLabel(site),\n defaultValue:\n field === 'bleeding'\n ? 'Tooth {{tooth}} {{site}} bleeding on probing'\n : 'Tooth {{tooth}} {{site}} plaque',\n });\n const accent =\n field === 'bleeding'\n ? 'ds:accent-[var(--destructive)]'\n : 'ds:accent-[var(--warning)]';\n\n if (readOnly) {\n if (!checked)\n return <span className=\"ds:inline-block ds:size-6\">{dash}</span>;\n return (\n <span\n role=\"img\"\n aria-label={label}\n className={`ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${\n field === 'bleeding'\n ? 'ds:bg-[var(--destructive)]'\n : 'ds:bg-[var(--warning)]'\n }`}\n />\n );\n }\n return (\n <label className=\"ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center\">\n <span className=\"ds:sr-only\">{label}</span>\n <input\n type=\"checkbox\"\n disabled={disabled}\n checked={checked}\n onChange={(e) =>\n setSite(\n fdi,\n site,\n field === 'bleeding'\n ? { bleeding: e.target.checked }\n : { plaque: e.target.checked },\n )\n }\n className={`ds:size-4 ${accent} ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50`}\n />\n </label>\n );\n };\n\n const renderFurcationCell = (\n fdi: string,\n side: PeriodontalSide,\n ): ReactNode => {\n const tooth = resolved[fdi];\n const slots = furcationSlots(fdi, side);\n if (tooth?.missing || slots === 0) return dash;\n const flags = tooth?.furcation?.[side] ?? [];\n return (\n <span className=\"ds:inline-flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]\">\n {Array.from({ length: slots }, (_, i) => {\n const checked = !!flags[i];\n const label = t('periodontalChart.toggle.furcation', {\n tooth: toothName(fdi),\n side: t(`periodontalChart.side.${side}`, {\n defaultValue: side === 'buccal' ? 'buccal' : 'lingual',\n }),\n n: i + 1,\n defaultValue: 'Tooth {{tooth}} {{side}} furcation {{n}}',\n });\n if (readOnly) {\n return checked ? (\n <span\n key={i}\n role=\"img\"\n aria-label={label}\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--primary)]\"\n />\n ) : (\n <span\n key={i}\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:border ds:border-[color:var(--border)]\"\n />\n );\n }\n return (\n <label\n key={i}\n className=\"ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center\"\n >\n <span className=\"ds:sr-only\">{label}</span>\n <input\n type=\"checkbox\"\n disabled={disabled}\n checked={checked}\n onChange={(e) => {\n const next = [...(tooth?.furcation?.[side] ?? [])];\n while (next.length < slots) next.push(false);\n next[i] = e.target.checked;\n setTooth(fdi, {\n furcation: { ...tooth?.furcation, [side]: next },\n });\n }}\n className=\"ds:size-4 ds:accent-[var(--primary)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n />\n </label>\n );\n })}\n </span>\n );\n };\n\n /* One measurement row (PD or REC) across an arch, for one aspect. */\n const renderNumericRow = (\n teeth: FdiId[],\n labelKey: string,\n defaultLabel: string,\n side: PeriodontalSide,\n field: NumericField,\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t(labelKey, defaultLabel)}\n </th>\n {teeth.map((fdi) => (\n <td key={fdi} className={cellClass}>\n {resolved[fdi]?.missing ? (\n dash\n ) : (\n <span className=\"ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]\">\n {sitesForSide(side).map((site) => (\n <span key={site}>{renderNumericInput(fdi, site, field)}</span>\n ))}\n </span>\n )}\n </td>\n ))}\n </tr>\n );\n\n /* One toggle row (BOP or plaque) across an arch, for one aspect. */\n const renderToggleRow = (\n teeth: FdiId[],\n labelKey: string,\n defaultLabel: string,\n side: PeriodontalSide,\n field: ToggleField,\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t(labelKey, defaultLabel)}\n </th>\n {teeth.map((fdi) => (\n <td key={fdi} className={cellClass}>\n {resolved[fdi]?.missing ? (\n dash\n ) : (\n <span className=\"ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]\">\n {sitesForSide(side).map((site) => (\n <span key={site}>{renderToggle(fdi, site, field)}</span>\n ))}\n </span>\n )}\n </td>\n ))}\n </tr>\n );\n\n const renderFurcationRow = (\n teeth: FdiId[],\n side: PeriodontalSide,\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.furcation', 'Furcation')}\n </th>\n {teeth.map((fdi) => (\n <td key={fdi} className={cellClass}>\n {renderFurcationCell(fdi, side)}\n </td>\n ))}\n </tr>\n );\n\n const renderFlagRow = (\n teeth: FdiId[],\n labelKey: string,\n defaultLabel: string,\n field: 'missing' | 'implant',\n ): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t(labelKey, defaultLabel)}\n </th>\n {teeth.map((fdi) => {\n // Implant is meaningless on an absent tooth; missing is always\n // togglable so a tooth can be marked present/absent.\n if (field === 'implant' && resolved[fdi]?.missing) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const checked = !!resolved[fdi]?.[field];\n const label = t(`periodontalChart.flag.${field}Label`, {\n tooth: toothName(fdi),\n defaultValue:\n field === 'missing'\n ? 'Tooth {{tooth}} missing'\n : 'Tooth {{tooth}} implant',\n });\n return (\n <td key={fdi} className={cellClass}>\n {readOnly ? (\n checked ? (\n <span\n role=\"img\"\n aria-label={label}\n className=\"type-meta ds:font-semibold\"\n >\n {field === 'missing'\n ? t('periodontalChart.flag.missingShort', '×')\n : t('periodontalChart.flag.implantShort', 'I')}\n </span>\n ) : (\n dash\n )\n ) : (\n <label className=\"ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center\">\n <span className=\"ds:sr-only\">{label}</span>\n <input\n type=\"checkbox\"\n disabled={disabled}\n checked={checked}\n onChange={(e) =>\n setTooth(\n fdi,\n field === 'missing'\n ? { missing: e.target.checked }\n : { implant: e.target.checked },\n )\n }\n className=\"ds:size-4 ds:accent-[var(--primary)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n />\n </label>\n )}\n </td>\n );\n })}\n </tr>\n );\n\n const renderMobilityRow = (teeth: FdiId[]): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.mobility', 'Mobility')}\n </th>\n {teeth.map((fdi) => {\n if (resolved[fdi]?.missing) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const grade = resolved[fdi]?.mobility;\n const label = t('periodontalChart.mobility.label', {\n tooth: toothName(fdi),\n defaultValue: 'Tooth {{tooth}} mobility grade',\n });\n if (readOnly) {\n return (\n <td key={fdi} className={cellClass}>\n {grade == null ? (\n dash\n ) : (\n <span role=\"img\" aria-label={label} className=\"type-meta\">\n {grade}\n </span>\n )}\n </td>\n );\n }\n return (\n <td key={fdi} className={cellClass}>\n <select\n disabled={disabled}\n aria-label={label}\n value={grade == null ? '' : String(grade)}\n onChange={(e) =>\n setTooth(fdi, {\n mobility:\n e.target.value === ''\n ? undefined\n : (Number(e.target.value) as PeriodontalMobility),\n })\n }\n className=\"ds:h-6 ds:rounded-[var(--radius-sm)] ds:border ds:border-[color:var(--input)] ds:bg-[var(--background)] ds:text-[color:var(--foreground)] ds:text-center ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n >\n <option value=\"\">\n {t('periodontalChart.mobility.none', '–')}\n </option>\n <option value=\"0\">0</option>\n <option value=\"1\">1</option>\n <option value=\"2\">2</option>\n <option value=\"3\">3</option>\n </select>\n </td>\n );\n })}\n </tr>\n );\n\n const renderNoteRow = (teeth: FdiId[]): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.note', 'Note')}\n </th>\n {teeth.map((fdi) => {\n if (resolved[fdi]?.missing) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const note = resolved[fdi]?.note ?? '';\n const label = t('periodontalChart.note.label', {\n tooth: toothName(fdi),\n defaultValue: 'Tooth {{tooth}} note',\n });\n if (readOnly) {\n return (\n <td key={fdi} className={`${cellClass} ds:text-start`}>\n {note ? <span className=\"type-meta\">{note}</span> : dash}\n </td>\n );\n }\n return (\n <td key={fdi} className={cellClass}>\n <input\n type=\"text\"\n disabled={disabled}\n aria-label={label}\n value={note}\n onChange={(e) => setTooth(fdi, { note: e.target.value })}\n className=\"ds:h-6 ds:w-[6ch] ds:rounded-[var(--radius-sm)] ds:border ds:border-[color:var(--input)] ds:bg-[var(--background)] ds:text-[color:var(--foreground)] ds:px-[var(--spacing-2xs)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50\"\n />\n </td>\n );\n })}\n </tr>\n );\n\n /* One row of photoreal tooth graphics for an arch (facial / side view),\n reusing ToothScheme's asset tree so the odontogram and perio chart show\n the same teeth. Decorative — the FDI column header names each tooth. */\n const renderToothRow = (teeth: FdiId[]): ReactNode => (\n <tr>\n <th scope=\"row\" className={rowHeaderClass}>\n {t('periodontalChart.row.tooth', 'Tooth')}\n </th>\n {teeth.map((fdi) => {\n const tooth = resolved[fdi];\n const meta = FDI_TO_META[fdi];\n if (tooth?.missing || !meta) {\n return (\n <td key={fdi} className={cellClass}>\n {dash}\n </td>\n );\n }\n const symbol = tooth?.implant ? 'implant' : 'default';\n return (\n <td key={fdi} className={cellClass}>\n <img\n src={toothImageUrl(meta, assetBaseUrl, symbol, 'side')}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"lazy\"\n className=\"ds:mx-auto ds:h-12 ds:w-auto ds:max-w-full ds:object-contain\"\n />\n </td>\n );\n })}\n </tr>\n );\n\n /* ---------------------------------------------------------------- */\n /* Decorative depth / recession curve (buccal aspect) */\n /* ---------------------------------------------------------------- */\n\n const renderCurve = (teeth: FdiId[]): ReactNode => {\n const SCALE = 6; // px per mm\n const STEP = 24; // px between sites\n const REC_MM = 5; // recession region above the gum line\n const DEP_MM = 13; // pocket-depth region below the gum line\n const MID = 8 + REC_MM * SCALE; // y of the 0 mm gingival-margin line\n const HEIGHT = MID + DEP_MM * SCALE + 8;\n const depthY = (mm: number) =>\n MID + Math.min(Math.max(0, mm), DEP_MM) * SCALE;\n const recY = (mm: number) =>\n MID - Math.min(Math.max(0, mm), REC_MM) * SCALE;\n\n // Collect readings as CONTIGUOUS runs — a gap (missing tooth or an\n // unrecorded site) breaks the run so the curve never bridges absent data.\n type Pt = { x: number; y: number };\n const depthRuns: Pt[][] = [];\n const marginRuns: Pt[][] = [];\n let depthRun: Pt[] = [];\n let marginRun: Pt[] = [];\n const flush = () => {\n if (depthRun.length) depthRuns.push(depthRun);\n if (marginRun.length) marginRuns.push(marginRun);\n depthRun = [];\n marginRun = [];\n };\n let x = STEP / 2;\n for (const fdi of teeth) {\n const missing = resolved[fdi]?.missing;\n for (const site of PERIODONTAL_BUCCAL_SITES) {\n const reading = missing ? undefined : resolved[fdi]?.sites?.[site];\n const pd = reading?.pocketDepthMm;\n const rec = reading?.recessionMm;\n if (pd != null && Number.isFinite(pd))\n depthRun.push({ x, y: depthY(pd) });\n else if (depthRun.length) {\n depthRuns.push(depthRun);\n depthRun = [];\n }\n if (rec != null && Number.isFinite(rec))\n marginRun.push({ x, y: recY(rec) });\n else if (marginRun.length) {\n marginRuns.push(marginRun);\n marginRun = [];\n }\n x += STEP;\n }\n }\n flush();\n\n const width = teeth.length * PERIODONTAL_BUCCAL_SITES.length * STEP;\n const toPts = (a: Pt[]) => a.map((p) => `${p.x},${p.y}`).join(' ');\n const depthPts = depthRuns.flat();\n const marginPts = marginRuns.flat();\n\n return (\n <svg\n viewBox={`0 0 ${width} ${HEIGHT}`}\n className=\"ds:h-[132px] ds:w-full\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {/* Depth gridlines (dashed) below the gum line */}\n {[3, 6, 9, 12].map((mm) => (\n <line\n key={mm}\n x1={0}\n x2={width}\n y1={depthY(mm)}\n y2={depthY(mm)}\n stroke=\"var(--border)\"\n strokeWidth={1}\n strokeDasharray=\"2 4\"\n />\n ))}\n {/* The 0 mm gingival-margin reference line (solid) */}\n <line\n x1={0}\n x2={width}\n y1={MID}\n y2={MID}\n stroke=\"var(--border)\"\n strokeWidth={1.5}\n />\n {/* One filled area + line per contiguous depth run */}\n {depthRuns.map((run, i) =>\n run.length >= 2 ? (\n <polygon\n key={`fill-${i}`}\n points={`${run[0].x},${MID} ${toPts(run)} ${run[run.length - 1].x},${MID}`}\n fill=\"var(--success)\"\n fillOpacity={0.14}\n />\n ) : null,\n )}\n {depthRuns.map((run, i) =>\n run.length >= 2 ? (\n <polyline\n key={`depth-${i}`}\n points={toPts(run)}\n fill=\"none\"\n stroke=\"var(--success)\"\n strokeWidth={2.5}\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n ) : null,\n )}\n {marginRuns.map((run, i) =>\n run.length >= 2 ? (\n <polyline\n key={`margin-${i}`}\n points={toPts(run)}\n fill=\"none\"\n stroke=\"var(--accent)\"\n strokeWidth={2.5}\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n ) : null,\n )}\n {/* Data points — so isolated readings (single-site runs) still show */}\n {depthPts.map((p, i) => (\n <circle\n key={`dp-${i}`}\n cx={p.x}\n cy={p.y}\n r={2}\n fill=\"var(--success)\"\n />\n ))}\n {marginPts.map((p, i) => (\n <circle\n key={`mp-${i}`}\n cx={p.x}\n cy={p.y}\n r={2}\n fill=\"var(--accent)\"\n />\n ))}\n </svg>\n );\n };\n\n /* ---------------------------------------------------------------- */\n /* One arch */\n /* ---------------------------------------------------------------- */\n\n const renderArch = (\n teeth: FdiId[],\n captionKey: string,\n defaultCaption: string,\n ): ReactNode => (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {/* dir=\"ltr\" pins the tooth-column order to clinical convention\n regardless of layout direction — mirroring the arch in RTL would\n swap the patient's left and right teeth (a patient-safety hazard),\n the same fixed-orientation rule ToothScheme follows. It also keeps\n the ← → arrow-key nav aligned with the visual column order. */}\n <div\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- keyboard-focusable horizontal scroll region; the data table is the accessible source of truth\n tabIndex={0}\n role=\"group\"\n dir=\"ltr\"\n aria-label={t(captionKey, defaultCaption)}\n className=\"ds:overflow-x-auto ds:rounded-[var(--radius-sm)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]\"\n >\n <table className=\"type-body-sm ds:w-full ds:border-collapse\">\n <caption className=\"type-label ds:text-start ds:mb-[var(--spacing-2xs)] ds:text-[color:var(--muted-foreground)]\">\n {t(captionKey, defaultCaption)}\n </caption>\n <thead>\n <tr>\n <th\n scope=\"col\"\n className=\"ds:px-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)] ds:text-start ds:border-b ds:border-[color:var(--border)]\"\n >\n {t('periodontalChart.column.measure', 'Measurement')}\n </th>\n {teeth.map((fdi) => (\n <th\n key={fdi}\n scope=\"col\"\n data-fdi={fdi}\n className=\"ds:px-[var(--spacing-2xs)] ds:py-[var(--spacing-2xs)] ds:text-center ds:border-b ds:border-[color:var(--border)] ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)]\"\n >\n <span className=\"ds:sr-only\">\n {t('periodontalChart.toothHeader', {\n tooth: toothName(fdi),\n defaultValue: 'Tooth {{tooth}}',\n })}\n </span>\n <span aria-hidden=\"true\">\n {periodontalToothLabel(fdi, numbering)}\n </span>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {renderFlagRow(\n teeth,\n 'periodontalChart.row.missing',\n 'Missing',\n 'missing',\n )}\n {renderFlagRow(\n teeth,\n 'periodontalChart.row.implant',\n 'Implant',\n 'implant',\n )}\n {renderMobilityRow(teeth)}\n {/* Buccal aspect */}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.pocketDepthBuccal',\n 'Pocket depth — buccal',\n 'buccal',\n 'pocketDepthMm',\n )}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.recessionBuccal',\n 'Recession — buccal',\n 'buccal',\n 'recessionMm',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.bleedingBuccal',\n 'Bleeding — buccal',\n 'buccal',\n 'bleeding',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.plaqueBuccal',\n 'Plaque — buccal',\n 'buccal',\n 'plaque',\n )}\n {renderFurcationRow(teeth, 'buccal')}\n {/* Tooth graphics sit between the buccal and lingual aspects */}\n {showTeeth ? renderToothRow(teeth) : null}\n {/* Lingual aspect */}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.pocketDepthLingual',\n 'Pocket depth — lingual',\n 'lingual',\n 'pocketDepthMm',\n )}\n {renderNumericRow(\n teeth,\n 'periodontalChart.row.recessionLingual',\n 'Recession — lingual',\n 'lingual',\n 'recessionMm',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.bleedingLingual',\n 'Bleeding — lingual',\n 'lingual',\n 'bleeding',\n )}\n {renderToggleRow(\n teeth,\n 'periodontalChart.row.plaqueLingual',\n 'Plaque — lingual',\n 'lingual',\n 'plaque',\n )}\n {renderFurcationRow(teeth, 'lingual')}\n {renderNoteRow(teeth)}\n </tbody>\n </table>\n {showCurves ? (\n <figure\n className=\"ds:m-0 ds:mt-[var(--spacing-2xs)]\"\n aria-label={t('periodontalChart.curve.label', {\n arch: t(captionKey, defaultCaption),\n defaultValue: '{{arch}} probing depth and recession profile',\n })}\n >\n {renderCurve(teeth)}\n </figure>\n ) : null}\n </div>\n </div>\n );\n\n /* ---------------------------------------------------------------- */\n /* Index strip */\n /* ---------------------------------------------------------------- */\n\n const renderIndices = (): ReactNode => {\n const items: Array<{\n key: string;\n label: string;\n value: string;\n elevated: boolean;\n }> = [\n {\n key: 'meanPd',\n label: t('periodontalChart.index.meanPocketDepth', 'Mean PD'),\n value:\n indices.meanPocketDepthMm == null\n ? '–'\n : t('periodontalChart.unit.mm', {\n value: formatNumber(indices.meanPocketDepthMm),\n defaultValue: '{{value}} mm',\n }),\n elevated: false,\n },\n {\n key: 'meanCal',\n label: t('periodontalChart.index.meanAttachment', 'Mean CAL'),\n value:\n indices.meanAttachmentLevelMm == null\n ? '–'\n : t('periodontalChart.unit.mm', {\n value: formatNumber(indices.meanAttachmentLevelMm),\n defaultValue: '{{value}} mm',\n }),\n elevated: false,\n },\n {\n key: 'plaque',\n label: t('periodontalChart.index.plaque', 'Plaque'),\n value:\n indices.plaquePercent == null\n ? '–'\n : t('periodontalChart.unit.percent', {\n value: formatNumber(indices.plaquePercent),\n defaultValue: '{{value}}%',\n }),\n elevated:\n indices.plaquePercent != null &&\n indices.plaquePercent > PLAQUE_WARN_PERCENT,\n },\n {\n key: 'bleeding',\n label: t('periodontalChart.index.bleeding', 'BoP'),\n value:\n indices.bleedingPercent == null\n ? '–'\n : t('periodontalChart.unit.percent', {\n value: formatNumber(indices.bleedingPercent),\n defaultValue: '{{value}}%',\n }),\n elevated:\n indices.bleedingPercent != null &&\n indices.bleedingPercent > BLEEDING_WARN_PERCENT,\n },\n ];\n return (\n <dl\n className=\"type-body-sm ds:flex ds:flex-wrap ds:gap-[var(--spacing-lg)] ds:m-0\"\n aria-label={t('periodontalChart.index.label', 'Summary indices')}\n >\n {items.map((item) => (\n <div\n key={item.key}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n <dt className=\"type-meta ds:text-[color:var(--muted-foreground)]\">\n {item.label}\n </dt>\n <dd\n className={`ds:m-0 ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ${\n item.elevated\n ? 'ds:font-bold ds:text-[color:var(--destructive)]'\n : 'ds:text-[color:var(--foreground)]'\n }`}\n >\n <span>{item.value}</span>\n {item.elevated ? (\n <span className=\"ds:sr-only\">\n {' '}\n {t('periodontalChart.index.elevated', 'elevated')}\n </span>\n ) : null}\n </dd>\n </div>\n ))}\n </dl>\n );\n };\n\n /* ---------------------------------------------------------------- */\n /* Legend */\n /* ---------------------------------------------------------------- */\n\n const legend = (\n <div\n role=\"group\"\n aria-label={t('periodontalChart.legend.label', 'Chart legend')}\n className=\"type-meta ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-md)] ds:text-[color:var(--muted-foreground)]\"\n >\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:h-[2px] ds:w-4 ds:bg-[var(--success)]\"\n />\n {t('periodontalChart.legend.depth', 'Pocket depth')}\n </span>\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:h-[2px] ds:w-4 ds:bg-[var(--accent)]\"\n />\n {t('periodontalChart.legend.recession', 'Recession')}\n </span>\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--destructive)]\"\n />\n {t('periodontalChart.legend.bleeding', 'Bleeding on probing')}\n </span>\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]\">\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--warning)]\"\n />\n {t('periodontalChart.legend.plaque', 'Plaque')}\n </span>\n </div>\n );\n\n return (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- roving-tabindex arrow-key nav across the chart group; each input is individually focusable and labelled\n <div\n ref={rootRef}\n role=\"group\"\n aria-label={resolvedAriaLabel}\n data-component=\"periodontal-chart\"\n data-component-id={id}\n data-mode={mode}\n id={id}\n {...rest}\n onKeyDown={onKeyDown}\n className={rootVariants({ disabled, className })}\n >\n {showIndices ? renderIndices() : null}\n {renderArch(arches.upper, 'periodontalChart.arch.upper', 'Upper arch')}\n {renderArch(arches.lower, 'periodontalChart.arch.lower', 'Lower arch')}\n {legend}\n </div>\n );\n },\n);\n\nPeriodontalChart.displayName = 'PeriodontalChart';\n\nexport type PeriodontalChartDisabled = NonNullable<\n VariantProps<typeof rootVariants>['disabled']\n>;\n"],"names":["FDI_SCHEMA","SITE_SCHEMA","MM_SCHEMA","INDICES_SCHEMA","FINDINGS_SCHEMA","sanitizeTooth","tooth","rest","periodontalChartAgent","handle","s","site","fdi","args","PERIODONTAL_BUCCAL_SITES","PERIODONTAL_LINGUAL_SITES","sitesForSide","side","MODERATE_POCKET_MM","SEVERE_POCKET_MM","PLAQUE_WARN_PERCENT","BLEEDING_WARN_PERCENT","pocketSeverity","depth","moderate","severe","PERIODONTAL_FURCATION_MAP","furcationSlots","_a","PERIODONTAL_PERMANENT_UPPER","PERMANENT_TEETH","PERIODONTAL_PERMANENT_LOWER","PERIODONTAL_PRIMARY_UPPER","PRIMARY_TEETH","PERIODONTAL_PRIMARY_LOWER","periodontalArches","dentition","periodontalToothLabel","numbering","FDI_TO_UNIVERSAL","FDI_TO_PALMER","round1","n","r","computePeriodontalIndices","value","depthSum","attachmentSum","scored","bleeding","plaque","deep","FINDING_ORDER","computePeriodontalFindings","options","rank","i","orderedFdis","a","b","findings","status","pockets","bleedingSites","plaqueSites","suppurationSites","maxPocketDepthMm","maxRecessionMm","PERIODONTAL_SITE_ORDER","reading","pocketDepthMm","recessionMm","suppuration","sev","mobility","furcation","_b","severity","p","emptyPeriodontalChart","upper","lower","out","toPeriodontalSiteList","state","toMobilityMap","rootVariants","cva","numericInputVariants","patchTooth","patch","patchSite","sites","severityTextClass","SITE_DEFAULT","PeriodontalChart","forwardRef","defaultValue","onChange","mode","disabled","showTeeth","assetBaseUrl","showCurves","showIndices","moderatePocketMm","severePocketMm","locale","ariaLabel","id","className","ref","t","i18n","useTranslation","resolvedLocale","rootRef","useRef","isControlled","internal","setInternal","useState","resolved","readOnly","valueRef","commit","useCallback","next","setSite","setTooth","clearTooth","focusSite","field","indices","useMemo","useImperativeHandle","useAgentRegistration","arches","resolvedAriaLabel","formatNumber","siteLabel","toothName","dash","jsx","onKeyDown","el","key","root","other","target","inputs","idx","cellClass","rowHeaderClass","renderNumericInput","raw","_c","label","severityWord","jsxs","e","text","renderToggle","checked","accent","renderFurcationCell","slots","flags","_","renderNumericRow","teeth","labelKey","defaultLabel","renderToggleRow","renderFurcationRow","renderFlagRow","renderMobilityRow","grade","renderNoteRow","note","renderToothRow","meta","FDI_TO_META","symbol","toothImageUrl","renderCurve","depthY","mm","recY","depthRuns","marginRuns","depthRun","marginRun","flush","x","missing","pd","rec","width","toPts","depthPts","marginPts","run","renderArch","captionKey","defaultCaption","renderIndices","items","item","legend"],"mappings":"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"}