hex-audit 0.5.2 → 0.5.4

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 (69) hide show
  1. package/dashboard/.next/BUILD_ID +1 -1
  2. package/dashboard/.next/app-build-manifest.json +22 -13
  3. package/dashboard/.next/app-path-routes-manifest.json +1 -1
  4. package/dashboard/.next/build-manifest.json +4 -4
  5. package/dashboard/.next/next-minimal-server.js.nft.json +1 -1
  6. package/dashboard/.next/next-server.js.nft.json +1 -1
  7. package/dashboard/.next/prerender-manifest.js +1 -1
  8. package/dashboard/.next/prerender-manifest.json +1 -1
  9. package/dashboard/.next/routes-manifest.json +1 -1
  10. package/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/dashboard/.next/server/app/_not-found.html +1 -1
  12. package/dashboard/.next/server/app/_not-found.rsc +4 -4
  13. package/dashboard/.next/server/app/access/page_client-reference-manifest.js +1 -1
  14. package/dashboard/.next/server/app/api/notes/record/route.js +9 -0
  15. package/dashboard/.next/server/app/api/notes/record/route.js.nft.json +1 -0
  16. package/dashboard/.next/server/app/api/notes/route.js +3 -0
  17. package/dashboard/.next/server/app/api/notes/route.js.nft.json +1 -0
  18. package/dashboard/.next/server/app/api/progress/route.js +1 -1
  19. package/dashboard/.next/server/app/calls/page_client-reference-manifest.js +1 -1
  20. package/dashboard/.next/server/app/conformance/page_client-reference-manifest.js +1 -1
  21. package/dashboard/.next/server/app/diagram/page_client-reference-manifest.js +1 -1
  22. package/dashboard/.next/server/app/diagram.html +1 -1
  23. package/dashboard/.next/server/app/diagram.rsc +4 -4
  24. package/dashboard/.next/server/app/flows/page_client-reference-manifest.js +1 -1
  25. package/dashboard/.next/server/app/flows.html +1 -1
  26. package/dashboard/.next/server/app/flows.rsc +4 -4
  27. package/dashboard/.next/server/app/functions/page_client-reference-manifest.js +1 -1
  28. package/dashboard/.next/server/app/issues/page.js +28 -5
  29. package/dashboard/.next/server/app/issues/page.js.nft.json +1 -1
  30. package/dashboard/.next/server/app/issues/page_client-reference-manifest.js +1 -1
  31. package/dashboard/.next/server/app/notes/page.js +22 -0
  32. package/dashboard/.next/server/app/notes/page.js.nft.json +1 -0
  33. package/dashboard/.next/server/app/notes/page_client-reference-manifest.js +1 -0
  34. package/dashboard/.next/server/app/overleaf/page_client-reference-manifest.js +1 -1
  35. package/dashboard/.next/server/app/page.js +2 -2
  36. package/dashboard/.next/server/app/page.js.nft.json +1 -1
  37. package/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  38. package/dashboard/.next/server/app/progress/page_client-reference-manifest.js +1 -1
  39. package/dashboard/.next/server/app/stats/page_client-reference-manifest.js +1 -1
  40. package/dashboard/.next/server/app-paths-manifest.json +5 -2
  41. package/dashboard/.next/server/chunks/494.js +1 -0
  42. package/dashboard/.next/server/chunks/530.js +1 -1
  43. package/dashboard/.next/server/functions-config-manifest.json +1 -1
  44. package/dashboard/.next/server/middleware-build-manifest.js +1 -1
  45. package/dashboard/.next/server/pages/404.html +1 -1
  46. package/dashboard/.next/server/pages/500.html +1 -1
  47. package/dashboard/.next/server/server-reference-manifest.js +1 -1
  48. package/dashboard/.next/server/server-reference-manifest.json +1 -1
  49. package/dashboard/.next/static/chunks/app/issues/page-abb062e4cf118340.js +1 -0
  50. package/dashboard/.next/static/chunks/app/{layout-23765c356f43208c.js → layout-a95b696089e278c3.js} +1 -1
  51. package/dashboard/.next/static/chunks/app/notes/page-bc458d3e1639c526.js +1 -0
  52. package/dashboard/.next/static/chunks/app/{page-a0f25953d4457935.js → page-1226422e170d8895.js} +1 -1
  53. package/dashboard/.next/static/chunks/{main-8f1e837090aef5cf.js → main-a6cc6e599cefa893.js} +1 -1
  54. package/dashboard/.next/static/css/23b3f575e87bea75.css +3 -0
  55. package/dashboard/app/api/notes/record/route.ts +52 -0
  56. package/dashboard/app/api/notes/route.ts +25 -0
  57. package/dashboard/app/issues/IssuesBoardClient.tsx +68 -22
  58. package/dashboard/app/layout.tsx +2 -2
  59. package/dashboard/app/notes/NotesClient.tsx +284 -0
  60. package/dashboard/app/notes/page.tsx +44 -0
  61. package/dashboard/components/DashboardLayout.tsx +25 -2
  62. package/dist/index.js +2 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/skills/diane.md +82 -35
  65. package/package.json +1 -1
  66. package/dashboard/.next/static/chunks/app/issues/page-6bfbf80884d3c9d8.js +0 -1
  67. package/dashboard/.next/static/css/9c84313d0484b74f.css +0 -3
  68. /package/dashboard/.next/static/{QeyYu_4EslVR8FTiyQpNs → JIjye0HUTWALF3gbtaZF8}/_buildManifest.js +0 -0
  69. /package/dashboard/.next/static/{QeyYu_4EslVR8FTiyQpNs → JIjye0HUTWALF3gbtaZF8}/_ssgManifest.js +0 -0
