@anthusai/papyrus 1.4.0-next.4 → 1.4.0-next.6

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/amplify/data/resource.js +13 -3
  2. package/amplify/data/resource.ts +13 -3
  3. package/amplify/data/schema.js +10 -0
  4. package/amplify/data/schema.ts +10 -0
  5. package/amplify/functions/category-action/handler.ts +20 -19
  6. package/amplify/functions/console-chat-responder/src/main.rs +1 -1
  7. package/amplify/functions/content-actions/handler.py +23 -0
  8. package/amplify/functions/content-actions/requirements.txt +1 -1
  9. package/amplify/functions/content-actions/resource.js +1 -1
  10. package/amplify/functions/content-actions/resource.ts +1 -1
  11. package/amplify/functions/renditions/requirements.txt +1 -1
  12. package/amplify/functions/shared/reference-curation-metadata.ts +15 -0
  13. package/amplify/functions/shared/slack-events.ts +2 -2
  14. package/amplify/publication/renditions-function.js +1 -1
  15. package/amplify/publication/renditions-function.ts +1 -1
  16. package/app/api/local-preview/derive/route.ts +28 -0
  17. package/app/api/local-preview/media/route.ts +25 -0
  18. package/app/globals.css +28 -28
  19. package/app/layout.tsx +8 -5
  20. package/app/newsroom/[section]/[...selection]/page.tsx +0 -1
  21. package/app/newsroom/[section]/page.tsx +0 -1
  22. package/app/newsroom/insights/[threadId]/page.tsx +5 -16
  23. package/app/newsroom/insights/page.tsx +4 -12
  24. package/components/board-card-excerpts.tsx +45 -0
  25. package/components/markus-ir-preview.tsx +47 -2
  26. package/components/news-desk-page.tsx +0 -1
  27. package/components/news-desk-taxonomy-client.ts +25 -220
  28. package/components/newsroom-article-editor.tsx +135 -39
  29. package/components/newsroom-assignment-detail.tsx +35 -1
  30. package/components/newsroom-board-view.tsx +29 -7
  31. package/components/newsroom-newsprint-board.css +10 -2
  32. package/components/newsroom-ops-overview.tsx +27 -5
  33. package/components/newsroom-organizer-shell.tsx +25 -7
  34. package/components/newsroom-organizer.css +196 -4
  35. package/components/priming-proof-page.tsx +17 -8
  36. package/components/prospectus-account-chooser.tsx +98 -0
  37. package/components/story-reporting-trail-client.ts +91 -0
  38. package/components/story-reporting-trail.tsx +115 -0
  39. package/components/topic-steering-workspace.tsx +154 -671
  40. package/components/ui/sheet.tsx +1 -0
  41. package/components/wiki-concepts-view.tsx +153 -0
  42. package/components/wiki-index-client.ts +51 -0
  43. package/lib/content-actions-client.ts +3 -0
  44. package/lib/content-actions-demo.ts +55 -3
  45. package/lib/content-item-lifecycle.ts +4 -2
  46. package/lib/content-source-context.ts +9 -1
  47. package/lib/front-matter-fields.ts +42 -0
  48. package/lib/graphql-content-repository.ts +42 -7
  49. package/lib/local-preview-seed.ts +55 -2
  50. package/lib/local-preview-tenant.ts +27 -0
  51. package/lib/newsroom-articles.ts +2 -0
  52. package/lib/newsroom-board.ts +4 -0
  53. package/lib/newsroom-forum-routes.ts +2 -43
  54. package/lib/newsroom-index-filters.ts +3 -40
  55. package/lib/newsroom-media-backend.ts +5 -0
  56. package/lib/newsroom-nav.ts +1 -3
  57. package/lib/newsroom-organizer-nav.ts +5 -12
  58. package/lib/newsroom-section-routing.ts +1 -1
  59. package/lib/newsroom-sections.ts +12 -0
  60. package/lib/papyrus-web-locations.ts +3 -24
  61. package/lib/priming-proof-report.ts +1 -0
  62. package/lib/prospectus-tenant.ts +22 -0
  63. package/lib/semantic-graph.ts +5 -5
  64. package/lib/site-brand.ts +9 -0
  65. package/lib/story-reporting-trail.ts +106 -0
  66. package/lib/tenant-keys.ts +32 -0
  67. package/lib/wiki.ts +56 -0
  68. package/package.json +15 -1
  69. package/routes.manifest.json +23 -7
@@ -1,7 +1,12 @@
1
1
  "use client";
2
2
 
3
3
  import Link from "next/link";
4
+ import { useState } from "react";
4
5
  import type { AssignmentRecord } from "../lib/category-repository";
6
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "./ui/select";
7
+
8
+ export type AssignmentDeskOption = { value: string; label: string; kind: "core" | "rotating" };
9
+ const NO_DESK_VALUE = "__no-desk__";
5
10
 
6
11
  const assignmentDateFormat = new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" });
7
12
 
@@ -17,7 +22,22 @@ type DetailField = { label: string; value: string | null | undefined };
17
22
  * The whole assignment, laid out by container width: one column in a narrow Board panel, labelled
18
23
  * columns when the container is wide, such as the dedicated page on a large monitor.
19
24
  */
