@anthusai/papyrus 5.43.0-next.4 → 5.43.0-next.40

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 (117) hide show
  1. package/amplify/data/resource.js +6 -0
  2. package/amplify/data/resource.ts +7 -0
  3. package/amplify/data/schema.js +6 -0
  4. package/amplify/data/schema.ts +7 -0
  5. package/amplify/functions/category-action/handler.ts +2 -1
  6. package/amplify/functions/content-actions/handler.py +8 -0
  7. package/amplify/functions/content-actions/requirements.txt +2 -0
  8. package/amplify/functions/content-actions/resource.js +1 -1
  9. package/amplify/functions/content-actions/resource.ts +1 -1
  10. package/amplify/functions/model-attachment-upload/handler.ts +1 -0
  11. package/amplify/functions/monitoring-view/schema.js +16 -0
  12. package/amplify/functions/monitoring-view/schema.ts +16 -0
  13. package/amplify/functions/procedure-action/handler.ts +2 -10
  14. package/amplify/functions/proposal-actions/proposals.py +20 -3
  15. package/amplify/plugins/reader-steering/function/handler.py +1 -3
  16. package/amplify/plugins/reader-steering/function/moderation.py +19 -0
  17. package/app/api/newsroom/source-frameable/route.ts +12 -0
  18. package/app/api/newsroom/source-icon/route.ts +20 -0
  19. package/app/newsroom/[section]/[...selection]/page.tsx +2 -0
  20. package/app/newsroom/[section]/page.tsx +1 -0
  21. package/bin/papyrus-app.mjs +12 -1
  22. package/bin/papyrus-infra.mjs +21 -1
  23. package/components/auto-accepted-badge.tsx +21 -0
  24. package/components/conversation-context.tsx +52 -0
  25. package/components/conversation-provider.tsx +20 -0
  26. package/components/conversation-thread.tsx +174 -0
  27. package/components/decision-control.tsx +306 -0
  28. package/components/dev-sandbox-editor-sign-in.tsx +2 -2
  29. package/components/markdown-context.tsx +412 -0
  30. package/components/monitoring-overview-view.tsx +143 -21
  31. package/components/monitoring-panel.tsx +64 -4
  32. package/components/needs-me-bodies.tsx +302 -0
  33. package/components/needs-me-detail.tsx +24 -0
  34. package/components/needs-me-feed.tsx +594 -0
  35. package/components/needs-me-reference-detail.tsx +79 -0
  36. package/components/needs-me-shared.tsx +48 -0
  37. package/components/news-desk-client-provider.tsx +24 -3
  38. package/components/news-desk-page.tsx +2 -1
  39. package/components/news-desk-taxonomy-client.ts +253 -31
  40. package/components/newsroom-access-gate.tsx +13 -20
  41. package/components/newsroom-board-skeleton.tsx +20 -0
  42. package/components/newsroom-board-view.tsx +2 -6
  43. package/components/newsroom-client-shell.tsx +2 -1
  44. package/components/newsroom-ops-overview.tsx +88 -73
  45. package/components/newsroom-ops-shell.tsx +8 -28
  46. package/components/newsroom-organizer.css +127 -3
  47. package/components/newsroom-references-view.tsx +334 -158
  48. package/components/notification-cards.tsx +370 -0
  49. package/components/notification-center.tsx +376 -213
  50. package/components/papyrus-console-shell.tsx +2 -1
  51. package/components/proposals-panel.tsx +21 -24
  52. package/components/proposals-view.tsx +4 -2
  53. package/components/reader-auth-control.tsx +3 -1
  54. package/components/reference-capture-client.ts +138 -0
  55. package/components/reference-capture-panel.tsx +268 -0
  56. package/components/reference-conversation.tsx +43 -0
  57. package/components/reference-feed.tsx +514 -0
  58. package/components/reference-review-card.tsx +62 -197
  59. package/components/reference-source-preview.tsx +27 -5
  60. package/components/reference-stories.tsx +34 -0
  61. package/components/references-page-loading.tsx +61 -0
  62. package/components/references-prefetch.ts +52 -0
  63. package/components/story-reporting-trail-client.ts +45 -8
  64. package/components/story-reporting-trail.tsx +24 -13
  65. package/components/topic-steering-workspace.tsx +50 -439
  66. package/components/ui/auto-grow-textarea.tsx +18 -0
  67. package/components/use-conversation.ts +84 -0
  68. package/components/use-media-query.ts +16 -0
  69. package/components/use-reference-preview-attachments.ts +101 -0
  70. package/components/use-reference-ratings.ts +188 -0
  71. package/infra/amplify-app-shell.js +18 -2
  72. package/infra/amplify-app-shell.ts +18 -2
  73. package/infra/chat-stack.js +222 -0
  74. package/infra/chat-stack.ts +250 -0
  75. package/infra/chat.test.js +142 -0
  76. package/infra/chat.test.ts +151 -0
  77. package/infra/index.js +1 -0
  78. package/infra/site-config.js +58 -1
  79. package/infra/site-config.ts +78 -1
  80. package/lib/category-repository.ts +6 -1
  81. package/lib/content-actions-client.ts +20 -0
  82. package/lib/conversation.ts +154 -0
  83. package/lib/local-preview-feed-fixture.ts +63 -0
  84. package/lib/local-preview-seed.ts +24 -2
  85. package/lib/local-preview-synthetic-rejects.ts +41 -0
  86. package/lib/monitoring-client.ts +29 -2
  87. package/lib/monitoring-overview.ts +52 -1
  88. package/lib/monitoring-paging.ts +58 -0
  89. package/lib/needs-me-items.ts +289 -0
  90. package/lib/needs-me-ranking.ts +315 -0
  91. package/lib/newsroom-all-pages.ts +21 -0
  92. package/lib/newsroom-chat-identity.ts +2 -1
  93. package/lib/newsroom-index-filters.ts +18 -0
  94. package/lib/newsroom-nav.ts +1 -1
  95. package/lib/newsroom-organizer-nav.ts +2 -1
  96. package/lib/newsroom-references.ts +25 -1
  97. package/lib/notification-feed.ts +212 -0
  98. package/lib/proposals-client.ts +29 -0
  99. package/lib/reader-subject.ts +3 -2
  100. package/lib/reference-capture.ts +294 -0
  101. package/lib/reference-feed-cache.ts +170 -0
  102. package/lib/reference-feed-ranking.ts +236 -0
  103. package/lib/reference-policy.ts +12 -0
  104. package/lib/reference-preview.ts +112 -0
  105. package/lib/review-client.ts +71 -5
  106. package/lib/review-drafts.ts +277 -0
  107. package/lib/review-fixture.ts +56 -2
  108. package/lib/review-requests.ts +89 -24
  109. package/lib/site-brand.ts +2 -0
  110. package/lib/source-frameable.ts +77 -0
  111. package/lib/source-icon.ts +104 -0
  112. package/lib/staff-groups.ts +23 -0
  113. package/lib/staging-gate.ts +2 -2
  114. package/lib/story-reporting-trail.ts +33 -2
  115. package/package.json +45 -2
  116. package/routes.manifest.json +23 -1
  117. package/components/newsroom-overview-skeleton.tsx +0 -25