@@ -0,0 +1,3 @@
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*
2
+ ! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com
3
+ */*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-sans);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--font-mono);font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"\201C""\201D""\2018""\2019";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows),0 3px 0 var(--tw-prose-kbd-shadows);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-kbd:#111827;--tw-prose-kbd-shadows:rgba(17,24,39,.1);--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:hsla(0,0%,100%,.1);--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:rgba(0,0,0,.5);--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-3{bottom:.75rem}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:.75rem}.right-3{right:.75rem}.top-0{top:0}.top-3{top:.75rem}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[60\]{z-index:60}.z-\[9000\]{z-index:9000}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-sp-2{margin-bottom:var(--space-2)}.mb-sp-3{margin-bottom:var(--space-3)}.mb-sp-4{margin-bottom:var(--space-4)}.mb-sp-5{margin-bottom:var(--space-5)}.mb-sp-6{margin-bottom:var(--space-6)}.ml-1{margin-left:.25rem}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-4{margin-right:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-sp-2{margin-top:var(--space-2)}.mt-sp-3{margin-top:var(--space-3)}.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-9{height:2.25rem}.h-\[600px\]{height:600px}.h-\[60vh\]{height:60vh}.h-\[700px\]{height:700px}.h-\[85vh\]{height:85vh}.h-full{height:100%}.h-screen{height:100vh}.max-h-72{max-height:18rem}.max-h-\[70vh\]{max-height:70vh}.max-h-\[80vh\]{max-height:80vh}.min-h-9{min-height:2.25rem}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-12{width:3rem}.w-2\.5{width:.625rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-32{width:8rem}.w-4{width:1rem}.w-44{width:11rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-8{width:2rem}.w-\[220px\]{width:220px}.w-full{width:100%}.min-w-0{min-width:0}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-\[200px\]{max-width:200px}.max-w-lg{max-width:32rem}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x:-100%}.translate-x-0{--tw-translate-x:0px}.rotate-180,.translate-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate:180deg}.rotate-90{--tw-rotate:90deg}.rotate-90,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-y{resize:vertical}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-sp-2{gap:var(--space-2)}.gap-sp-3{gap:var(--space-3)}.gap-sp-4{gap:var(--space-4)}.gap-sp-5{gap:var(--space-5)}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-1{row-gap:.25rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-sp-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(var(--space-5) * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(var(--space-5) * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-border-subtle>:not([hidden])~:not([hidden]){border-color:var(--border-subtle)}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-\[var\(--accent\)\],.border-accent{border-color:var(--accent)}.border-border-default{border-color:var(--border-default)}.border-border-emphasis{border-color:var(--border-emphasis)}.border-border-subtle{border-color:var(--border-subtle)}.bg-\[var\(--accent\)\]{background-color:var(--accent)}.bg-\[var\(--critical\)\]{background-color:var(--critical)}.bg-\[var\(--medium\)\]{background-color:var(--medium)}.bg-\[var\(--success\)\]{background-color:var(--success)}.bg-accent{background-color:var(--accent)}.bg-accent-subtle{background-color:var(--accent-subtle)}.bg-severity-critical{background-color:var(--critical)}.bg-surface-0{background-color:var(--surface-0)}.bg-surface-1{background-color:var(--surface-1)}.bg-surface-2{background-color:var(--surface-2)}.bg-surface-3{background-color:var(--surface-3)}.bg-text-tertiary{background-color:var(--text-tertiary)}.bg-transparent{background-color:transparent}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-sp-4{padding:var(--space-4)}.p-sp-6{padding:var(--space-6)}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-sp-3{padding-left:var(--space-3);padding-right:var(--space-3)}.px-sp-4{padding-left:var(--space-4);padding-right:var(--space-4)}.px-sp-5{padding-left:var(--space-5);padding-right:var(--space-5)}.px-sp-6{padding-left:var(--space-6);padding-right:var(--space-6)}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-sp-2{padding-top:var(--space-2);padding-bottom:var(--space-2)}.py-sp-3{padding-top:var(--space-3);padding-bottom:var(--space-3)}.py-sp-4{padding-top:var(--space-4);padding-bottom:var(--space-4)}.py-sp-6{padding-top:var(--space-6);padding-bottom:var(--space-6)}.py-sp-8{padding-top:var(--space-8);padding-bottom:var(--space-8)}.pb-4{padding-bottom:1rem}.pt-16{padding-top:4rem}.pt-6{padding-top:1.5rem}.pt-\[12vh\]{padding-top:12vh}.pt-sp-2{padding-top:var(--space-2)}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-\[11px\]{font-size:11px}.text-body{font-size:14px;line-height:1.55;font-weight:400}.text-caption{font-size:12px;line-height:1.4;font-weight:400}.text-display{font-size:28px;line-height:1.2;font-weight:600}.text-heading{font-size:15px;line-height:1.4;font-weight:500}.text-title{font-size:20px;line-height:1.3;font-weight:600}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-relaxed{line-height:1.625}.tracking-wider{letter-spacing:.05em}.text-\[var\(--critical\)\]{color:var(--critical)}.text-\[var\(--high\)\]{color:var(--high)}.text-\[var\(--info\)\]{color:var(--info)}.text-\[var\(--low\)\]{color:var(--low)}.text-\[var\(--medium\)\]{color:var(--medium)}.text-\[var\(--neutral\)\]{color:var(--neutral)}.text-\[var\(--success\)\]{color:var(--success)}.text-accent{color:var(--accent)}.text-severity-critical{color:var(--critical)}.text-surface-0{color:var(--surface-0)}.text-text-primary{color:var(--text-primary)}.text-text-secondary{color:var(--text-secondary)}.text-text-tertiary{color:var(--text-tertiary)}.placeholder-text-tertiary::-moz-placeholder{color:var(--text-tertiary)}.placeholder-text-tertiary::placeholder{color:var(--text-tertiary)}.opacity-20{opacity:.2}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.opacity-90{opacity:.9}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-2xl,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg)}.shadow-sm{--tw-shadow:var(--shadow-sm);--tw-shadow-colored:var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}:root{--critical:oklch(0.58 0.22 25);--high:oklch(0.68 0.16 55);--medium:oklch(0.72 0.14 85);--low:oklch(0.58 0.14 250);--info:oklch(0.55 0.01 265);--success:oklch(0.60 0.16 145);--neutral:oklch(0.50 0.005 265);--font-sans:"Manrope",system-ui,-apple-system,sans-serif;--font-mono:"JetBrains Mono",ui-monospace,"SF Mono",monospace;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:24px;--space-6:32px;--space-8:48px;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--overlay-bg:oklch(0.08 0.005 265/0.75);--shadow-sm:0 1px 2px oklch(0 0 0/0.05);--shadow-md:0 2px 8px oklch(0 0 0/0.08),0 1px 2px oklch(0 0 0/0.04);--shadow-lg:0 8px 24px oklch(0 0 0/0.12),0 2px 8px oklch(0 0 0/0.06)}[data-theme=dark]{--surface-0:oklch(0.13 0.005 265);--surface-1:oklch(0.17 0.006 265);--surface-2:oklch(0.21 0.006 265);--surface-3:oklch(0.26 0.006 265);--border-subtle:oklch(0.28 0.005 265/0.5);--border-default:oklch(0.32 0.006 265/0.55);--border-emphasis:oklch(0.42 0.01 265/0.65);--text-primary:oklch(0.93 0.005 265);--text-secondary:oklch(0.65 0.01 265);--text-tertiary:oklch(0.48 0.008 265);--accent:oklch(0.58 0.19 265);--accent-subtle:oklch(0.58 0.19 265/0.10);--accent-secondary:oklch(0.68 0.14 265);--accent-warm:oklch(0.68 0.16 55)}[data-theme=light]{--surface-0:oklch(0.97 0.003 265);--surface-1:oklch(1 0 0);--surface-2:oklch(0.96 0.004 265);--surface-3:oklch(0.93 0.005 265);--border-subtle:oklch(0.85 0.005 265/0.5);--border-default:oklch(0.80 0.006 265/0.45);--border-emphasis:oklch(0.70 0.01 265/0.5);--text-primary:oklch(0.15 0.01 265);--text-secondary:oklch(0.42 0.01 265);--text-tertiary:oklch(0.60 0.008 265);--accent:oklch(0.48 0.22 265);--accent-subtle:oklch(0.48 0.22 265/0.08);--accent-secondary:oklch(0.55 0.18 265);--accent-warm:oklch(0.58 0.16 55)}body{font-family:var(--font-sans);color:var(--text-primary);background:var(--surface-0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:14px;line-height:1.55}.font-mono,code,pre{font-family:var(--font-mono)}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.transition-colors,[role=button],a,button,input,select,textarea{transition:color .2s ease,background-color .2s ease,border-color .2s ease,opacity .2s ease,box-shadow .2s ease}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border-default);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--border-emphasis)}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.stagger>*{opacity:0;animation:fade-in .3s ease-out forwards}.stagger>:first-child{animation-delay:.03s}.stagger>:nth-child(2){animation-delay:.06s}.stagger>:nth-child(3){animation-delay:.09s}.stagger>:nth-child(4){animation-delay:.12s}.stagger>:nth-child(5){animation-delay:.15s}.stagger>:nth-child(6){animation-delay:.18s}.stagger>:nth-child(7){animation-delay:.21s}.stagger>:nth-child(8){animation-delay:.24s}@keyframes scale-in{0%{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}.scale-in{animation:scale-in .35s ease-out forwards}.mermaid svg{background:transparent!important}.first\:mt-0:first-child{margin-top:0}.hover\:border-\[var\(--accent\)\]:hover{border-color:var(--accent)}.hover\:border-border-default:hover{border-color:var(--border-default)}.hover\:bg-accent-secondary:hover{background-color:var(--accent-secondary)}.hover\:bg-surface-2:hover{background-color:var(--surface-2)}.hover\:bg-surface-3:hover{background-color:var(--surface-3)}.hover\:text-text-primary:hover{color:var(--text-primary)}.hover\:text-text-secondary:hover{color:var(--text-secondary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.focus\:border-accent:focus{border-color:var(--accent)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-accent:focus{--tw-ring-color:var(--accent)}.focus-visible\:outline:focus-visible{outline-style:solid}.focus-visible\:outline-2:focus-visible{outline-width:2px}.focus-visible\:-outline-offset-2:focus-visible{outline-offset:-2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-accent:focus-visible{outline-color:var(--accent)}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.group:hover .group-hover\:text-accent{color:var(--accent)}.prose-headings\:text-text-primary :is(:where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--text-primary)}.prose-p\:text-text-secondary :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--text-secondary)}.prose-a\:text-accent :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--accent)}.prose-a\:no-underline :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){text-decoration-line:none}.hover\:prose-a\:underline :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))):hover{text-decoration-line:underline}.prose-blockquote\:border-border-emphasis :is(:where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *))){border-color:var(--border-emphasis)}.prose-blockquote\:text-text-secondary :is(:where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--text-secondary)}.prose-strong\:text-text-primary :is(:where(strong):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--text-primary)}.prose-code\:text-accent :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--accent)}.prose-code\:before\:content-none :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))):before{--tw-content:none;content:var(--tw-content)}.prose-code\:after\:content-none :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))):after{--tw-content:none;content:var(--tw-content)}.prose-pre\:border :is(:where(pre):not(:where([class~=not-prose],[class~=not-prose] *))){border-width:1px}.prose-pre\:border-border-default :is(:where(pre):not(:where([class~=not-prose],[class~=not-prose] *))){border-color:var(--border-default)}.prose-pre\:bg-surface-0 :is(:where(pre):not(:where([class~=not-prose],[class~=not-prose] *))){background-color:var(--surface-0)}.prose-li\:text-text-secondary :is(:where(li):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--text-secondary)}.prose-th\:text-text-primary :is(:where(th):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--text-primary)}.prose-td\:text-text-secondary :is(:where(td):not(:where([class~=not-prose],[class~=not-prose] *))){color:var(--text-secondary)}.prose-hr\:border-border-default :is(:where(hr):not(:where([class~=not-prose],[class~=not-prose] *))){border-color:var(--border-default)}@media (min-width:640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:768px){.md\:ml-\[220px\]{margin-left:220px}.md\:hidden{display:none}.md\:translate-x-0{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:pt-6{padding-top:1.5rem}}@media (min-width:1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}@media (min-width:1280px){.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}@font-face{font-family:__Manrope_73ee6c;font-style:normal;font-weight:200 800;font-display:swap;src:url(/_next/static/media/438aa629764e75f3-s.woff2) format("woff2");unicode-range:u+0460-052f,u+1c80-1c8a,u+20b4,u+2de0-2dff,u+a640-a69f,u+fe2e-fe2f}@font-face{font-family:__Manrope_73ee6c;font-style:normal;font-weight:200 800;font-display:swap;src:url(/_next/static/media/875ae681bfde4580-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:__Manrope_73ee6c;font-style:normal;font-weight:200 800;font-display:swap;src:url(/_next/static/media/51251f8b9793cdb3-s.woff2) format("woff2");unicode-range:u+0370-0377,u+037a-037f,u+0384-038a,u+038c,u+038e-03a1,u+03a3-03ff}@font-face{font-family:__Manrope_73ee6c;font-style:normal;font-weight:200 800;font-display:swap;src:url(/_next/static/media/e857b654a2caa584-s.woff2) format("woff2");unicode-range:u+0102-0103,u+0110-0111,u+0128-0129,u+0168-0169,u+01a0-01a1,u+01af-01b0,u+0300-0301,u+0303-0304,u+0308-0309,u+0323,u+0329,u+1ea0-1ef9,u+20ab}@font-face{font-family:__Manrope_73ee6c;font-style:normal;font-weight:200 800;font-display:swap;src:url(/_next/static/media/cc978ac5ee68c2b6-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:__Manrope_73ee6c;font-style:normal;font-weight:200 800;font-display:swap;src:url(/_next/static/media/4c9affa5bc8f420e-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:__Manrope_Fallback_73ee6c;src:local("Arial");ascent-override:103.31%;descent-override:29.07%;line-gap-override:0.00%;size-adjust:103.19%}.__className_73ee6c{font-family:__Manrope_73ee6c,__Manrope_Fallback_73ee6c;font-style:normal}.__variable_73ee6c{--font-sans:"__Manrope_73ee6c","__Manrope_Fallback_73ee6c"}@font-face{font-family:__JetBrains_Mono_3c557b;font-style:normal;font-weight:100 800;font-display:swap;src:url(/_next/static/media/886030b0b59bc5a7-s.woff2) format("woff2");unicode-range:u+0460-052f,u+1c80-1c8a,u+20b4,u+2de0-2dff,u+a640-a69f,u+fe2e-fe2f}@font-face{font-family:__JetBrains_Mono_3c557b;font-style:normal;font-weight:100 800;font-display:swap;src:url(/_next/static/media/0aa834ed78bf6d07-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:__JetBrains_Mono_3c557b;font-style:normal;font-weight:100 800;font-display:swap;src:url(/_next/static/media/67957d42bae0796d-s.woff2) format("woff2");unicode-range:u+0370-0377,u+037a-037f,u+0384-038a,u+038c,u+038e-03a1,u+03a3-03ff}@font-face{font-family:__JetBrains_Mono_3c557b;font-style:normal;font-weight:100 800;font-display:swap;src:url(/_next/static/media/f911b923c6adde36-s.woff2) format("woff2");unicode-range:u+0102-0103,u+0110-0111,u+0128-0129,u+0168-0169,u+01a0-01a1,u+01af-01b0,u+0300-0301,u+0303-0304,u+0308-0309,u+0323,u+0329,u+1ea0-1ef9,u+20ab}@font-face{font-family:__JetBrains_Mono_3c557b;font-style:normal;font-weight:100 800;font-display:swap;src:url(/_next/static/media/939c4f875ee75fbb-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:__JetBrains_Mono_3c557b;font-style:normal;font-weight:100 800;font-display:swap;src:url(/_next/static/media/bb3ef058b751a6ad-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:__JetBrains_Mono_Fallback_3c557b;src:local("Arial");ascent-override:75.79%;descent-override:22.29%;line-gap-override:0.00%;size-adjust:134.59%}.__className_3c557b{font-family:__JetBrains_Mono_3c557b,__JetBrains_Mono_Fallback_3c557b;font-style:normal}.__variable_3c557b{--font-mono:"__JetBrains_Mono_3c557b","__JetBrains_Mono_Fallback_3c557b"}
@@ -0,0 +1,52 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { getOutputDirPath } from '@/lib/data';
5
+ import { createSession } from '../../../../../src/core/notes';
6
+ import { transcribe, WhisperMissingError } from '../../../../../src/core/whisper';
7
+
8
+ export const dynamic = 'force-dynamic';
9
+
10
+ // Recording stays on the machine: the browser MediaRecorder posts the audio
11
+ // blob here (localhost), we save it under .hex/notes/audio/, transcribe it with
12
+ // a LOCAL Whisper engine, and register a session for /diane to ingest. If no
13
+ // engine is installed we keep the audio and return a setup hint — no narration
14
+ // is ever lost.
15
+ export async function POST(request: NextRequest) {
16
+ try {
17
+ const form = await request.formData();
18
+ const audio = form.get('audio');
19
+ const contract = (String(form.get('contract') ?? '').trim() || 'general');
20
+ if (!(audio instanceof Blob)) {
21
+ return NextResponse.json({ error: 'audio file is required' }, { status: 400 });
22
+ }
23
+
24
+ const outputDir = getOutputDirPath();
25
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
26
+ const audioRel = path.join('audio', `${ts}.webm`);
27
+ const audioPath = path.join(outputDir, 'notes', audioRel);
28
+ fs.mkdirSync(path.dirname(audioPath), { recursive: true });
29
+ fs.writeFileSync(audioPath, Buffer.from(await audio.arrayBuffer()));
30
+
31
+ let transcript: string;
32
+ try {
33
+ transcript = transcribe(audioPath);
34
+ } catch (err) {
35
+ if (err instanceof WhisperMissingError) {
36
+ return NextResponse.json(
37
+ { error: err.message, audioSaved: audioRel, whisperMissing: true },
38
+ { status: 422 },
39
+ );
40
+ }
41
+ throw err;
42
+ }
43
+
44
+ const session = createSession(outputDir, { ts, contract, transcript, audio: audioRel });
45
+ return NextResponse.json({ ok: true, session: session.file, contract });
46
+ } catch (err) {
47
+ return NextResponse.json(
48
+ { error: err instanceof Error ? err.message : String(err) },
49
+ { status: 500 },
50
+ );
51
+ }
52
+ }
@@ -0,0 +1,25 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { getOutputDirPath } from '@/lib/data';
3
+ import { writeNote } from '../../../../src/core/notes';
4
+
5
+ export const dynamic = 'force-dynamic';
6
+
7
+ // Mirror dashboard/app/api/issues/[id]/route.ts: import the canonical
8
+ // notes-mutation module across the src/ <-> dashboard/ boundary (works via
9
+ // experimental.externalDir) and call it in-process. The CLI, the /diane skill,
10
+ // and this route all write notes through the same code path.
11
+ export async function PUT(request: NextRequest) {
12
+ const { target, body } = (await request.json()) as { target?: unknown; body?: unknown };
13
+ if (typeof target !== 'string' || typeof body !== 'string') {
14
+ return NextResponse.json({ error: 'target and body must be strings' }, { status: 400 });
15
+ }
16
+ try {
17
+ writeNote(getOutputDirPath(), target, body);
18
+ return NextResponse.json({ ok: true, target });
19
+ } catch (err) {
20
+ return NextResponse.json(
21
+ { error: err instanceof Error ? err.message : String(err) },
22
+ { status: 400 },
23
+ );
24
+ }
25
+ }
@@ -11,7 +11,8 @@ import {
11
11
  useDroppable,
12
12
  } from '@dnd-kit/core';
13
13
  import { SeverityBadge } from '@/components/SeverityBadge';
14
- import { findingToHackmd, type FormatContext } from '@/lib/finding-markdown';
14
+ import { MarkdownRenderer } from '@/components/MarkdownRenderer';
15
+ import { findingToHackmd, findingToGithubBody, type FormatContext } from '@/lib/finding-markdown';
15
16
 
16
17
  export type BoardColumn = 'potential' | 'verified' | 'synced' | 'invalid' | 'duplicate';
17
18
  export type SyncState = 'none' | 'unsynced' | 'synced_open' | 'synced_closed' | 'conflict';
@@ -218,7 +219,7 @@ export function IssuesBoardClient({
218
219
  </header>
219
220
 
220
221
  <DndContext sensors={sensors} onDragEnd={handleDragEnd}>
221
- <div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-5">
222
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-5">
222
223
  {COLUMN_DEFS.map((col) => (
223
224
  <Column key={col.id} column={col} issues={grouped[col.id]} onOpen={setOpenId} />
224
225
  ))}
@@ -364,7 +365,12 @@ function Card({ issue, onOpen }: { issue: BoardIssue; onOpen: (id: string) => vo
364
365
  <span className="font-mono text-caption text-text-tertiary">{issue.id}</span>
365
366
  <SeverityBadge severity={issue.severity} />
366
367
  </div>
367
- <h3 className="mt-1 text-body font-medium text-text-primary line-clamp-2">{issue.title}</h3>
368
+ <h3
369
+ title={issue.title}
370
+ className="mt-1 text-body font-medium text-text-primary line-clamp-3"
371
+ >
372
+ {issue.title}
373
+ </h3>
368
374
  <div className="mt-2 flex flex-wrap items-center gap-1.5 text-caption">
369
375
  <SourceChip source={issue.source} />
370
376
  <SyncChip issue={issue} />
@@ -490,6 +496,8 @@ function IssueModal({
490
496
  const [updateFromClient, setUpdateFromClient] = useState(issue.update_from_client ?? '');
491
497
  const [saving, setSaving] = useState(false);
492
498
  const [copied, setCopied] = useState(false);
499
+ // Open in read-only view mode; the Edit button switches to the form below.
500
+ const [isEditing, setIsEditing] = useState(false);
493
501
  const overlayRef = useRef<HTMLDivElement>(null);
494
502
 
495
503
  // Synced issues are read-only in Hex; edits happen on GitHub.
@@ -534,7 +542,7 @@ function IssueModal({
534
542
  };
535
543
  await onSave(updates);
536
544
  setSaving(false);
537
- onClose();
545
+ setIsEditing(false); // back to the rendered view, now showing the saved values
538
546
  };
539
547
 
540
548
  return (
@@ -551,8 +559,8 @@ function IssueModal({
551
559
  <div
552
560
  role="dialog"
553
561
  aria-modal="true"
554
- aria-label={`Edit issue ${issue.id}`}
555
- className="relative flex max-h-[90vh] w-full max-w-2xl flex-col overflow-hidden border border-border-emphasis bg-surface-1 shadow-2xl"
562
+ aria-label={`Issue ${issue.id}`}
563
+ className="relative flex h-[85vh] w-full max-w-4xl flex-col overflow-hidden border border-border-emphasis bg-surface-1 shadow-2xl"
556
564
  style={{ borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-accent)' }}
557
565
  onClick={(e) => e.stopPropagation()}
558
566
  >
@@ -588,6 +596,8 @@ function IssueModal({
588
596
  <span className="font-mono">/sync-issues</span> to pull the changes back.
589
597
  </div>
590
598
  )}
599
+ {isEditing ? (
600
+ <>
591
601
  <Field label="Title">
592
602
  <input
593
603
  type="text"
@@ -660,6 +670,17 @@ function IssueModal({
660
670
  className="w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 font-mono text-caption text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"
661
671
  />
662
672
  </Field>
673
+ </>
674
+ ) : (
675
+ <>
676
+ <MarkdownRenderer
677
+ content={`**Title**: ${issue.title}\n\n**Severity**: ${issue.severity}\n\n${findingToGithubBody(
678
+ issue,
679
+ formatCtx,
680
+ )}`}
681
+ />
682
+ </>
683
+ )}
663
684
 
664
685
  {issue.code_locations.length > 0 && (
665
686
  <div className="mt-4">
@@ -699,22 +720,47 @@ function IssueModal({
699
720
  {copied ? 'Copied!' : 'Copy as HackMD'}
700
721
  </button>
701
722
  <div className="flex items-center gap-2">
702
- <button
703
- type="button"
704
- onClick={onClose}
705
- className="rounded-md px-3 py-1.5 text-body text-text-secondary hover:text-text-primary"
706
- >
707
- {locked ? 'Close' : 'Cancel'}
708
- </button>
709
- {!locked && (
710
- <button
711
- type="button"
712
- onClick={submit}
713
- disabled={saving}
714
- className="rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 hover:bg-accent-secondary disabled:opacity-50"
715
- >
716
- {saving ? 'Saving…' : 'Save'}
717
- </button>
723
+ {isEditing ? (
724
+ <>
725
+ <button
726
+ type="button"
727
+ onClick={() => setIsEditing(false)}
728
+ className="rounded-md px-3 py-1.5 text-body text-text-secondary hover:text-text-primary"
729
+ >
730
+ Cancel
731
+ </button>
732
+ <button
733
+ type="button"
734
+ onClick={submit}
735
+ disabled={saving}
736
+ className="rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 hover:bg-accent-secondary disabled:opacity-50"
737
+ >
738
+ {saving ? 'Saving…' : 'Save'}
739
+ </button>
740
+ </>
741
+ ) : (
742
+ <>
743
+ <button
744
+ type="button"
745
+ onClick={onClose}
746
+ className="rounded-md px-3 py-1.5 text-body text-text-secondary hover:text-text-primary"
747
+ >
748
+ Close
749
+ </button>
750
+ <button
751
+ type="button"
752
+ onClick={() => setIsEditing(true)}
753
+ disabled={locked}
754
+ title={
755
+ locked
756
+ ? 'Synced to GitHub — edit it there, then run /sync-issues to pull the changes back.'
757
+ : undefined
758
+ }
759
+ className="rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 hover:bg-accent-secondary disabled:cursor-not-allowed disabled:opacity-40"
760
+ >
761
+ Edit
762
+ </button>
763
+ </>
718
764
  )}
719
765
  </div>
720
766
  </div>
@@ -3,7 +3,7 @@ import { Manrope, JetBrains_Mono } from 'next/font/google';
3
3
  import { ThemeProvider } from 'next-themes';
4
4
  import { DashboardLayout } from '@/components/DashboardLayout';
5
5
  import { CommandPalette } from '@/components/CommandPalette';
6
- import { version } from '../../package.json';
6
+ import pkg from '../../package.json';
7
7
  import './globals.css';
8
8
 
9
9
  const manrope = Manrope({
@@ -33,7 +33,7 @@ export default function RootLayout({
33
33
  <body className={`${manrope.variable} ${jetbrainsMono.variable} min-h-screen bg-surface-0 text-text-primary font-sans`}>
34
34
  <ThemeProvider attribute="data-theme" defaultTheme="dark" enableSystem={false}>
35
35
  <CommandPalette />
36
- <DashboardLayout version={version}>{children}</DashboardLayout>
36
+ <DashboardLayout version={pkg.version}>{children}</DashboardLayout>
37
37
  </ThemeProvider>
38
38
  </body>
39
39
  </html>
@@ -0,0 +1,284 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useMemo, useRef, useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { MarkdownRenderer } from '@/components/MarkdownRenderer';
6
+
7
+ interface ContractNote {
8
+ name: string;
9
+ body: string;
10
+ }
11
+
12
+ interface NotesClientProps {
13
+ general: string;
14
+ contractNotes: ContractNote[];
15
+ contractOptions: string[];
16
+ activeContract: string | null;
17
+ unprocessedCount: number;
18
+ }
19
+
20
+ type RecState = 'idle' | 'recording' | 'transcribing';
21
+ const OTHER = '__other__';
22
+
23
+ function fmt(sec: number): string {
24
+ const m = Math.floor(sec / 60);
25
+ const s = sec % 60;
26
+ return `${m}:${String(s).padStart(2, '0')}`;
27
+ }
28
+
29
+ export function NotesClient({
30
+ general,
31
+ contractNotes,
32
+ contractOptions,
33
+ activeContract,
34
+ unprocessedCount,
35
+ }: NotesClientProps) {
36
+ const router = useRouter();
37
+
38
+ // ── Docs (left rail) ──
39
+ const docs = useMemo(
40
+ () => [{ key: 'general', label: 'General', body: general }, ...contractNotes.map((c) => ({ key: c.name, label: c.name, body: c.body }))],
41
+ [general, contractNotes],
42
+ );
43
+ const initialKey =
44
+ activeContract && docs.some((d) => d.key === activeContract) ? activeContract : 'general';
45
+ const [selectedKey, setSelectedKey] = useState<string>(initialKey);
46
+ const current = docs.find((d) => d.key === selectedKey) ?? docs[0];
47
+
48
+ // ── Editing ──
49
+ const [editing, setEditing] = useState(false);
50
+ const [draft, setDraft] = useState('');
51
+ const [saving, setSaving] = useState(false);
52
+
53
+ const startEdit = () => {
54
+ setDraft(current.body);
55
+ setEditing(true);
56
+ };
57
+ const save = async () => {
58
+ setSaving(true);
59
+ try {
60
+ const res = await fetch('/api/notes', {
61
+ method: 'PUT',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({ target: current.key, body: draft }),
64
+ });
65
+ if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error ?? 'save failed');
66
+ setEditing(false);
67
+ router.refresh();
68
+ } catch (err) {
69
+ setStatusMsg(err instanceof Error ? err.message : 'Save failed');
70
+ } finally {
71
+ setSaving(false);
72
+ }
73
+ };
74
+
75
+ // ── Recording ──
76
+ const [recState, setRecState] = useState<RecState>('idle');
77
+ const [recordContract, setRecordContract] = useState<string>(
78
+ activeContract ?? contractOptions[0] ?? OTHER,
79
+ );
80
+ const [otherName, setOtherName] = useState('');
81
+ const [elapsed, setElapsed] = useState(0);
82
+ const [statusMsg, setStatusMsg] = useState<string | null>(null);
83
+
84
+ const recorderRef = useRef<MediaRecorder | null>(null);
85
+ const chunksRef = useRef<Blob[]>([]);
86
+ const streamRef = useRef<MediaStream | null>(null);
87
+ const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
88
+
89
+ const effectiveContract = (recordContract === OTHER ? otherName : recordContract).trim();
90
+
91
+ const upload = useCallback(async () => {
92
+ setRecState('transcribing');
93
+ setStatusMsg('Transcribing locally…');
94
+ try {
95
+ const blob = new Blob(chunksRef.current, { type: 'audio/webm' });
96
+ const fd = new FormData();
97
+ fd.append('audio', blob, 'narration.webm');
98
+ fd.append('contract', effectiveContract || 'general');
99
+ const res = await fetch('/api/notes/record', { method: 'POST', body: fd });
100
+ const data = await res.json().catch(() => ({}));
101
+ if (res.status === 422 && data.whisperMissing) {
102
+ setStatusMsg(
103
+ `Audio saved (${data.audioSaved}) but no local Whisper engine is installed. ${data.error}`,
104
+ );
105
+ } else if (!res.ok) {
106
+ setStatusMsg(data.error ?? 'Recording failed');
107
+ } else {
108
+ setStatusMsg(`Session saved for ${data.contract}. Run /diane in Claude Code to process it.`);
109
+ router.refresh();
110
+ }
111
+ } catch (err) {
112
+ setStatusMsg(err instanceof Error ? err.message : 'Recording failed');
113
+ } finally {
114
+ setRecState('idle');
115
+ }
116
+ }, [effectiveContract, router]);
117
+
118
+ const startRecording = useCallback(async () => {
119
+ setStatusMsg(null);
120
+ try {
121
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
122
+ streamRef.current = stream;
123
+ chunksRef.current = [];
124
+ const mr = new MediaRecorder(stream);
125
+ mr.ondataavailable = (e) => {
126
+ if (e.data.size > 0) chunksRef.current.push(e.data);
127
+ };
128
+ mr.onstop = () => {
129
+ streamRef.current?.getTracks().forEach((t) => t.stop());
130
+ if (timerRef.current) clearInterval(timerRef.current);
131
+ void upload();
132
+ };
133
+ recorderRef.current = mr;
134
+ mr.start();
135
+ setElapsed(0);
136
+ timerRef.current = setInterval(() => setElapsed((e) => e + 1), 1000);
137
+ setRecState('recording');
138
+ } catch {
139
+ setStatusMsg('Could not access the microphone. Grant permission and try again.');
140
+ }
141
+ }, [upload]);
142
+
143
+ const stopRecording = useCallback(() => {
144
+ recorderRef.current?.stop();
145
+ }, []);
146
+
147
+ const canRecord = recState === 'idle' && effectiveContract.length > 0;
148
+
149
+ return (
150
+ <div className="space-y-sp-5">
151
+ {/* ── Recorder bar ── */}
152
+ <div className="rounded-lg border border-border-subtle bg-surface-1 p-4">
153
+ <div className="flex flex-wrap items-center gap-3">
154
+ <span className="text-heading font-medium text-text-primary">Record narration</span>
155
+
156
+ <select
157
+ value={recordContract}
158
+ onChange={(e) => setRecordContract(e.target.value)}
159
+ disabled={recState !== 'idle'}
160
+ className="rounded-md border border-border-default bg-surface-2 px-3 py-1.5 text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50"
161
+ >
162
+ {contractOptions.map((c) => (
163
+ <option key={c} value={c}>
164
+ {c}
165
+ </option>
166
+ ))}
167
+ <option value={OTHER}>Other…</option>
168
+ </select>
169
+
170
+ {recordContract === OTHER && (
171
+ <input
172
+ type="text"
173
+ placeholder="Contract name"
174
+ value={otherName}
175
+ onChange={(e) => setOtherName(e.target.value)}
176
+ disabled={recState !== 'idle'}
177
+ className="rounded-md border border-border-default bg-surface-2 px-3 py-1.5 text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50"
178
+ />
179
+ )}
180
+
181
+ {recState === 'recording' ? (
182
+ <button
183
+ type="button"
184
+ onClick={stopRecording}
185
+ className="flex items-center gap-2 rounded-md bg-severity-critical px-3 py-1.5 text-body font-medium text-surface-0"
186
+ >
187
+ <span className="h-2.5 w-2.5 rounded-sm bg-surface-0" />
188
+ Stop · {fmt(elapsed)}
189
+ </button>
190
+ ) : (
191
+ <button
192
+ type="button"
193
+ onClick={startRecording}
194
+ disabled={!canRecord}
195
+ className="flex items-center gap-2 rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 transition-opacity duration-200 hover:opacity-90 disabled:opacity-40"
196
+ >
197
+ <span className="h-2.5 w-2.5 rounded-full bg-surface-0" />
198
+ {recState === 'transcribing' ? 'Transcribing…' : 'Record'}
199
+ </button>
200
+ )}
201
+ </div>
202
+
203
+ {statusMsg && <p className="mt-3 text-caption text-text-secondary">{statusMsg}</p>}
204
+ {unprocessedCount > 0 && (
205
+ <p className="mt-2 text-caption text-accent">
206
+ {unprocessedCount} narration session{unprocessedCount > 1 ? 's' : ''} waiting — run{' '}
207
+ <code className="font-mono">/diane</code> in Claude Code to process.
208
+ </p>
209
+ )}
210
+ </div>
211
+
212
+ {/* ── Notes (rail + doc) ── */}
213
+ <div className="flex gap-sp-5">
214
+ <nav className="w-44 shrink-0 space-y-0.5">
215
+ {docs.map((d) => (
216
+ <button
217
+ key={d.key}
218
+ type="button"
219
+ onClick={() => {
220
+ setSelectedKey(d.key);
221
+ setEditing(false);
222
+ }}
223
+ className={`block w-full truncate rounded-md px-3 py-1.5 text-left text-body ${
224
+ d.key === selectedKey
225
+ ? 'bg-accent-subtle font-medium text-accent'
226
+ : 'text-text-secondary hover:bg-surface-2 hover:text-text-primary'
227
+ }`}
228
+ >
229
+ {d.label}
230
+ </button>
231
+ ))}
232
+ </nav>
233
+
234
+ <div className="min-w-0 flex-1 rounded-lg border border-border-subtle bg-surface-1 p-5">
235
+ <div className="mb-3 flex items-center justify-between">
236
+ <span className="text-heading font-medium text-text-primary">{current.label}</span>
237
+ {editing ? (
238
+ <div className="flex gap-2">
239
+ <button
240
+ type="button"
241
+ onClick={() => setEditing(false)}
242
+ className="rounded-md px-3 py-1 text-caption text-text-secondary hover:text-text-primary"
243
+ >
244
+ Cancel
245
+ </button>
246
+ <button
247
+ type="button"
248
+ onClick={save}
249
+ disabled={saving}
250
+ className="rounded-md bg-accent px-3 py-1 text-caption font-medium text-surface-0 disabled:opacity-50"
251
+ >
252
+ {saving ? 'Saving…' : 'Save'}
253
+ </button>
254
+ </div>
255
+ ) : (
256
+ <button
257
+ type="button"
258
+ onClick={startEdit}
259
+ className="rounded-md border border-border-default px-3 py-1 text-caption text-text-secondary hover:bg-surface-2 hover:text-text-primary"
260
+ >
261
+ Edit
262
+ </button>
263
+ )}
264
+ </div>
265
+
266
+ {editing ? (
267
+ <textarea
268
+ value={draft}
269
+ onChange={(e) => setDraft(e.target.value)}
270
+ className="h-[60vh] w-full resize-y rounded-md border border-border-default bg-surface-0 p-3 font-mono text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"
271
+ />
272
+ ) : current.body.trim() ? (
273
+ <MarkdownRenderer content={current.body} />
274
+ ) : (
275
+ <p className="text-body text-text-tertiary">
276
+ No notes yet for {current.label}. Record a narration above and run{' '}
277
+ <code className="font-mono">/diane</code>, or click Edit to write notes by hand.
278
+ </p>
279
+ )}
280
+ </div>
281
+ </div>
282
+ </div>
283
+ );
284
+ }