20
- export function NewsroomAssignmentDetail({ assignment, pageHref, showTitle = true }: { assignment: AssignmentRecord; pageHref?: string; showTitle?: boolean }) {
25
+ export function NewsroomAssignmentDetail({ assignment, deskOptions = [], onDeskChange, pageHref, showTitle = true }: {
26
+ assignment: AssignmentRecord;
27
+ deskOptions?: AssignmentDeskOption[];
28
+ onDeskChange?: (deskId: string | null) => Promise<void>;
29
+ pageHref?: string;
30
+ showTitle?: boolean;
31
+ }) {
32
+ const [deskError, setDeskError] = useState<string | null>(null);
33
+ const [savingDesk, setSavingDesk] = useState(false);
34
+ const currentDesk = assignment.sectionId || assignment.sectionKey || "";
35
+ const deskChoices = deskOptions;
36
+ const changeDesk = (value: string) => {
37
+ if (!onDeskChange) return;
38
+ setSavingDesk(true); setDeskError(null);
39
+ onDeskChange(value === NO_DESK_VALUE ? null : value).catch((reason: unknown) => setDeskError(reason instanceof Error ? reason.message : "Could not save the desk.")).finally(() => setSavingDesk(false));
40
+ };
21
41
  const facts: DetailField[] = [
22
42
  { label: "Status", value: assignment.status },
23
43
  { label: "Type", value: assignment.assignmentTypeKey },
@@ -37,6 +57,20 @@ export function NewsroomAssignmentDetail({ assignment, pageHref, showTitle = tru
37
57
  <dl className="newsroom-assignment-detail-facts">
38
58
  {facts.map(field => <div key={field.label}><dt>{field.label}</dt><dd>{field.value}</dd></div>)}
39
59
  </dl>
60
+ {onDeskChange && <label className="newsroom-assignment-detail-desk">
61
+ <span>Desk</span>
62
+ <Select onValueChange={(value) => { if (typeof value === "string") changeDesk(value); }} value={deskChoices.some(option => option.value === currentDesk) ? currentDesk : NO_DESK_VALUE} disabled={savingDesk}>
63
+ <SelectTrigger aria-label="Desk" className="w-full" data-newsroom-assignment-desk>
64
+ <SelectValue>{deskChoices.find(option => option.value === currentDesk)?.label ?? "No desk"}</SelectValue>
65
+ </SelectTrigger>
66
+ <SelectContent>
67
+ <SelectItem value={NO_DESK_VALUE}>No desk</SelectItem>
68
+ {(["core", "rotating"] as const).map(kind => deskChoices.some(option => option.kind === kind) ? <SelectGroup key={kind}><SelectLabel>{kind === "core" ? "Core desks" : "Rotating desks"}</SelectLabel>{deskChoices.filter(option => option.kind === kind).map(option => <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>)}</SelectGroup> : null)}
69
+ </SelectContent>
70
+ </Select>
71
+ {currentDesk && !deskChoices.some(option => option.value === currentDesk) ? <small className="newsroom-desk-legacy-value">Old section value: {currentDesk}</small> : null}
72
+ {deskError && <p role="alert" className="newsroom-assignment-detail-error">{deskError}</p>}
73
+ </label>}
40
74
  <div className="newsroom-assignment-detail-prose">
41
75
  {prose.map(field => <section key={field.label}><h3>{field.label}</h3><p>{field.value}</p></section>)}
42
76
  </div>
@@ -1,10 +1,12 @@
1
1
  "use client";
2
2
 
3
- import { useEffect, useMemo, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import Link from "next/link";
5
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
5
6
  import { IssueCollection, IssueDetailPanel, IssueTypeFilter, issueTypeFromFilter, useBoardColumnState } from "@kanbus/ui";
6
7
  import "@kanbus/ui/styles/kanban.css";
7
8
  import "./newsroom-newsprint-board.css";
9
+ import { BoardCardExcerpts } from "./board-card-excerpts";
8
10
  import { PlusIcon } from "lucide-react";
9
11
  import { newsroomHref } from "../lib/newsroom-base-path";
10
12
  import {
@@ -21,10 +23,10 @@ import {
21
23
  type NativeBoardIssue,
22
24
  } from "../lib/newsroom-board";
23
25
  import type { AssignmentRecord, NewsroomSectionRecord } from "../lib/category-repository";
24
- import { displayNewsroomSectionShortTitle } from "../lib/newsroom-sections";
26
+ import { displayNewsroomSectionShortTitle, newsroomDeskOptions } from "../lib/newsroom-sections";
25
27
  import { getContentItemName, getContentItemStageDefinitions, SITE_BRAND } from "../lib/site-brand";
26
28
  import { getNewsroomNavHref } from "../lib/newsroom-nav";
27
- import { loadNewsroomAssignmentPage, loadNewsroomPublishedContentItemVersions } from "./news-desk-taxonomy-client";
29
+ import { loadNewsroomAssignmentPage, loadNewsroomPublishedContentItemVersions, saveNewsroomAssignmentDesk } from "./news-desk-taxonomy-client";
28
30
  import { usePagedContentItemVersions } from "./use-paged-content-item-versions";
29
31
  import type { ContentItemVersionRecord } from "../lib/content-item-lifecycle";
30
32
  import { useOptionalNewsDeskClient } from "./news-desk-client-provider";
@@ -36,6 +38,8 @@ import { NewsroomOpsShell, NewsroomOpsStatusBanner } from "./newsroom-ops-shell"
36
38
  import { useResolvedPapyrusTheme } from "./use-resolved-papyrus-theme";
37
39
  import { readLocalReaderSettings, subscribeReaderSettingsChanges } from "./reader-settings";
38
40
 
41
+ /** A board with a story or assignment open lives at `?open=<id>`, distinct from the standalone page for that record. */
42
+ const SELECTED_ISSUE_PARAMETER = "open";
39
43
  const STATIC_MOTION = { mode: "static" } as const;
40
44
  const BOARD_REFRESH_INTERVAL_MILLISECONDS = 30_000;
41
45
  const COLUMN_LOAD_MORE_DISTANCE_PIXELS = 240;
@@ -63,11 +67,21 @@ export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardS
63
67
  const [publishedVersions, setPublishedVersions] = useState<ContentItemVersionRecord[]>([]);
64
68
  const [assignments, setAssignments] = useState<AssignmentRecord[]>([]);
65
69
  const [view, setView] = useState<"board" | "list">("board");
66
- const [selected, setSelected] = useState<string | null>(null);
70
+ const router = useRouter();
71
+ const pathname = usePathname();
72
+ const searchParams = useSearchParams();
73
+ const selected = searchParams.get(SELECTED_ISSUE_PARAMETER);
74
+ const setSelected = useCallback((issueId: string | null) => {
75
+ const next = new URLSearchParams(searchParams.toString());
76
+ if (issueId) next.set(SELECTED_ISSUE_PARAMETER, issueId); else next.delete(SELECTED_ISSUE_PARAMETER);
77
+ const query = next.toString();
78
+ router.push(query ? `${pathname}?${query}` : pathname, { scroll: false });
79
+ }, [pathname, router, searchParams]);
67
80
  const [supportingRecordsLoading, setSupportingRecordsLoading] = useState(false);
68
81
  const [supportingRecordsError, setSupportingRecordsError] = useState<string | null>(null);
69
82
  const generation = useRef(0);
70
83
  const boardBodyRef = useRef<HTMLDivElement | null>(null);
84
+ const [boardBodyElement, setBoardBodyElement] = useState<HTMLDivElement | null>(null);
71
85
  const [typeFilter, setTypeFilter] = useState<string | null>("all");
72
86
  const showsContentItems = subject !== "assignment";
73
87
  const contentItemColumnKeyPrefix = `${contentItemBoardType()}/`;
@@ -88,7 +102,7 @@ export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardS
88
102
  }, []);
89
103
  useEffect(() => {
90
104
  const request = ++generation.current;
91
- setAssignmentBoardIssues([]); setPublishedVersions([]); setAssignments([]); setSelected(null); setSupportingRecordsError(null);
105
+ setAssignmentBoardIssues([]); setPublishedVersions([]); setAssignments([]); setSupportingRecordsError(null);
92
106
  if (!allowed) { setSupportingRecordsLoading(false); return; }
93
107
  const loadSupportingRecords = () => subject !== "assignment"
94
108
  ? Promise.all([loadNewsroomPublishedContentItemVersions(), loadEveryNewsroomAssignment()]).then(([published, everyAssignment]) => {
@@ -139,6 +153,13 @@ export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardS
139
153
  });
140
154
  const deskTitle = session?.shell.dashboard?.newsroomSections?.find(section => section.id === deskId)?.title ?? (deskId ? displayNewsroomSectionShortTitle({ id: deskId } as NewsroomSectionRecord) : "Desk");
141
155
  const pageTitle = subject === "contentItem" ? getContentItemName().plural : subject === "desk" ? deskTitle : "Assignments";
156
+ const deskOptions = useMemo(() => newsroomDeskOptions(session?.shell.dashboard?.newsroomSections ?? []), [session?.shell.dashboard?.newsroomSections]);
157
+ const changeAssignmentDesk = async (record: AssignmentRecord, deskId: string | null) => {
158
+ await saveNewsroomAssignmentDesk(record.id, deskId);
159
+ const moved = { ...record, sectionId: deskId, sectionKey: deskId };
160
+ setAssignments(current => current.map(candidate => candidate.id === record.id ? moved : candidate));
161
+ setAssignmentBoardIssues(current => current.map(candidate => candidate.nativeId === record.id ? { ...candidate } : candidate));
162
+ };
142
163
  const issue = issues.find(item => item.id === selected);
143
164
  if (!allowed) return <NewsDeskAccessGate shell={session?.shell ?? null} />;
144
165
  return <NewsroomOpsShell activeTab={subject === "contentItem" ? "articles" : subject === "desk" ? "desks" : "assignments"} appTitle={SITE_BRAND.appTitle} backLabel={SITE_BRAND.backToHomeLabel} pageTitle={pageTitle}
@@ -155,7 +176,8 @@ export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardS
155
176
  <div className="organizer-native-board papyrus-newsprint-board" data-color-mode={theme}>
156
177
  {error && <NewsroomOpsStatusBanner tone="error">{error}</NewsroomOpsStatusBanner>}
157
178
  {subject === "desk" && <IssueTypeFilter config={config} value={typeFilter} onChange={setTypeFilter} name="Record type" className="organizer-board-type-filter" />}
158
- <div className="organizer-board-body" data-detail-open={Boolean(issue)} ref={boardBodyRef} onScrollCapture={handleBoardScroll}>
179
+ <div className="organizer-board-body" data-detail-open={Boolean(issue)} ref={node => { boardBodyRef.current = node; setBoardBodyElement(node); }} onScrollCapture={handleBoardScroll}>
180
+ <BoardCardExcerpts excerpts={issues.flatMap(item => item.kind === "contentItem" && item.excerpt ? [{ issueId: item.id, text: item.excerpt }] : [])} scope={boardBodyElement} />
159
181
  <IssueCollection issues={issues} config={config} query={{ type: subject === "desk" ? issueTypeFromFilter(typeFilter) : boardType }} view={view}
160
182
  motion={lowMotion ? STATIC_MOTION : undefined}
161
183
  collapsedColumns={columnState.collapsedColumns} onToggleCollapse={columnState.toggleColumn} initialScroll="start"
@@ -170,7 +192,7 @@ export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardS
170
192
  : (() => {
171
193
  const record = assignments.find(candidate => candidate.id === issue.nativeId);
172
194
  return record
173
- ? <NewsroomAssignmentDetail assignment={record} pageHref={getNewsroomNavHref(issue.href)} showTitle={false} />
195
+ ? <NewsroomAssignmentDetail assignment={record} deskOptions={deskOptions} onDeskChange={deskId => changeAssignmentDesk(record, deskId)} pageHref={getNewsroomNavHref(issue.href)} showTitle={false} />
174
196
  : <Link href={getNewsroomNavHref(issue.href)}>Open {issue.type}</Link>;
175
197
  })()}
176
198
  </IssueDetailPanel>}
@@ -20,6 +20,7 @@
20
20
  }
21
21
  .papyrus-newsprint-board .kb-grid { height: 100%; min-height: 0; scrollbar-width: thin; }
22
22
  .papyrus-newsprint-board .kb-column { border-left: 1px solid var(--org-line); padding-left: 10px; }
23
+ .papyrus-newsprint-board .kb-column:first-child { border-left: 0; padding-left: 0; }
23
24
  .papyrus-newsprint-board .kb-column-header {
24
25
  border-bottom: 2px solid var(--org-ink);
25
26
  padding: 5px 0;
@@ -35,10 +36,11 @@
35
36
  --issue-priority-bg: var(--org-paper) !important;
36
37
  background: transparent !important;
37
38
  border-radius: 0;
38
- border-bottom: 1px solid var(--org-line) !important;
39
+ border: 0 !important;
39
40
  box-shadow: none !important;
40
41
  }
41
- .papyrus-newsprint-board .issue-accent-bar { border-bottom: 1px solid var(--org-line); background: var(--org-paper) !important; color: var(--org-ink) !important; }
42
+ .papyrus-newsprint-board .issue-card + .issue-card { border-top: 1px solid var(--org-line) !important; }
43
+ .papyrus-newsprint-board .issue-accent-bar { border-bottom: 0; background: var(--org-paper) !important; color: var(--org-ink) !important; }
42
44
  .papyrus-newsprint-board .issue-accent-id { font: 400 9px Newsroom UI, system-ui, sans-serif; letter-spacing: .8px; white-space: nowrap; color: var(--org-ink) !important; }
43
45
  .papyrus-newsprint-board .issue-accent-priority { border-radius: 0; color: var(--org-muted) !important; background: transparent !important; font-size: 8px; }
44
46
  .papyrus-newsprint-board .issue-card h3 { font: 700 19px/1.2 Newsroom Display, Georgia, serif !important; letter-spacing: -.2px; }
@@ -46,3 +48,9 @@
46
48
  .papyrus-newsprint-board .issue-card[data-selected=true] h3 { color: var(--org-ink) !important; }
47
49
  .papyrus-newsprint-board .kb-issue-detail { background: var(--org-paper); border: 1px solid var(--org-line); border-radius: 0; box-shadow: none; }
48
50
  .papyrus-newsprint-board .kb-issue-detail h2 { font: 700 28px/1.2 Newsroom Display, Georgia, serif; border-bottom: 3px double var(--org-ink); padding-bottom: 12px; }
51
+ .papyrus-newsprint-board .issue-card .issue-accent-bar { height: auto !important; margin: 0 !important; width: 100% !important; padding: 0 !important; min-height: 0; }
52
+ .papyrus-newsprint-board .issue-card .issue-card-content { padding-top: 2px !important; gap: 4px; }
53
+ .papyrus-newsprint-board .issue-card h3 { margin: 0; }
54
+ .board-card-excerpt p { margin: 0; font-size: 13px; line-height: 1.4; color: var(--org-ink); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
55
+ .board-card-excerpt[data-expanded="true"] p { display: block; -webkit-line-clamp: unset; }
56
+ .board-card-excerpt button { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; text-decoration: underline; text-underline-offset: 3px; color: var(--org-ink); cursor: pointer; }
@@ -5,7 +5,6 @@ import {
5
5
  ClipboardListIcon,
6
6
  FileTextIcon,
7
7
  LayersIcon,
8
- LightbulbIcon,
9
8
  MessageSquareIcon,
10
9
  NetworkIcon,
11
10
  SearchIcon,
@@ -30,6 +29,7 @@ import {
30
29
  buildNewsroomSectionHref,
31
30
  displayNewsroomSectionShortTitle,
32
31
  isEnabledNewsroomSection,
32
+ newsroomDeskKind,
33
33
  sortNewsroomSections,
34
34
  } from "../lib/newsroom-sections";
35
35
  import { newsroomInboxRowClassName } from "../lib/newsroom-list-selection";
@@ -49,7 +49,6 @@ const DESTINATION_ICONS: Record<DestinationKey, typeof BookOpenIcon> = {
49
49
  assignments: ClipboardListIcon,
50
50
  references: BookOpenIcon,
51
51
  messages: MessageSquareIcon,
52
- insights: LightbulbIcon,
53
52
  topics: LayersIcon,
54
53
  concepts: NetworkIcon,
55
54
  administration: SettingsIcon,
@@ -102,6 +101,8 @@ export function NewsroomOpsOverview({
102
101
  () => sortNewsroomSections(newsroomSections).filter(isEnabledNewsroomSection),
103
102
  [newsroomSections],
104
103
  );
104
+ const coreDesks = useMemo(() => desks.filter(desk => newsroomDeskKind(desk) === "core"), [desks]);
105
+ const rotatingDesks = useMemo(() => desks.filter(desk => newsroomDeskKind(desk) === "rotating"), [desks]);
105
106
 
106
107
  const destinationCounts: Partial<Record<DestinationKey, number | null>> = {
107
108
  references: referenceCounts.pending ?? 0,
@@ -174,11 +175,11 @@ export function NewsroomOpsOverview({
174
175
  href={href}
175
176
  key={lineageId}
176
177
  >
177
- <Badge className="mt-0.5 shrink-0" variant="outline">Pending</Badge>
178
178
  <div className="min-w-0 flex-1">
179
179
  <p className="truncate text-sm font-medium text-foreground">{referenceDisplayTitle(reference)}</p>
180
180
  <p className="text-xs text-muted-foreground">Review source intake · {formatReferenceListDate(reference)}</p>
181
181
  </div>
182
+ <Badge className="mt-0.5 shrink-0" variant="outline">Pending</Badge>
182
183
  </Link>
183
184
  );
184
185
  })}
@@ -195,11 +196,11 @@ export function NewsroomOpsOverview({
195
196
  href={href}
196
197
  key={assignment.id}
197
198
  >
198
- <Badge className="mt-0.5 shrink-0" variant="secondary">{assignment.status}</Badge>
199
199
  <div className="min-w-0 flex-1">
200
200
  <p className="truncate text-sm font-medium text-foreground">{assignment.title}</p>
201
201
  <p className="text-xs text-muted-foreground">Assignment · {assignment.assignmentTypeKey}</p>
202
202
  </div>
203
+ <Badge className="mt-0.5 shrink-0" variant="secondary">{assignment.status}</Badge>
203
204
  </Link>
204
205
  );
205
206
  })}
@@ -216,8 +217,9 @@ export function NewsroomOpsOverview({
216
217
  {desks.length ? (
217
218
  <section aria-label="Desks" className="space-y-3" data-newsroom-ops-desks>
218
219
  <h2 className="text-sm font-medium text-muted-foreground">Desks</h2>
220
+ {coreDesks.length ? <h3 className="newsroom-desk-group-heading" data-newsroom-ops-desk-group="core">Core desks</h3> : null}
219
221
  <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
220
- {desks.map((section) => (
222
+ {coreDesks.map((section) => (
221
223
  <Link
222
224
  className="block rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
223
225
  data-newsroom-ops-desk={section.id}
@@ -235,6 +237,26 @@ export function NewsroomOpsOverview({
235
237
  </Link>
236
238
  ))}
237
239
  </div>
240
+ {rotatingDesks.length ? <h3 className="newsroom-desk-group-heading" data-newsroom-ops-desk-group="rotating">Rotating desks <small>appear for a while, then rotate out</small></h3> : null}
241
+ {rotatingDesks.length ? <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
242
+ {rotatingDesks.map((section) => (
243
+ <Link
244
+ className="block rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
245
+ data-newsroom-ops-desk={section.id}
246
+ href={buildNewsroomSectionHref(section.id, demo)}
247
+ key={section.id}
248
+ >
249
+ <Card className="h-full transition-colors hover:border-primary/30">
250
+ <CardHeader className="p-4">
251
+ <CardTitle className="text-base font-semibold">{section.title}</CardTitle>
252
+ <CardDescription className="uppercase tracking-wide text-[0.65rem]">
253
+ {displayNewsroomSectionShortTitle(section)}
254
+ </CardDescription>
255
+ </CardHeader>
256
+ </Card>
257
+ </Link>
258
+ ))}
259
+ </div> : null}
238
260
  </section>
239
261
  ) : null}
240
262
  </div>
@@ -2,20 +2,36 @@
2
2
 
3
3
  import { useEffect, useId, useState } from "react";
4
4
  import Link from "next/link";
5
- import { usePathname } from "next/navigation";
6
- import { BookOpen, ClipboardList, LayoutDashboard, Menu, MessagesSquare, PanelLeftClose, PanelLeftOpen, Settings, UserRound, LogIn, LogOut } from "lucide-react";
5
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
6
+ import { BookOpen, ClipboardList, Menu, Search, MessagesSquare, PanelLeftClose, PanelLeftOpen, Settings, UserRound, LogIn, LogOut } from "lucide-react";
7
7
  import { ORGANIZER_NAV_GROUPS, ORGANIZER_PRIMARY_NAV, ORGANIZER_SETTINGS_DESTINATION } from "../lib/newsroom-organizer-nav";
8
8
  import { browserPathToInternalNewsroomPath } from "../lib/newsroom-base-path";
9
9
  import { getNewsroomNavHref } from "../lib/newsroom-nav";
10
10
  import { SITE_BRAND } from "../lib/site-brand";
11
11
  import { useOptionalNewsDeskClient } from "./news-desk-client-provider";
12
12
  import { ReaderAuthControl } from "./reader-auth-control";
13
+ import { ProspectusAccountChooser, useProspectusAccountSummary } from "./prospectus-account-chooser";
13
14
  import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetTrigger } from "./ui/sheet";
14
15
  import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator } from "./ui/dropdown-menu";
15
16
  import type { NewsroomOpsShellProps } from "./newsroom-ops-shell";
16
17
  import "./newsroom-organizer.css";
17
18
 
18
- const ICONS = { overview: LayoutDashboard, articles: ClipboardList, references: BookOpen, messages: MessagesSquare };
19
+ /** The standard place to search the knowledge base; the Search page is the results page. */
20
+ function OrganizerSearch({ demo }: { demo: boolean }) {
21
+ const router = useRouter();
22
+ const searchParams = useSearchParams();
23
+ const [text, setText] = useState(searchParams.get("q") ?? "");
24
+ return <form className="organizer-search" role="search" onSubmit={event => {
25
+ event.preventDefault();
26
+ const query = text.trim();
27
+ if (query) router.push(getNewsroomNavHref(`/newsroom/search?q=${encodeURIComponent(query)}`, demo));
28
+ }}>
29
+ <Search size={14} aria-hidden="true" />
30
+ <input aria-label="Search the knowledge base" data-newsroom-search type="search" onChange={event => setText(event.target.value)} placeholder="Search" value={text} />
31
+ </form>;
32
+ }
33
+
34
+ const ICONS = { articles: ClipboardList, references: BookOpen, messages: MessagesSquare };
19
35
 
20
36
  /** Navigation is a presentation layer; existing route and server authorization still own access. */
21
37
  function Navigation({ activePath, demo, admin, surface, onNavigate }: { activePath: string; demo: boolean; admin: boolean; surface: string; onNavigate?: () => void }) {
@@ -39,6 +55,7 @@ function Account({ placement }: { placement: "sidebar" | "header" }) {
39
55
  const session = useOptionalNewsDeskClient();
40
56
  const auth = session?.shell.auth ?? { status: "loading" as const, label: "Checking sign-in" };
41
57
  const [open, setOpen] = useState(false);
58
+ const accountSummary = useProspectusAccountSummary();
42
59
  useEffect(() => {
43
60
  const media = matchMedia("(max-width: 900px)");
44
61
  const close = () => setOpen(false);
@@ -46,12 +63,13 @@ function Account({ placement }: { placement: "sidebar" | "header" }) {
46
63
  return () => media.removeEventListener("change", close);
47
64
  }, []);
48
65
  return <DropdownMenu open={open} onOpenChange={setOpen}>
49
- <DropdownMenuTrigger className={`organizer-account organizer-account-${placement}`} aria-label="Account menu" title="Account menu">
50
- <UserRound size={18} aria-hidden="true" /><span><strong>{auth.label}</strong><small>{auth.status === "signedIn" ? "Account & preferences" : auth.status === "signedOut" ? "Not signed in" : "Checking session"}</small></span>
66
+ <DropdownMenuTrigger id={`organizer-account-trigger-${placement}`} className={`organizer-account organizer-account-${placement}`} aria-label="Account menu" title="Account menu">
67
+ <UserRound size={18} aria-hidden="true" /><span><strong>{auth.label}</strong><small>{accountSummary ?? (auth.status === "signedIn" ? "Account & preferences" : auth.status === "signedOut" ? "Not signed in" : "Checking session")}</small></span>
51
68
  </DropdownMenuTrigger>
52
69
  <DropdownMenuContent className="organizer-account-popup" side={placement === "sidebar" ? "top" : "bottom"} align="end">
53
70
  <DropdownMenuGroup>
54
71
  <DropdownMenuLabel>{auth.label}</DropdownMenuLabel><DropdownMenuSeparator />
72
+ <ProspectusAccountChooser />
55
73
  <DropdownMenuItem render={<Link href={getNewsroomNavHref(ORGANIZER_SETTINGS_DESTINATION.path)} />}><Settings aria-hidden="true" />{ORGANIZER_SETTINGS_DESTINATION.label}</DropdownMenuItem>
56
74
  <ReaderAuthControl authState={auth} renderAction={({ disabled, onClick, signedIn }) => <DropdownMenuItem disabled={disabled} closeOnClick={false} onClick={onClick}>
57
75
  {signedIn ? <LogOut aria-hidden="true" /> : <LogIn aria-hidden="true" />}{signedIn ? "Sign out" : "Sign in"}
@@ -107,7 +125,7 @@ export function NewsroomOrganizerShell({ activeTab, appTitle, backHref = "/", ba
107
125
  return <div className="newsroom-organizer" data-news-desk data-newsroom-ops-shell="true" data-newsroom-chrome="organizer" data-sidebar-collapsed={collapsed || !showNavigation ? "true" : "false"}>
108
126
  {showNavigation && <aside className="organizer-sidebar" hidden={collapsed} id="organizer-sidebar">
109
127
  <div className="organizer-sidebar-head">
110
- <Link className="organizer-brand" href={getNewsroomNavHref("/newsroom", demo)}>{appTitle}<small>The newsroom</small></Link>
128
+ <Link className="organizer-brand" href={getNewsroomNavHref("/newsroom", demo)}>{SITE_BRAND.components?.NewsroomLogo ? <SITE_BRAND.components.NewsroomLogo className="organizer-brand-logo" /> : appTitle}<small>The newsroom</small></Link>
111
129
  {sidebarToggle}
112
130
  </div>
113
131
  <Navigation activePath={activePath} demo={demo} admin={admin} surface="sidebar" />
@@ -117,7 +135,7 @@ export function NewsroomOrganizerShell({ activeTab, appTitle, backHref = "/", ba
117
135
  <header className="organizer-topbar" data-newsroom-ops-topbar>
118
136
  {showNavigation && collapsed && sidebarToggle}
119
137
  <Link className="organizer-masthead" href={backHref} title={backLabel}>{appTitle}</Link>
120
- <h1 data-newsroom-ops-title>{pageTitle}</h1><div className="organizer-header-actions">{headerActions}</div><Account placement="header" />
138
+ <h1 data-newsroom-ops-title>{pageTitle}</h1><OrganizerSearch demo={demo} /><div className="organizer-header-actions">{headerActions}</div><Account placement="header" />
121
139
  </header>
122
140
  <main className="organizer-main">{demo && <p className="organizer-demo-notice">Demo workspace · synthetic examples</p>}{children}</main>
123
141
  </div>
@@ -15,9 +15,9 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
15
15
  .organizer-brand small{display:block;font:400 10px Newsroom UI,system-ui;letter-spacing:.3em;text-transform:uppercase;margin-top:12px}
16
16
  .organizer-destinations{min-height:0;overflow:auto;overscroll-behavior:contain;flex:1;padding-right:1px}
17
17
  .organizer-destinations section+section{margin-top:24px}.organizer-destinations h3{font:600 10px Newsroom UI,system-ui;letter-spacing:.07em;text-transform:uppercase;color:var(--org-muted);margin:16px 0 8px}
18
- .organizer-destinations a{display:flex;flex-direction:column;align-items:start;gap:6px;padding:14px 8px;border-bottom:1px solid var(--org-line);color:var(--org-muted);text-decoration:none;min-height:44px}
19
- .organizer-destinations a strong{font:700 16px Newsroom Display,Georgia,serif}.organizer-destinations a small{font:400 11px/1.5 Newsroom UI,system-ui;white-space:normal;overflow-wrap:anywhere}
20
- .organizer-destinations a[aria-current=page]{border-bottom:2px solid var(--org-ink);color:var(--org-ink)}
18
+ .organizer-destinations a{display:flex;flex-direction:column;align-items:start;gap:6px;padding:14px 8px;border-bottom:1px solid var(--org-line);color:var(--org-ink);text-decoration:none;min-height:44px}
19
+ .organizer-destinations a strong{font:700 16px Newsroom Display,Georgia,serif}.organizer-destinations a small{font:400 11px/1.5 Newsroom UI,system-ui;white-space:normal;overflow-wrap:anywhere;color:var(--org-muted)}
20
+ .organizer-destinations a[aria-current=page]{border-bottom:3px double var(--org-ink);color:var(--org-ink);box-shadow:inset 3px 0 0 var(--org-ink)}
21
21
  .newsroom-organizer :is(a,button):focus-visible,.organizer-menu :is(a,button):focus-visible{outline:2px solid currentColor;outline-offset:-3px}
22
22
  .organizer-workspace{display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden;padding:0 16px}
23
23
  .organizer-topbar{height:48px;min-height:48px;display:flex;align-items:center;gap:16px;border-bottom:1px solid var(--org-ink)}
@@ -39,7 +39,7 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
39
39
  .organizer-native-board{display:flex;flex-direction:column;flex:1;min-height:0;min-width:0;gap:8px}
40
40
  .organizer-board-view-toggle{display:flex;gap:4px;flex-shrink:0}
41
41
  .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}
42
- .organizer-header-button[aria-pressed=true]{background:var(--org-ink);color:var(--org-paper)}
42
+ .organizer-header-button[aria-pressed=true]{background:transparent;color:var(--org-ink);font-weight:700;box-shadow:inset 0 -3px 0 var(--org-ink)}
43
43
  .organizer-board-note{font-size:11px;line-height:1.5;margin:0;color:var(--org-muted);white-space:nowrap}
44
44
  @media(min-width:901px){.newsroom-organizer[data-sidebar-collapsed=false] .organizer-masthead{display:none}}
45
45
  .organizer-board-body{position:relative;display:grid;grid-template-columns:minmax(0,1fr);flex:1;min-width:0;min-height:0;gap:16px}
@@ -82,3 +82,195 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
82
82
  .newsroom-embedded-article [class*="grid-cols-2"],.newsroom-embedded-article [class*="lg:grid"]{grid-template-columns:minmax(0,1fr) !important}
83
83
  @container (min-width:1100px){.newsroom-embedded-article [class*="lg:grid"]{grid-template-columns:repeat(2,minmax(0,1fr)) !important}}
84
84
  .newsroom-organizer [class*="rounded"]:not([data-slot=avatar]):not([class*="animate-spin"]):not([role=progressbar]),.organizer-menu,.organizer-account-popup,[data-slot=dialog-content],[data-slot=popover-content],[data-slot=dropdown-menu-content],[data-slot=select-content],[data-slot=tooltip-content]{border-radius:0 !important}
85
+
86
+ :root.light-theme .newsroom-organizer,:root.light-theme .organizer-menu,:root.light-theme .organizer-account-popup{--org-ink:var(--foreground-strong);--org-muted:var(--foreground)}
87
+ .newsroom-organizer [class~="bg-primary"],.newsroom-organizer [class~="bg-secondary"],.newsroom-organizer [data-slot=button][class*="bg-primary"]{background:transparent !important;color:var(--org-ink) !important;border:1px solid var(--org-ink) !important;font-weight:700}
88
+ .newsroom-organizer [class~="text-primary-foreground"],.newsroom-organizer [class~="text-secondary-foreground"]{color:var(--org-ink) !important}
89
+ .newsroom-organizer [data-selected=true],.newsroom-organizer [aria-current=true]{background:transparent !important;color:var(--org-ink) !important;box-shadow:inset 3px 0 0 var(--org-ink);font-weight:600}
90
+ .newsroom-organizer .news-desk-columns--administration{--administration-gap:16px;--administration-quiet:color-mix(in srgb,rgb(var(--ink-solid-rgb)) 64%,var(--org-paper));--administration-strong:rgb(var(--ink-solid-rgb));container-type:inline-size;font-family:Newsroom UI,system-ui,sans-serif;font-size:14px;line-height:1.5;max-width:1280px}
91
+ .news-desk-columns--administration :is(h2,h3,strong){font-family:Newsroom Display,Georgia,serif;color:var(--administration-strong)}
92
+ .news-desk-columns--administration .category-steering-section--lead>.category-steering-section__header{display:none}
93
+ .news-desk-columns--administration .news-desk-settings-shell{display:grid;grid-template-columns:minmax(0,1fr);gap:24px;align-items:start}
94
+ .news-desk-columns--administration .news-desk-settings-nav{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;border-right:0;padding-right:0}
95
+ .news-desk-columns--administration .news-desk-settings-nav a{display:flex;flex-direction:column;gap:4px;padding:12px;border:1px solid var(--org-line);background:transparent;color:inherit;text-decoration:none;min-height:44px;min-width:0}
96
+ .news-desk-columns--administration .news-desk-settings-nav a[data-active=true]{border-color:var(--administration-strong);box-shadow:inset 0 -2px 0 var(--administration-strong);background:transparent}
97
+ .news-desk-columns--administration .news-desk-settings-nav a strong{font-size:16px;font-weight:700;line-height:1.2}
98
+ .news-desk-columns--administration .news-desk-settings-nav a span{display:none;font:400 12px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:0;text-transform:none;color:var(--administration-quiet)}
99
+ .news-desk-columns--administration .news-desk-settings-panel{display:grid;gap:24px;min-width:0}
100
+ .news-desk-columns--administration .category-steering-section__header{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:4px 16px;margin:0 0 16px;padding-bottom:8px;border-bottom:2px solid var(--administration-strong)}
101
+ .news-desk-columns--administration .category-steering-section__header h2{margin:0;font-size:22px;font-weight:700;line-height:1.2}
102
+ .news-desk-columns--administration .category-steering-section__header span{font:400 12px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:0;text-transform:none;color:var(--administration-quiet)}
103
+ .news-desk-columns--administration .story-label{margin:0;font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--administration-quiet)}
104
+ .news-desk-columns--administration .news-desk-doctrine-list{display:grid;gap:24px;min-width:0;max-width:none;width:100%}
105
+ .news-desk-columns--administration .news-desk-doctrine-card{display:grid;gap:16px;min-width:0;padding:0;border:0}
106
+ .news-desk-columns--administration .news-desk-doctrine-card+.news-desk-doctrine-card{padding-top:24px;border-top:1px solid var(--org-line)}
107
+ .news-desk-columns--administration .news-desk-doctrine-card__header,.news-desk-columns--administration .news-desk-doctrine-card__footer,.news-desk-columns--administration .news-desk-doctrine-card__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 12px}
108
+ .news-desk-columns--administration .news-desk-doctrine-card__header>div{display:grid;gap:2px}
109
+ .news-desk-columns--administration .news-desk-doctrine-card h3{margin:0;font-size:20px;font-weight:700;line-height:1.2}
110
+ .news-desk-columns--administration .news-desk-doctrine-card__header>span,.news-desk-columns--administration .news-desk-doctrine-card__footer>span,.news-desk-columns--administration .news-desk-doctrine-card__actions>span{font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--administration-quiet)}
111
+ .news-desk-columns--administration .news-desk-doctrine-card__footer{padding-top:0;border-top:0}
112
+ .news-desk-columns--administration .news-desk-doctrine-card__actions{justify-content:flex-end}
113
+ .news-desk-columns--administration .news-desk-doctrine-card__field{display:grid;gap:6px;min-width:0}
114
+ .news-desk-columns--administration .news-desk-doctrine-card__field>span{font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--administration-quiet)}
115
+ .news-desk-columns--administration :is(.news-desk-doctrine-card__field,.news-desk-merge-panel) :is(input:not([type=checkbox]),select,textarea){width:100%;max-width:100%;min-height:40px;padding:8px 10px;border:1px solid var(--org-line);background:transparent;color:var(--administration-strong);font:400 14px/1.5 Newsroom UI,system-ui,sans-serif;letter-spacing:0}
116
+ .news-desk-columns--administration .news-desk-doctrine-card__field textarea{min-height:96px;max-height:360px;field-sizing:content;resize:vertical}
117
+ .news-desk-columns--administration .news-desk-doctrine-card__field :is(input,select,textarea):focus-visible{outline:2px solid var(--administration-strong);outline-offset:-1px}
118
+ .news-desk-columns--administration .news-desk-doctrine-card__field:has(input[type=checkbox]){grid-auto-flow:column;justify-content:start;align-items:center;gap:12px}
119
+ .news-desk-columns--administration .news-desk-doctrine-card__field input[type=checkbox]{width:18px;height:18px;margin:0;accent-color:var(--administration-strong)}
120
+ .news-desk-columns--administration details{border-top:1px solid var(--org-line);padding-top:12px}
121
+ .news-desk-columns--administration details[open]{display:grid;gap:16px}
122
+ .news-desk-columns--administration [data-news-desk-admin-panel]{display:grid;gap:24px;align-content:start}
123
+ .news-desk-columns--administration [data-news-desk-admin-panel]>.news-desk-doctrine-card{padding-top:24px;border-top:1px solid var(--org-line)}
124
+ .news-desk-columns--administration summary{cursor:pointer;min-height:32px;display:flex;align-items:center;font:600 12px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--administration-quiet)}
125
+ .news-desk-columns--administration details[open] summary{margin-bottom:0}
126
+ .news-desk-columns--administration :is(.news-desk-doctrine-card__actions button,.news-desk-user-row__actions button,.news-desk-primary-action-button,.news-desk-merge-panel button){display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;padding:4px 12px;border:1px solid var(--org-line);background:transparent;color:var(--administration-strong);font:600 12px/1.2 Newsroom UI,system-ui,sans-serif;letter-spacing:.05em;text-transform:uppercase;white-space:nowrap;cursor:pointer}
127
+ .news-desk-columns--administration .news-desk-doctrine-card__actions button:last-child:not(:only-child),.news-desk-columns--administration .news-desk-doctrine-card__actions button:only-child,.news-desk-columns--administration .news-desk-primary-action-button,.news-desk-columns--administration [data-news-desk-merge-confirm]{background:transparent;border:2px solid var(--org-ink);color:var(--org-ink);font-weight:700}
128
+ .news-desk-columns--administration :is(.news-desk-doctrine-card__actions,.news-desk-user-row__actions) button:disabled,.news-desk-columns--administration .news-desk-primary-action-button:disabled{background:transparent;border-color:var(--org-line);color:var(--administration-quiet);opacity:.6;cursor:default}
129
+ .news-desk-columns--administration .news-desk-administration-category-layout{display:grid;grid-template-columns:minmax(0,1fr);gap:24px;align-items:start}
130
+ .news-desk-columns--administration .news-desk-administration-category-list{display:grid;gap:0;min-width:0;max-height:min(46dvh,420px);overflow:auto;padding:0;border:1px solid var(--org-line)}
131
+ .news-desk-columns--administration .news-desk-administration-category-list>.story-label{padding:12px 12px 6px;border-top:1px solid var(--org-line)}
132
+ .news-desk-columns--administration .news-desk-administration-category-list>.story-label:first-child{border-top:0}
133
+ .news-desk-columns--administration .news-desk-administration-category-list>.news-desk-primary-action-button{margin:12px}
134
+ .news-desk-columns--administration .news-desk-administration-category-list>.category-steering-empty{padding:12px;border:0}
135
+ .news-desk-columns--administration .news-desk-administration-category-button{display:grid;gap:2px;width:100%;padding:10px 12px;border:0;border-left:3px solid transparent;border-top:1px solid var(--org-line);background:transparent;text-align:left;color:inherit;cursor:pointer;min-height:44px}
136
+ .news-desk-columns--administration .news-desk-administration-category-list>.story-label+.news-desk-administration-category-button{border-top:0}
137
+ .news-desk-columns--administration .news-desk-administration-category-button[data-selected=true]{border-left-color:var(--administration-strong);background:color-mix(in srgb,var(--administration-strong) 7%,transparent)}
138
+ .news-desk-columns--administration .news-desk-administration-category-button span{font:700 15px/1.25 Newsroom Display,Georgia,serif;color:var(--administration-strong)}
139
+ .news-desk-columns--administration .news-desk-administration-category-button small{font:400 12px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:0;text-transform:none;color:var(--administration-quiet)}
140
+ .news-desk-columns--administration .news-desk-administration-category-doctrine{display:grid;gap:0;margin-top:16px;padding-top:24px;border-top:1px solid var(--org-line)}
141
+ .news-desk-columns--administration .news-desk-user-list{display:grid;gap:0;max-width:960px}
142
+ .news-desk-columns--administration .news-desk-user-row{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;align-items:start;padding:16px 0;border-top:1px solid var(--org-line)}
143
+ .news-desk-columns--administration .news-desk-user-row:first-child{padding-top:0;border-top:0}
144
+ .news-desk-columns--administration .news-desk-user-row>div:first-child{display:grid;gap:6px;min-width:0}
145
+ .news-desk-columns--administration .news-desk-user-row header{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;justify-content:flex-start}
146
+ .news-desk-columns--administration .news-desk-user-row strong{font-size:18px;font-weight:700;line-height:1.2}
147
+ .news-desk-columns--administration .news-desk-user-row header span{font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--administration-quiet)}
148
+ .news-desk-columns--administration .news-desk-user-row p{margin:0;font:400 13px/1.5 Newsroom UI,system-ui,sans-serif;letter-spacing:0;text-transform:none;color:var(--administration-quiet);overflow-wrap:anywhere}
149
+ .news-desk-columns--administration .news-desk-user-row__actions{display:flex;flex-wrap:wrap;gap:8px}
150
+ .news-desk-columns--administration .news-desk-chip-row{display:flex;flex-wrap:wrap;gap:6px}
151
+ .news-desk-columns--administration .news-desk-chip-row span{padding:2px 8px;border:1px solid var(--org-line);font:400 12px/1.5 Newsroom UI,system-ui,sans-serif;letter-spacing:0;text-transform:none;color:var(--administration-quiet);overflow-wrap:anywhere}
152
+ .news-desk-columns--administration .news-desk-merge-panel{display:grid;gap:16px;margin-top:24px;padding:16px;border:1px solid var(--org-line)}
153
+ .news-desk-columns--administration .category-steering-alert{padding:10px 12px;border:1px solid var(--org-line);border-left:3px solid var(--administration-strong);font-size:13px;line-height:1.5;color:var(--administration-strong)}
154
+ .news-desk-columns--administration .category-steering-empty{padding:16px;border:1px dashed var(--org-line);font:400 14px/1.5 Newsroom UI,system-ui,sans-serif;letter-spacing:0;text-transform:none;color:var(--administration-quiet)}
155
+ @container (min-width:600px){
156
+ .news-desk-columns--administration .news-desk-settings-nav{grid-template-columns:repeat(4,minmax(0,1fr))}
157
+ .news-desk-columns--administration .news-desk-settings-nav a span{display:block}
158
+ .news-desk-columns--administration .news-desk-user-row{grid-template-columns:minmax(0,1fr) auto}
159
+ .news-desk-columns--administration .news-desk-user-row__actions{justify-content:flex-end;max-width:260px}
160
+ }
161
+ @container (min-width:900px){
162
+ .news-desk-columns--administration .news-desk-settings-shell{grid-template-columns:240px minmax(0,1fr);gap:32px}
163
+ .news-desk-columns--administration .news-desk-settings-nav{grid-template-columns:minmax(0,1fr);gap:0;position:sticky;top:0}
164
+ .news-desk-columns--administration .news-desk-settings-nav a{border-width:0 0 1px;padding:12px 8px}
165
+ .news-desk-columns--administration .news-desk-settings-nav a[data-active=true]{box-shadow:inset 3px 0 0 var(--administration-strong);border-bottom-color:var(--org-line)}
166
+ .news-desk-columns--administration .news-desk-administration-category-layout{grid-template-columns:minmax(220px,280px) minmax(0,1fr)}
167
+ .news-desk-columns--administration .news-desk-administration-category-list{max-height:none}
168
+ }
169
+ .newsroom-assignment-detail-desk{display:grid;gap:4px;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--org-muted)}
170
+ .newsroom-assignment-detail-error{margin:0;text-transform:none;letter-spacing:0;color:var(--org-ink);font-weight:700}
171
+ [data-slot=select-content],[data-slot=select-item],[data-slot=dropdown-menu-item],[data-slot=dropdown-menu-content],[role=option],[role=menuitem],[role=listbox],[role=menu]{border-radius:0 !important}
172
+ .newsroom-desk-group-heading{margin:16px 0 8px;font:700 12px Newsroom Display,Georgia,serif;text-transform:uppercase;letter-spacing:.08em;border-bottom:1px solid var(--org-ink);padding-bottom:4px}
173
+ .newsroom-desk-group-heading small{font:400 11px Newsroom UI,system-ui;text-transform:none;letter-spacing:0;color:var(--org-muted);margin-left:8px}
174
+ .newsroom-desk-legacy-value{display:block;font-size:11px;color:var(--org-muted);overflow-wrap:anywhere}
175
+ .newsroom-article-view-toggle{display:inline-flex}
176
+ .newsroom-article-front-matter>summary{cursor:pointer;font-size:14px;font-weight:600;padding:6px 0;list-style-position:inside}
177
+ .newsroom-article-front-matter[open]>summary{margin-bottom:6px}
178
+ .story-trail{container-type:inline-size;display:grid;gap:20px}
179
+ .story-trail-section h3{margin:0 0 8px;font-size:12px;text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--org-ink);padding-bottom:4px}
180
+ .story-trail-section h3 small{font-weight:400;color:var(--org-muted);margin-left:6px}
181
+ .story-trail-list,.story-trail-events{list-style:none;margin:0;padding:0;display:grid;gap:12px}
182
+ .story-trail-list>li{border-bottom:1px solid var(--org-line);padding-bottom:12px;display:grid;gap:4px}
183
+ .story-trail-list>li:last-child{border-bottom:0}
184
+ .story-trail-events{gap:2px;padding-left:12px;border-left:2px solid var(--org-line);font-size:13px}
185
+ .story-trail-row{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline}
186
+ .story-trail-meta,.story-trail-empty{font-size:12px;color:var(--org-muted);margin:0}
187
+ .story-trail-chip{font-size:11px;border:1px solid var(--org-ink);padding:0 6px;text-transform:uppercase;letter-spacing:.04em}
188
+ .story-trail-message pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0 0;font-size:12px;max-height:320px;overflow:auto}
189
+ .story-trail-message summary{cursor:pointer;font-size:13px}
190
+ @container (min-width:900px){.story-trail{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:32px;align-items:start}}
191
+ .newsroom-article-older-version{margin:0 0 12px;padding:8px 12px;border:1px solid var(--org-ink);border-left-width:5px;font-weight:600}
192
+ .newsroom-article-link-button{background:none;border:0;padding:0;font:inherit;text-decoration:underline;cursor:pointer;color:inherit}
193
+ .story-trail-list>li[data-story-reference-focused]{border-left:3px solid var(--org-ink);padding-left:10px}
194
+ .story-trail-list>li[data-story-trail-feed-reference]{border-left:1px solid var(--org-line);padding-left:10px}
195
+ .wiki{display:grid;grid-template-columns:minmax(200px,260px) minmax(0,1fr);gap:24px;align-items:start}
196
+ .wiki-sidebar{display:grid;gap:8px;position:sticky;top:0;max-height:100vh;overflow:auto;border-right:1px solid var(--org-line);padding-right:16px}
197
+ .wiki-home{text-align:left;background:none;border:0;border-bottom:1px solid var(--org-ink);padding:4px 0;font:inherit;font-weight:600;cursor:pointer;color:var(--org-ink)}
198
+ .wiki-home[aria-current="page"]{text-decoration:underline;text-underline-offset:4px}
199
+ .wiki-search,.wiki-kind{font:inherit;border:1px solid var(--org-ink);border-radius:0;padding:4px 8px;background:transparent;color:var(--org-ink)}
200
+ .wiki-sidebar-list,.wiki-concept-list{list-style:none;margin:0;padding:0;display:grid;gap:6px}
201
+ .wiki-sidebar-list a{color:var(--org-ink);text-decoration:none}
202
+ .wiki-sidebar-list a[aria-current="page"]{font-weight:700;text-decoration:underline;text-underline-offset:4px}
203
+ .wiki-main{min-width:0;max-width:820px}
204
+ .wiki-page h1{margin:0 0 6px}
205
+ .wiki-page h2{font-size:12px;text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--org-ink);padding-bottom:4px;margin:24px 0 8px}
206
+ .wiki-page h3{font-size:13px;margin:12px 0 4px}
207
+ .wiki-page-tools{display:flex;justify-content:flex-end;margin-bottom:8px}
208
+ .wiki-tool-link{background:none;border:0;padding:0;font:inherit;color:var(--org-ink);text-decoration:underline;text-underline-offset:3px;cursor:pointer}
209
+ .wiki-link,.wiki-concept-list a{color:var(--org-ink);text-decoration:underline;text-underline-offset:3px}
210
+ .wiki-concept-list li{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline}
211
+ .wiki-chip{font-size:11px;border:1px solid var(--org-ink);padding:0 6px;text-transform:uppercase;letter-spacing:.04em}
212
+ .wiki-meta,.wiki-empty{font-size:12px;color:var(--org-muted);margin:0}
213
+ @media (max-width:760px){.wiki{grid-template-columns:minmax(0,1fr)}.wiki-sidebar{position:static;max-height:240px;border-right:0;padding-right:0}}
214
+ .organizer-board-body .kb-issue-detail [data-markus-preview] a,.organizer-board-body .kb-issue-detail .wiki-link{display:inline;min-height:0;padding:0}
215
+ .newsroom-article-front-matter-fields{display:grid;gap:10px;max-width:640px}
216
+ .newsroom-article-front-matter-fields label{display:grid;gap:4px;font-size:13px;font-weight:600}
217
+ .newsroom-article-summary{border-bottom:1px solid var(--org-line);margin-bottom:12px;padding-bottom:10px}
218
+ .newsroom-article-subtitle{margin:0 0 4px;font-size:18px;font-weight:600;line-height:1.3}
219
+ .newsroom-article-blurb{margin:0;font-size:14px;line-height:1.45}
220
+ .organizer-search{display:flex;align-items:center;gap:6px;border-bottom:1px solid var(--org-ink);padding:2px 0;min-width:140px;max-width:240px}
221
+ .organizer-search input{font:inherit;font-size:13px;border:0;background:transparent;color:var(--org-ink);outline:0;width:100%;padding:2px 0}
222
+ .organizer-search input:focus-visible{text-decoration:underline;text-underline-offset:4px}
223
+ @media (max-width:900px){.organizer-search{min-width:0;max-width:120px}}
224
+ .taxonomy-view-switch{display:flex;gap:20px;border-bottom:1px solid var(--org-line);margin-bottom:12px}
225
+ .taxonomy-view-switch a{color:var(--org-ink);text-decoration:none;padding:4px 0;border-bottom:2px solid transparent}
226
+ .taxonomy-view-switch a[aria-current="page"]{font-weight:700;border-bottom-color:var(--org-ink)}
227
+ .news-desk-settings-shell[data-settings-nav="hidden"]{display:block}
228
+ .news-desk-columns--taxonomy{display:block;margin:0;--administration-quiet:var(--org-muted);--administration-strong:var(--org-ink)}
229
+ .news-desk-columns--taxonomy .category-steering-section--lead>.category-steering-section__header{display:flex}
230
+ .news-desk-columns--taxonomy .news-desk-main-column{display:grid;grid-template-columns:minmax(220px,300px) minmax(0,1fr);gap:0 40px;align-items:start;grid-column:auto;container-type:inline-size}
231
+ .news-desk-columns--taxonomy .news-desk-desk-list{display:grid;grid-template-columns:minmax(0,1fr);gap:0}
232
+ .news-desk-columns--taxonomy .news-desk-desk-card{border-top:0;border-bottom:1px solid var(--org-line);border-left:3px solid transparent;padding:12px 0 12px 12px;gap:2px;background:none}
233
+ .news-desk-columns--taxonomy .news-desk-desk-card[data-selected="true"]{border-left-color:var(--org-ink);background:none;padding:12px 0 12px 12px}
234
+ .news-desk-columns--taxonomy .news-desk-desk-card[data-selected="true"] strong{text-decoration:underline;text-underline-offset:4px}
235
+ .news-desk-columns--taxonomy .news-desk-desk-card>span{font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;color:var(--org-muted)}
236
+ .news-desk-columns--taxonomy .news-desk-desk-card strong{font:700 18px/1.2 Newsroom Display,Georgia,serif;color:var(--org-ink)}
237
+ .news-desk-columns--taxonomy .news-desk-desk-card p{font:400 13px/1.4 Newsroom UI,system-ui,sans-serif;color:var(--org-ink);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
238
+ .news-desk-columns--taxonomy .news-desk-desk-card small{font:400 12px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:0;text-transform:none;color:var(--org-muted)}
239
+ .news-desk-columns--taxonomy .news-desk-desk-detail{margin:0;display:grid;gap:24px;min-width:0}
240
+ .news-desk-columns--taxonomy .news-desk-topic-detail__header{display:grid;gap:12px;border-bottom:3px double var(--org-ink);padding-bottom:16px}
241
+ .news-desk-columns--taxonomy .news-desk-topic-detail__header h3{margin:0;font:700 30px/1.15 Newsroom Display,Georgia,serif}
242
+ .news-desk-columns--taxonomy .news-desk-topic-detail__header>div>span{font:400 13px/1.4 Newsroom UI,system-ui,sans-serif;color:var(--org-muted)}
243
+ .news-desk-columns--taxonomy .news-desk-topic-detail__header dl{display:flex;flex-wrap:wrap;gap:8px 32px;margin:0}
244
+ .news-desk-columns--taxonomy .news-desk-topic-detail__header dl>div{display:flex;flex-direction:column-reverse;gap:2px}
245
+ .news-desk-columns--taxonomy .news-desk-topic-detail__header dt{font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--org-muted)}
246
+ .news-desk-columns--taxonomy .news-desk-topic-detail__header dd{margin:0;font:700 22px/1.1 Newsroom Display,Georgia,serif}
247
+ .news-desk-columns--taxonomy .news-desk-desk-detail__grid{grid-template-columns:minmax(0,1fr);gap:32px}
248
+ @container (min-width:900px){.news-desk-columns--taxonomy .news-desk-desk-detail__grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}}
249
+ .news-desk-columns--taxonomy .category-steering-category-card{display:grid;gap:14px;border:0;padding:0;background:none}
250
+ .news-desk-columns--taxonomy .category-steering-category-card>header,.news-desk-columns--taxonomy .category-steering-category-card>footer{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:8px 12px}
251
+ .news-desk-columns--taxonomy .category-steering-category-card label{display:grid;gap:6px}
252
+ .news-desk-columns--taxonomy .category-steering-category-card label>span{font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--org-muted)}
253
+ .news-desk-columns--taxonomy .category-steering-category-card :is(input,textarea){width:100%;min-height:40px;padding:8px 10px;border:1px solid var(--org-line);border-radius:0;background:transparent;color:var(--org-ink);font:400 14px/1.5 Newsroom UI,system-ui,sans-serif}
254
+ .news-desk-columns--taxonomy .news-desk-desk-subtopics{border-top:1px solid var(--org-line);padding-top:16px}
255
+ .news-desk-columns--taxonomy .news-desk-chip-row{display:flex;flex-wrap:wrap;gap:8px}
256
+ .news-desk-columns--taxonomy .news-desk-chip-row span{border:1px solid var(--org-ink);padding:2px 8px;font:400 13px/1.4 Newsroom UI,system-ui,sans-serif;border-radius:0}
257
+ .news-desk-columns--taxonomy .news-desk-rail-column{grid-column:auto;padding:0;margin-top:32px;border-top:1px solid var(--org-ink);padding-top:16px}
258
+ .news-desk-columns--taxonomy .news-desk-ledger-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:24px}
259
+ .news-desk-columns--taxonomy .news-desk-ledger-item{display:grid;gap:4px;border:0;padding:0}
260
+ .news-desk-columns--taxonomy .news-desk-ledger-item header{display:flex;flex-wrap:wrap;justify-content:space-between;gap:4px 8px}
261
+ .news-desk-columns--taxonomy .news-desk-ledger-item p{margin:0;font:400 13px/1.45 Newsroom UI,system-ui,sans-serif}
262
+ @media (max-width:900px){.news-desk-columns--taxonomy .news-desk-main-column{grid-template-columns:minmax(0,1fr);gap:24px}}
263
+ .news-desk-columns--taxonomy :is(.category-steering-category-card>header span,.category-steering-category-card>footer span,.news-desk-desk-detail__grid span,.category-steering-subcategory-list__label,.news-desk-ledger-item header span){color:var(--org-ink);opacity:1;font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase}
264
+ .news-desk-columns--taxonomy .category-steering-category-card>header strong{font:700 12px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase;color:var(--org-ink)}
265
+ .news-desk-columns--taxonomy .category-steering-category-card button{display:inline-flex;align-items:center;justify-content:center;min-height:36px;padding:4px 12px;border:2px solid var(--org-ink);border-radius:0;background:transparent;color:var(--org-ink);font:700 12px/1.2 Newsroom UI,system-ui,sans-serif;letter-spacing:.05em;text-transform:uppercase;cursor:pointer}
266
+ .news-desk-columns--taxonomy .category-steering-category-card button:disabled{border-color:var(--org-line);opacity:.6;cursor:default}
267
+ .news-desk-columns--taxonomy .news-desk-rail-column .category-steering-section__header{border-bottom:0;padding-bottom:4px}
268
+ .organizer-account-chooser{padding:8px 12px;border-bottom:1px solid var(--org-line);display:grid;gap:6px}
269
+ .organizer-account-chooser ul{list-style:none;margin:0;padding:0;display:grid}
270
+ .organizer-account-chooser li button{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;text-align:left;background:none;border:0;border-left:3px solid transparent;padding:6px 8px;font:inherit;color:var(--org-ink);cursor:pointer}
271
+ .organizer-account-chooser li button[data-selected]{border-left-color:var(--org-ink);font-weight:700}
272
+ .organizer-account-chooser li button:disabled{opacity:.6;cursor:default}
273
+ .organizer-account-chooser li button span{display:grid;gap:1px}
274
+ .organizer-account-chooser li button small,.organizer-account-chooser-note{font-size:12px;color:var(--org-ink);margin:0}
275
+ .organizer-account-chooser-heading{margin:0;font:600 11px/1.4 Newsroom UI,system-ui,sans-serif;letter-spacing:.07em;text-transform:uppercase}
276
+ .organizer-account-chooser-link{color:var(--org-ink);text-decoration:underline;text-underline-offset:3px}