@@ -0,0 +1,20 @@
1
+ import { FEED_SURFACE_PROPS } from "./reference-feed";
2
+
3
+ /** The board's loading state: three columns of pulsing cards in the feed's flat tones and rounded rectangles, with no outlines. */
4
+ export function NewsroomBoardSkeleton() {
5
+ return (
6
+ <div aria-busy="true" aria-label="Loading board records" className="grid w-full grid-cols-1 gap-3 sm:grid-cols-3" data-board-skeleton role="status" {...FEED_SURFACE_PROPS}>
7
+ {Array.from({ length: 3 }, (_, column) => (
8
+ <div className="min-h-64 space-y-3 rounded-[20px] bg-[var(--feed-footer)] p-4" key={column}>
9
+ <div className="h-4 w-1/2 animate-pulse rounded-full bg-[var(--feed-control)]" />
10
+ {Array.from({ length: 2 }, (_, row) => (
11
+ <div className="h-24 rounded-2xl bg-[var(--feed-card)] p-3" key={row}>
12
+ <div className="h-4 w-3/4 animate-pulse rounded-full bg-[var(--feed-control)]" />
13
+ <div className="mt-3 h-3 w-1/2 animate-pulse rounded-full bg-[var(--feed-control)]" />
14
+ </div>
15
+ ))}
16
+ </div>
17
+ ))}
18
+ </div>
19
+ );
20
+ }
@@ -31,6 +31,7 @@ import type { ContentItemVersionRecord } from "../lib/content-item-lifecycle";
31
31
  import { useOptionalNewsDeskClient } from "./news-desk-client-provider";
32
32
  import { NewsDeskAccessGate } from "./newsroom-access-gate";
33
33
  import { isLocalPreviewEnabled } from "../lib/local-preview";
34
+ import { NewsroomBoardSkeleton } from "./newsroom-board-skeleton";
34
35
  import { NewsroomArticleEditor } from "./newsroom-article-editor";
35
36
  import { NewsroomAssignmentDetail } from "./newsroom-assignment-detail";
36
37
  import { NewsroomOpsShell, NewsroomOpsStatusBanner } from "./newsroom-ops-shell";
@@ -174,12 +175,7 @@ export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardS
174
175
  {error && <NewsroomOpsStatusBanner tone="error">{error}</NewsroomOpsStatusBanner>}
175
176
  {subject === "desk" && <IssueTypeFilter config={config} value={typeFilter} onChange={setTypeFilter} name="Record type" className="organizer-board-type-filter" />}
176
177
  <div className="organizer-board-body" data-detail-open={Boolean(issue)} ref={boardBodyRef} onScrollCapture={handleBoardScroll}>
177
- {loading && !issues.length ? <div aria-label="Loading board records" className="grid w-full grid-cols-1 gap-3 sm:grid-cols-3" role="status">
178
- {Array.from({ length: 3 }, (_, column) => <div className="min-h-64 space-y-3 rounded-xl border bg-card p-4" key={column}>
179
- <div className="h-4 w-1/2 rounded bg-muted" />
180
- {Array.from({ length: 2 }, (_, row) => <div className="h-24 rounded-lg border bg-background p-3" key={row}><div className="h-4 w-3/4 rounded bg-muted" /><div className="mt-3 h-3 w-1/2 rounded bg-muted" /></div>)}
181
- </div>)}
182
- </div> : <IssueCollection issues={issues} config={config} query={{ type: subject === "desk" ? issueTypeFromFilter(typeFilter) : boardType }} view={view}
178
+ {loading && !issues.length ? <NewsroomBoardSkeleton /> : <IssueCollection issues={issues} config={config} query={{ type: subject === "desk" ? issueTypeFromFilter(typeFilter) : boardType }} view={view}
183
179
  motion={lowMotion ? STATIC_MOTION : undefined}
184
180
  collapsedColumns={columnState.collapsedColumns} onToggleCollapse={columnState.toggleColumn} initialScroll="start"
185
181
  selectedIssueId={selected} detailOpen={Boolean(issue)} onSelectIssue={item => setSelected(item.id)} ariaLabel={pageTitle}
@@ -2,13 +2,14 @@ import { NewsDeskClientProvider } from "./news-desk-client-provider";
2
2
  import { createDemoCategorySteeringDashboard } from "../lib/category-repository";
3
3
  import { resolveDevSandboxEditorAuth } from "../lib/dev-sandbox-editor-auth";
4
4
  import { isLocalPreviewEnabled } from "../lib/local-preview";
5
+ import { withReferenceFeedFixture } from "../lib/local-preview-feed-fixture";
5
6
  import { getBrandNewsroomSectionsConfigPath, resolveActiveSiteBrand } from "../lib/site-brand-tenant";
6
7
 
7
8
  export function NewsroomClientShell({ children }: Readonly<{ children: React.ReactNode }>) {
8
9
  const devSandboxEditorAuth = resolveDevSandboxEditorAuth();
9
10
  const activeBrand = resolveActiveSiteBrand();
10
11
  const localPreviewDashboard = isLocalPreviewEnabled()
11
- ? createDemoCategorySteeringDashboard(activeBrand.id, getBrandNewsroomSectionsConfigPath(activeBrand))
12
+ ? withReferenceFeedFixture(createDemoCategorySteeringDashboard(activeBrand.id, getBrandNewsroomSectionsConfigPath(activeBrand)))
12
13
  : null;
13
14
 
14
15
  return (
@@ -2,18 +2,11 @@
2
2
 
3
3
  import Link from "next/link";
4
4
  import { usePathname } from "next/navigation";
5
- import { useMemo } from "react";
6
- import { Badge } from "./ui/badge";
5
+ import { useCallback, useMemo } from "react";
7
6
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card";
8
- import type { AssignmentRecord, NewsroomSectionRecord, ReferenceRecord } from "../lib/category-repository";
7
+ import type { AssignmentRecord, NewsroomSectionRecord, ReferenceAttachmentRecord, ReferenceRecord } from "../lib/category-repository";
9
8
  import { getNewsroomNavHref } from "../lib/newsroom-nav";
10
- import {
11
- filterReferencesByStatus,
12
- formatReferenceListDate,
13
- referenceDisplayTitle,
14
- referenceLineageId,
15
- selectCanonicalReferenceRecords,
16
- } from "../lib/newsroom-references";
9
+ import { referenceLineageId, selectCanonicalReferenceRecords, type ReferenceCurationAction } from "../lib/newsroom-references";
17
10
  import {
18
11
  buildNewsroomSectionHref,
19
12
  displayNewsroomSectionShortTitle,
@@ -21,9 +14,15 @@ import {
21
14
  newsroomDeskKind,
22
15
  sortNewsroomSections,
23
16
  } from "../lib/newsroom-sections";
24
- import { newsroomInboxRowClassName } from "../lib/newsroom-list-selection";
17
+ import { cn } from "../lib/utils";
18
+ import { FEED_SURFACE_PROPS } from "./reference-feed";
25
19
  import type { RelevanceDecisionView } from "../lib/relevance-decisions";
20
+ import type { ReferenceRejectionReasonCode } from "../lib/reference-policy";
21
+ import { referencePreviewsByLineage, type ReferencePreview } from "../lib/reference-preview";
22
+ import type { NotificationFixture } from "./notification-center";
26
23
  import { NotificationCenter } from "./notification-center";
24
+ import type { CorrectCaptureAction } from "./reference-capture-client";
25
+ import type { ReviewChoice } from "./reference-review-card";
27
26
 
28
27
  type NewsroomOpsOverviewProps = {
29
28
  assignments: AssignmentRecord[];
@@ -33,26 +32,57 @@ type NewsroomOpsOverviewProps = {
33
32
  humanCuratedReferenceLineageIds?: ReadonlySet<string>;
34
33
  newsroomSections: NewsroomSectionRecord[];
35
34
  references: ReferenceRecord[];
35
+ referenceAttachments?: ReferenceAttachmentRecord[];
36
+ /** True until the References have been read; a saved copy of the last view fills the page meanwhile. */
37
+ loading?: boolean;
38
+ savedCopy?: { references: ReferenceRecord[]; previews: Array<[string, ReferencePreview]>; decisions: Array<[string, RelevanceDecisionView]> } | null;
39
+ predictionState?: "loading" | "ready" | "error";
40
+ /** Words for why a rating cannot be saved right now, or null. */
41
+ disabledReason?: string | null;
42
+ onReview?: (reference: ReferenceRecord, action: ReferenceCurationAction, options?: { reasonCode?: ReferenceRejectionReasonCode | null; note?: string; decisionRelationId?: string; shareable?: boolean }) => Promise<void> | void;
43
+ /** Saves a pasted or corrected capture (the `correctReferenceCapture` action). Without it the capture panel has no edit action. */
44
+ onCorrectCapture?: CorrectCaptureAction;
45
+ /** Local real-data preview of the Notification Center (scripts/export-notification-center-fixture.py). */
46
+ notificationFixture?: NotificationFixture | null;
36
47
  };
37
48
 
38
49
  const NO_DECISIONS: ReadonlyMap<string, RelevanceDecisionView> = new Map();
39
- const NO_IDS: ReadonlySet<string> = new Set();
40
50
 
41
51
  export function NewsroomOpsOverview({
42
52
  actorLabel = "",
43
53
  assignments,
44
54
  decisions = NO_DECISIONS,
45
55
  demo = false,
46
- humanCuratedReferenceLineageIds = NO_IDS,
56
+ humanCuratedReferenceLineageIds,
57
+ disabledReason = null,
58
+ loading = false,
47
59
  newsroomSections,
60
+ notificationFixture = null,
61
+ onCorrectCapture,
62
+ onReview,
63
+ predictionState = "ready",
64
+ referenceAttachments = [],
48
65
  references,
66
+ savedCopy = null,
49
67
  }: NewsroomOpsOverviewProps) {
50
68
  const pathname = usePathname();
51
- const canonicalReferences = useMemo(() => selectCanonicalReferenceRecords(references), [references]);
52
- const pendingReferences = useMemo(
53
- () => filterReferencesByStatus(canonicalReferences, "pending").slice(0, 5),
54
- [canonicalReferences],
55
- );
69
+ const liveReferences = useMemo(() => selectCanonicalReferenceRecords(references), [references]);
70
+ // Until the live read arrives, a saved copy of the last view fills the page. It is display only.
71
+ const showingSavedCopy = loading && !liveReferences.length && Boolean(savedCopy?.references.length);
72
+ const canonicalReferences = showingSavedCopy ? savedCopy!.references : liveReferences;
73
+ const shownDecisions = useMemo(() => (showingSavedCopy && !decisions.size ? new Map(savedCopy!.decisions) : decisions), [decisions, savedCopy, showingSavedCopy]);
74
+ const previews = useMemo(() => {
75
+ const live = referencePreviewsByLineage(referenceAttachments);
76
+ return showingSavedCopy && !live.size ? new Map(savedCopy!.previews) : live;
77
+ }, [referenceAttachments, savedCopy, showingSavedCopy]);
78
+ const rateReference = useCallback(async (reference: ReferenceRecord, choice: ReviewChoice, reasonCode: ReferenceRejectionReasonCode | null, note?: string) => {
79
+ const decision = shownDecisions.get(referenceLineageId(reference));
80
+ await onReview?.(reference, choice, {
81
+ reasonCode: choice === "reject" ? reasonCode : null,
82
+ ...(note ? { note } : {}),
83
+ ...(decision ? { decisionRelationId: decision.decisionRelationId, shareable: false } : {}),
84
+ });
85
+ }, [onReview, shownDecisions]);
56
86
  const attentionAssignments = useMemo(
57
87
  () => assignments
58
88
  .filter((assignment) => {
@@ -68,58 +98,21 @@ export function NewsroomOpsOverview({
68
98
  );
69
99
  const coreDesks = useMemo(() => desks.filter(desk => newsroomDeskKind(desk) === "core"), [desks]);
70
100
  const rotatingDesks = useMemo(() => desks.filter(desk => newsroomDeskKind(desk) === "rotating"), [desks]);
101
+ const rateDisabled = disabledReason ?? (showingSavedCopy ? "Confirming your sign-in. You can answer in a moment." : null);
71
102
 
72
- return (
73
- <div
74
- className="space-y-8 font-sans"
75
- data-news-desk-section="overview"
76
- data-newsroom-ops-home="true"
77
- >
78
- <div className="space-y-8" data-newsroom-overview-feeds>
79
- <div aria-label="Notification Center feed" className="newsroom-overview-feed-frame rounded-xl border bg-card p-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" data-newsroom-overview-notifications role="region" tabIndex={0}>
80
- <NotificationCenter
81
- actorLabel={actorLabel}
82
- decisions={decisions}
83
- demo={demo}
84
- humanCuratedReferenceLineageIds={humanCuratedReferenceLineageIds}
85
- references={canonicalReferences}
86
- />
87
- </div>
88
- <section aria-label="Needs attention" className="space-y-3">
89
- <h2 className="text-sm font-medium text-muted-foreground">Needs attention</h2>
90
- {pendingReferences.length || attentionAssignments.length ? (
103
+ const below = (
104
+ <div className="space-y-12 pt-6" data-newsroom-overview-below {...FEED_SURFACE_PROPS}>
105
+ {attentionAssignments.length ? (
106
+ <section aria-label="Open assignments" className="space-y-3">
107
+ <h2 className="text-sm font-medium text-muted-foreground">Open assignments</h2>
91
108
  <div className="space-y-2">
92
- {pendingReferences.map((reference) => {
93
- const lineageId = referenceLineageId(reference);
94
- const href = getNewsroomNavHref(`/newsroom/references/${encodeURIComponent(lineageId)}`, demo);
95
- const active = pathname === href || pathname.startsWith(`${href}/`);
96
- return (
97
- <Link
98
- aria-current={active ? "true" : undefined}
99
- className={newsroomInboxRowClassName(active)}
100
- data-newsroom-inbox-item="reference"
101
- data-newsroom-list-row
102
- data-selected={active || undefined}
103
- data-newsroom-card
104
- data-newsroom-card-id={lineageId}
105
- href={href}
106
- key={lineageId}
107
- >
108
- <div className="min-w-0 flex-1">
109
- <p className="truncate text-sm font-medium text-foreground">{referenceDisplayTitle(reference)}</p>
110
- <p className="text-xs text-muted-foreground">Review source intake · {formatReferenceListDate(reference)}</p>
111
- </div>
112
- <Badge className="mt-0.5 shrink-0" variant="outline">Pending</Badge>
113
- </Link>
114
- );
115
- })}
116
109
  {attentionAssignments.map((assignment) => {
117
110
  const href = getNewsroomNavHref(`/newsroom/assignments/${encodeURIComponent(assignment.id)}`, demo);
118
111
  const active = pathname === href || pathname.startsWith(`${href}/`);
119
112
  return (
120
113
  <Link
121
114
  aria-current={active ? "true" : undefined}
122
- className={newsroomInboxRowClassName(active)}
115
+ className={cn("flex items-start gap-3 rounded-[16px] bg-[var(--feed-card)] p-3 text-foreground outline-none transition-colors hover:bg-[var(--feed-control)] focus-visible:ring-2 focus-visible:ring-ring", active && "bg-[var(--feed-control)]")}
123
116
  data-newsroom-inbox-item="assignment"
124
117
  data-newsroom-list-row
125
118
  data-selected={active || undefined}
@@ -130,19 +123,13 @@ export function NewsroomOpsOverview({
130
123
  <p className="truncate text-sm font-medium text-foreground">{assignment.title}</p>
131
124
  <p className="text-xs text-muted-foreground">Assignment · {assignment.assignmentTypeKey}</p>
132
125
  </div>
133
- <Badge className="mt-0.5 shrink-0" variant="secondary">{assignment.status}</Badge>
126
+ <span className="mt-0.5 shrink-0 rounded-full bg-[var(--feed-control)] px-2 py-0.5 text-xs font-medium">{assignment.status}</span>
134
127
  </Link>
135
128
  );
136
129
  })}
137
130
  </div>
138
- ) : (
139
- <Card>
140
- <CardContent className="p-4 text-sm text-muted-foreground">
141
- No pending references or open assignments. Check those destinations for other work.
142
- </CardContent>
143
- </Card>
144
- )}
145
- </section>
131
+ </section>
132
+ ) : null}
146
133
 
147
134
  {desks.length ? (
148
135
  <section aria-label="Sections" className="space-y-3" data-newsroom-ops-desks>
@@ -156,7 +143,7 @@ export function NewsroomOpsOverview({
156
143
  href={buildNewsroomSectionHref(section.id, demo)}
157
144
  key={section.id}
158
145
  >
159
- <Card className="h-full transition-colors hover:border-primary/30">
146
+ <Card className="h-full rounded-[20px] border-0 bg-[var(--feed-card)] shadow-none ring-0 transition-colors hover:bg-[var(--feed-control)]">
160
147
  <CardHeader className="p-4">
161
148
  <CardTitle className="text-base font-semibold">{section.title}</CardTitle>
162
149
  <CardDescription className="uppercase tracking-wide text-[0.65rem]">
@@ -176,7 +163,7 @@ export function NewsroomOpsOverview({
176
163
  href={buildNewsroomSectionHref(section.id, demo)}
177
164
  key={section.id}
178
165
  >
179
- <Card className="h-full transition-colors hover:border-primary/30">
166
+ <Card className="h-full rounded-[20px] border-0 bg-[var(--feed-card)] shadow-none ring-0 transition-colors hover:bg-[var(--feed-control)]">
180
167
  <CardHeader className="p-4">
181
168
  <CardTitle className="text-base font-semibold">{section.title}</CardTitle>
182
169
  <CardDescription className="uppercase tracking-wide text-[0.65rem]">
@@ -189,6 +176,34 @@ export function NewsroomOpsOverview({
189
176
  </div> : null}
190
177
  </section>
191
178
  ) : null}
179
+ </div>
180
+ );
181
+
182
+ return (
183
+ <div
184
+ className="h-full font-sans"
185
+ data-news-desk-section="overview"
186
+ data-newsroom-ops-home="true"
187
+ >
188
+ <div className="h-full" data-newsroom-overview-feeds>
189
+ <div className="h-full" data-newsroom-overview-notifications>
190
+ <NotificationCenter
191
+ below={below}
192
+ actorLabel={actorLabel}
193
+ decisions={shownDecisions}
194
+ demo={demo}
195
+ fixture={notificationFixture}
196
+ humanCuratedReferenceLineageIds={humanCuratedReferenceLineageIds}
197
+ onCorrectCapture={onCorrectCapture}
198
+ onRateReference={rateReference}
199
+ referenceAttachments={referenceAttachments}
200
+ predictionLoading={predictionState === "loading"}
201
+ previews={previews}
202
+ referenceDisabledReason={rateDisabled}
203
+ references={canonicalReferences}
204
+ referencesLoading={loading && !canonicalReferences.length}
205
+ />
206
+ </div>
192
207
  </div>
193
208
  </div>
194
209
  );
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
 
3
- import { SearchIcon } from "lucide-react";
4
3
  import { Button } from "./ui/button";
5
4
  import { Separator } from "./ui/separator";
6
5
  import { cn } from "../lib/utils";
@@ -143,6 +142,8 @@ export function NewsroomOpsShell({ activeTab, appTitle, backHref = "/", backLabe
143
142
  media.addEventListener("change", change);
144
143
  return () => { document.removeEventListener("focusin", remember); media.removeEventListener("change", change); cancelAnimationFrame(frame); };
145
144
  }, []);
145
+ // The mobile top bar prefers the brand's compact mark, then scales the full logo; the serif title is the no-logo fallback.
146
+ const MastheadLogo = SITE_BRAND.components?.NewsroomLogoCompact ?? SITE_BRAND.components?.NewsroomLogo;
146
147
  const activePath = browserPathToInternalNewsroomPath(pathname ?? "/newsroom");
147
148
  const sidebarToggleLabel = collapsed ? "Expand sidebar" : "Collapse sidebar";
148
149
  const sidebarToggle = <button type="button" className="organizer-toggle" title={sidebarToggleLabel}
@@ -162,10 +163,10 @@ export function NewsroomOpsShell({ activeTab, appTitle, backHref = "/", backLabe
162
163
  <div className="organizer-workspace">
163
164
  <header className="organizer-topbar" data-newsroom-ops-topbar>
164
165
  {showNavigation && collapsed && sidebarToggle}
165
- <Link className="organizer-masthead" href={backHref} title={backLabel}>{appTitle}</Link>
166
+ <Link className="organizer-masthead" href={backHref} title={backLabel}>{MastheadLogo ? <MastheadLogo className="organizer-masthead-logo" /> : appTitle}</Link>
166
167
  <h1 data-newsroom-ops-title>{pageTitle}</h1><OrganizerSearch demo={demo} /><div className="organizer-header-actions">{headerActions}</div><Account placement="header" />
167
168
  </header>
168
- <main className="organizer-main">{demo && <p className="organizer-demo-notice">Demo workspace · synthetic examples</p>}{children}</main>
169
+ <main className="organizer-main">{demo && !session?.shell.dashboard?.realDataPreview && <p className="organizer-demo-notice">Demo workspace · synthetic examples</p>}{children}</main>
169
170
  </div>
170
171
  {showNavigation && <NewsroomMobileNavigation activeTab={activeTab} items={ORGANIZER_PRIMARY_NAV.map(item => ({ ...item, href: getNewsroomNavHref(item.path, demo) }))}
171
172
  chat={consoleState?.shouldOfferConsole ? { open: consoleState.open, onOpen: consoleState.openConsole } : undefined}
@@ -177,27 +178,6 @@ export function NewsroomOpsShell({ activeTab, appTitle, backHref = "/", backLabe
177
178
  </div>;
178
179
  }
179
180
 
180
- export function NewsroomOpsSearchButton({
181
- disabled,
182
- onPress,
183
- }: {
184
- disabled?: boolean;
185
- onPress?: () => void;
186
- }) {
187
- return (
188
- <Button
189
- aria-label="Search knowledge base"
190
- disabled={disabled}
191
- onClick={onPress}
192
- size="icon-sm"
193
- type="button"
194
- variant="outline"
195
- >
196
- <SearchIcon className="size-4" />
197
- </Button>
198
- );
199
- }
200
-
201
181
  export function NewsroomOpsStatusBanner({
202
182
  children,
203
183
  tone = "info",
@@ -208,10 +188,10 @@ export function NewsroomOpsStatusBanner({
208
188
  return (
209
189
  <div
210
190
  className={cn(
211
- "mb-4 rounded-xl border px-3 py-2 text-sm",
212
- tone === "error" && "border-destructive/30 bg-destructive/10 text-destructive",
213
- tone === "ok" && "border-primary/20 bg-primary/10 text-foreground",
214
- tone === "info" && "border-border bg-muted/40 text-foreground",
191
+ // A success is announced to screen readers and never drawn: it would push the page down. Errors and notes are flat fills, no outlines.
192
+ tone === "ok" ? "sr-only" : "mb-4 rounded-lg px-3 py-2 text-sm",
193
+ tone === "error" && "bg-destructive/10 text-destructive",
194
+ tone === "info" && "bg-muted/60 text-foreground",
215
195
  )}
216
196
  role="status"
217
197
  >
@@ -16,15 +16,14 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
16
16
  .organizer-destinations a[aria-current=page]{color:var(--org-ink);box-shadow:inset 3px 0 0 var(--org-accent)}
17
17
  .newsroom-organizer :is(a,button):focus-visible,.organizer-menu :is(a,button):focus-visible{outline:2px solid currentColor;outline-offset:-3px}
18
18
  .organizer-workspace{display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden;padding:0 16px}
19
- .organizer-topbar{height:48px;min-height:48px;display:flex;align-items:center;gap:16px;border-bottom:1px solid var(--org-line)}
19
+ .organizer-topbar{height:48px;min-height:48px;display:flex;align-items:center;gap:16px}
20
20
  .organizer-masthead{font:900 28px var(--font-wordmark),Georgia,serif;white-space:nowrap;max-width:30vw;overflow:hidden;text-overflow:ellipsis}
21
+ .organizer-masthead-logo{display:block;height:24px;width:auto;max-width:36vw;overflow:visible}
21
22
  .organizer-topbar h1{font:400 12px var(--kb-theme-ui-font);flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0}
22
23
  .organizer-toggle{display:flex;align-items:center;justify-content:center;width:36px;min-height:36px;padding:0;border:1px solid var(--org-line);flex-shrink:0}
23
24
  .organizer-sidebar-head{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.organizer-sidebar-head .organizer-brand{flex:1;min-width:0}
24
25
  .organizer-header-actions{display:flex;align-items:center;gap:8px;min-width:0;max-width:45%;overflow-x:auto}
25
26
  .organizer-main{min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;flex:1;padding:16px 0}
26
- .newsroom-overview-feed-frame{height:auto}
27
- @media(min-width:640px){.newsroom-overview-feed-frame{height:38rem;overflow-y:auto}}
28
27
  .organizer-main>[data-category-steering]{min-height:0}.organizer-demo-notice{font-size:11px;color:var(--org-muted);margin-bottom:12px}
29
28
  .organizer-account{display:flex;align-items:center;gap:10px;min-height:44px;flex-shrink:0;border:1px solid var(--org-line);padding:8px;text-align:left;border-radius:3px}
30
29
  .organizer-account span{min-width:0}.organizer-account strong{display:block;font:600 11px var(--kb-theme-ui-font);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.organizer-account small{display:block;font-size:9px;color:var(--org-muted);margin-top:2px}
@@ -35,6 +34,17 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
35
34
  .organizer-bottom-nav{display:none}
36
35
  .organizer-bottom-nav button{appearance:none;border:0;background:transparent;color:inherit}
37
36
  .organizer-main:has(.organizer-native-board){display:flex;overflow:hidden}
37
+ @media (min-width:768px){
38
+ .organizer-main:has(>[data-category-steering]>[data-news-desk-section=references]){display:flex;flex-direction:column;overflow:hidden;padding-bottom:0}
39
+ .organizer-main>[data-category-steering]:has(>[data-news-desk-section=references]){display:flex;flex-direction:column;flex:1;min-height:0}
40
+ .organizer-main:has(>[data-category-steering]>[data-newsroom-ops-home]){display:flex;flex-direction:column;overflow:hidden;padding-bottom:0}
41
+ .organizer-main>[data-category-steering]:has(>[data-newsroom-ops-home]){display:flex;flex-direction:column;flex:1;min-height:0}
42
+ [data-newsroom-ops-home]{display:flex;flex-direction:column;flex:1;min-height:0;overflow-y:auto}
43
+ [data-newsroom-ops-home]>[data-newsroom-overview-feeds]{display:contents}
44
+ [data-newsroom-overview-notifications]{display:flex;flex-direction:column;flex:0 0 100%;min-height:0}
45
+ [data-newsroom-overview-notifications]>[data-notification-center]{flex:1;min-height:0}
46
+ [data-newsroom-overview-feeds]>:not([data-newsroom-overview-notifications]){flex:none}
47
+ }
38
48
  .organizer-native-board{display:flex;flex-direction:column;flex:1;min-height:0;min-width:0;gap:8px}
39
49
  .organizer-board-view-toggle{display:flex;gap:4px;flex-shrink:0}
40
50
  .organizer-header-button{display:inline-flex;align-items:center;gap:6px;min-height:36px;padding:4px 12px;border:1px solid var(--org-line);font:inherit;color:inherit;background:transparent;text-decoration:none;white-space:nowrap;cursor:pointer}
@@ -184,6 +194,17 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
184
194
  .story-trail-row{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline}
185
195
  .story-trail-meta,.story-trail-empty{font-size:12px;color:var(--org-muted);margin:0}
186
196
  .story-trail-chip{font-size:11px;border:1px solid var(--org-ink);padding:0 6px;text-transform:uppercase;letter-spacing:.04em}
197
+ .story-trail-reference{display:grid;grid-template-columns:minmax(0,96px) minmax(0,1fr);gap:12px;align-items:start;background:color-mix(in srgb,var(--org-ink) 6%,transparent);border-radius:10px;padding:12px}
198
+ .story-trail-reference:not(:has(.story-trail-reference-image)){grid-template-columns:minmax(0,1fr)}
199
+ .story-trail-reference-image{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:8px;background:color-mix(in srgb,var(--org-ink) 10%,transparent)}
200
+ .story-trail-reference-text{display:grid;gap:4px;min-width:0}
201
+ .story-trail-reference-links{display:flex;flex-wrap:wrap;gap:4px 16px;font-size:13px}
202
+ .story-trail-reference-links a,.reference-stories a{display:inline-flex;align-items:center;gap:4px}
203
+ .story-trail-role{display:inline-flex;align-items:center;gap:4px;font-size:11px;text-transform:uppercase;letter-spacing:.04em;background:color-mix(in srgb,var(--org-ink) 12%,transparent);border-radius:999px;padding:1px 8px}
204
+ .reference-stories{display:grid;gap:8px}
205
+ .reference-stories h3{margin:0;font-size:14px;font-weight:500}
206
+ .reference-stories ul{list-style:none;margin:0;padding:0;display:grid;gap:8px}
207
+ .reference-stories li{display:flex;flex-wrap:wrap;gap:4px 12px;align-items:center;background:color-mix(in srgb,var(--org-ink) 6%,transparent);border-radius:10px;padding:10px 12px}
187
208
  .story-trail-message pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0 0;font-size:12px;max-height:320px;overflow:auto}
188
209
  .story-trail-message summary{cursor:pointer;font-size:13px}
189
210
  @container (min-width:900px){.story-trail{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:32px;align-items:start}}
@@ -347,3 +368,106 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
347
368
  .news-desk-columns--administration .news-desk-newsletter__text{margin:0;padding:12px;max-height:280px;overflow:auto;border:1px solid var(--org-line);font:400 12px/1.5 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere}
348
369
  .news-desk-columns--administration .news-desk-newsletter__filters{display:flex;flex-wrap:wrap;gap:12px 24px}
349
370
  .news-desk-columns--administration .news-desk-newsletter__filters .news-desk-doctrine-card__field{min-width:180px}
371
+
372
+ /* The References feed is rounded and borderless (Ryan's design rule): flat tones and rounded rectangles instead of lines and boxes.
373
+ The newsroom chrome squares every "rounded" class above; inside a [data-feed-surface] the rounded classes mean what they say.
374
+ The repeated attribute raises specificity over the squaring rules without !important escalation. */
375
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface] [class*="rounded-full"],
376
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface][class*="rounded-full"] { border-radius: var(--feed-radius-pill) !important; }
377
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface] [class*="rounded-[20px]"],
378
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface][class*="rounded-[20px]"] { border-radius: var(--feed-radius-lg) !important; }
379
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface] [class*="rounded-2xl"],
380
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface][class*="rounded-2xl"] { border-radius: var(--feed-radius-md) !important; }
381
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface] [class*="rounded-[16px]"],
382
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface][class*="rounded-[16px]"] { border-radius: var(--feed-radius-md) !important; }
383
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface] [class*="rounded-3xl"],
384
+ .newsroom-organizer[data-kanbus-theme] [data-feed-surface][data-feed-surface][data-feed-surface][class*="rounded-3xl"] { border-radius: var(--feed-radius-xl) !important; }
385
+
386
+ /* The mobile sheet is portaled outside .newsroom-organizer; it carries the theme attributes itself, so the same radii apply inside it. */
387
+ [data-kanbus-theme][data-feed-surface] [class*="rounded-full"] { border-radius: var(--feed-radius-pill) !important; }
388
+ [data-kanbus-theme][data-feed-surface] [class*="rounded-[20px]"] { border-radius: var(--feed-radius-lg) !important; }
389
+ [data-kanbus-theme][data-feed-surface] [class*="rounded-2xl"], [data-kanbus-theme][data-feed-surface] [class*="rounded-[16px]"] { border-radius: var(--feed-radius-md) !important; }
390
+ [data-kanbus-theme][data-feed-surface] [class*="rounded-3xl"] { border-radius: var(--feed-radius-xl) !important; }
391
+
392
+ /* Feed tones. Light: cards a touch lighter than the page, footers between, controls a touch darker. Dark: each step lifts toward the ink.
393
+ Also set on the portaled mobile sheet, which carries the theme attributes itself. */
394
+ [data-kanbus-theme] [data-feed-surface], [data-kanbus-theme][data-feed-surface] {
395
+ --feed-card: color-mix(in oklab, var(--kb-theme-paper) 35%, white);
396
+ --feed-footer: color-mix(in oklab, var(--kb-theme-paper) 70%, white);
397
+ --feed-control: color-mix(in oklab, var(--kb-theme-paper) 90%, var(--kb-theme-ink));
398
+ /* Corner radii of the whole feed layer, in one place (Ryan, 2026-10-27: half of the first design). lg: cards and the detail pane;
399
+ md: rating cells, buttons, textareas; xl: sheets; pill: chips, filters and toggles (a rounded rectangle, not a capsule). */
400
+ --feed-radius-xl: 12px;
401
+ --feed-radius-lg: 10px;
402
+ --feed-radius-md: 8px;
403
+ --feed-radius-pill: 8px;
404
+ /* Verdict tones for the rating grid. Hue is never the only signal: the cell also carries a CircleCheck or
405
+ CircleX and the word Match or Miss. Named here, in the feed layer, because the component must not hold
406
+ literal palette values and because Tailwind's dark: variant (.dark *) never fires in this app. */
407
+ --feed-verdict-match-tint: #05966933;
408
+ --feed-verdict-match-ink: #064e3b;
409
+ --feed-verdict-match-fill: #059669;
410
+ --feed-verdict-miss-tint: #dc262633;
411
+ --feed-verdict-miss-ink: #7f1d1d;
412
+ --feed-verdict-miss-fill: #dc2626;
413
+ /* The quiet step for an unpredicted cell and a switched-off button. */
414
+ --feed-quiet: 0.6;
415
+ }
416
+ [data-kanbus-theme][data-color-mode="dark"] [data-feed-surface], [data-kanbus-theme][data-color-mode="dark"][data-feed-surface] {
417
+ --feed-card: color-mix(in oklab, var(--kb-theme-paper) 94%, var(--kb-theme-ink));
418
+ --feed-footer: color-mix(in oklab, var(--kb-theme-paper) 90%, var(--kb-theme-ink));
419
+ --feed-control: color-mix(in oklab, var(--kb-theme-paper) 80%, var(--kb-theme-ink));
420
+ /* Dark verdicts lift toward the tint rather than down into it, so the ink reads on a dark card. */
421
+ --feed-verdict-match-tint: #34d39933;
422
+ --feed-verdict-match-ink: #d1fae5;
423
+ --feed-verdict-match-fill: #34d399;
424
+ --feed-verdict-miss-tint: #f8717133;
425
+ --feed-verdict-miss-ink: #fee2e2;
426
+ --feed-verdict-miss-fill: #f87171;
427
+ }
428
+
429
+ /* Borderless detail pane, Add panel and mobile sheet ([data-feed-flat]): no border, rule or box anywhere inside. Regions are told apart
430
+ by tone (card, footer, control) and shapes are rounded rectangles. The repeated attribute outranks the newsroom chrome's own rules. */
431
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat],
432
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] * { border-width: 0 !important; }
433
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] *:not(:focus-visible) { box-shadow: none; }
434
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] :is([data-slot=separator], hr) { display: none !important; }
435
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] :is(button, [role=button], [data-slot=badge]):not([aria-pressed=true]):not([data-slot=sheet-close]):not([class*="hover:bg-muted"]) {
436
+ background: var(--feed-control) !important; border-radius: var(--feed-radius-pill) !important; color: var(--foreground, inherit);
437
+ }
438
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] :is(button, [role=button])[aria-pressed=true]:not([class*="destructive"]) {
439
+ background: var(--primary) !important; color: var(--primary-foreground) !important; border-radius: var(--feed-radius-pill) !important;
440
+ }
441
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] :is(button, [role=button])[aria-pressed=true][class*="destructive"] { border-radius: var(--feed-radius-pill) !important; }
442
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] :is(textarea, input:not([type=radio]):not([type=checkbox]), select, details, [data-cyclotron-choice]:not([data-predicted])) {
443
+ background: var(--feed-control) !important; border-radius: 16px !important;
444
+ }
445
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] [data-cyclotron-choice] { border-radius: var(--feed-radius-pill) !important; }
446
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] [data-review-queue-bar] { background: var(--feed-footer) !important; }
447
+ /* The decision control keeps one corner across its segments and its reason field, and its predicted segment
448
+ keeps its tones apart: the flat rules above give every unpressed button the same control fill, which would
449
+ leave the predicted side and an untouched side looking identical. */
450
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] [data-decision-control] :is(button, textarea) { border-radius: var(--feed-radius-md) !important; }
451
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] [data-decision-control] [data-review-choice][data-state="idle"] { background: color-mix(in oklab, var(--feed-control) 45%, transparent) !important; }
452
+
453
+ /* The rating grid keeps its own rounded rectangles in the borderless panes (not the pill shape the flat rules give other buttons). */
454
+ [data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat][data-feed-flat] [data-rating-grid] :is(button, [data-cyclotron-choice]) { border-radius: var(--feed-radius-md) !important; }
455
+
456
+ /* Flat chrome (Ryan, 2026-10-28): the shell has no outlines or rules either. Controls are tone fills in rounded rectangles; on a phone the
457
+ page's own heading names the page, so the bar keeps one search field and the account button, nothing cut off. */
458
+ .newsroom-organizer,.organizer-menu,.organizer-account-popup{--org-control:color-mix(in oklab,var(--org-paper) 90%,var(--org-ink))}
459
+ .organizer-sidebar{border-right:0}
460
+ .organizer-search{border-bottom:0;background:var(--org-control);border-radius:8px;padding:6px 10px}
461
+ .newsroom-organizer button.organizer-toggle,.newsroom-organizer button.organizer-account{border:0;background:var(--org-control);border-radius:8px !important}
462
+ .organizer-account-popup{border:0}
463
+ .organizer-menu .organizer-destinations a{border:0;background:var(--org-control);border-radius:8px !important}
464
+ @media(max-width:900px){
465
+ .organizer-topbar h1{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
466
+ .organizer-search{flex:1;max-width:none}
467
+ .organizer-header-settings{display:none}
468
+ .organizer-bottom-nav{border-top:0;background:color-mix(in oklab,var(--org-paper) 95%,var(--org-ink))}
469
+ }
470
+
471
+ /* Captured text in the reference detail reads in the feed's own type, not the analysis view's serif and small caps. */
472
+ [data-capture-text] .reference-capture-markdown,[data-capture-text] .reference-capture-markdown :is(h1,h2,h3,h4,h5,h6,p,li,th,td){font-family:inherit;letter-spacing:normal;text-transform:none}
473
+ [data-capture-text] .reference-capture-markdown :is(h3,h4,h5){font-size:1em;font-weight:600;margin:1em 0 .25em}