@actioneer/ads 1.0.8 → 1.0.10

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 (64) hide show
  1. package/DESIGN.md +125 -50
  2. package/README.md +31 -7
  3. package/components/answer.js +1 -1
  4. package/components/assistant-ui/composer-harness.js +14 -10
  5. package/components/body-page-header.d.ts +9 -4
  6. package/components/body-page-header.js +10 -10
  7. package/components/breadcrumbs.js +15 -15
  8. package/components/button.d.ts +16 -1
  9. package/components/button.js +7 -4
  10. package/components/card.js +18 -18
  11. package/components/chart.d.ts +40 -4
  12. package/components/chart.js +824 -628
  13. package/components/chip.d.ts +2 -1
  14. package/components/chip.js +49 -46
  15. package/components/collection-header.js +4 -4
  16. package/components/connector-card.js +30 -30
  17. package/components/date-range-picker.js +1 -1
  18. package/components/detail-list.d.ts +28 -0
  19. package/components/detail-list.js +52 -0
  20. package/components/drawer.d.ts +38 -15
  21. package/components/drawer.js +150 -86
  22. package/components/empty-state.d.ts +3 -2
  23. package/components/empty-state.js +22 -22
  24. package/components/field.d.ts +16 -0
  25. package/components/field.js +63 -42
  26. package/components/file-dropzone.d.ts +15 -4
  27. package/components/file-dropzone.js +100 -64
  28. package/components/filter-bar.d.ts +44 -0
  29. package/components/filter-bar.js +89 -0
  30. package/components/filters.d.ts +37 -0
  31. package/components/filters.js +129 -0
  32. package/components/index.d.ts +4 -0
  33. package/components/index.js +526 -510
  34. package/components/input-field.d.ts +1 -1
  35. package/components/input-field.js +24 -22
  36. package/components/modal.d.ts +2 -1
  37. package/components/modal.js +120 -106
  38. package/components/panel.js +26 -27
  39. package/components/popover.d.ts +6 -1
  40. package/components/popover.js +74 -64
  41. package/components/sidebar.d.ts +15 -0
  42. package/components/sidebar.js +166 -129
  43. package/components/sparkline.d.ts +30 -0
  44. package/components/sparkline.js +69 -0
  45. package/components/table.d.ts +8 -0
  46. package/components/table.js +357 -350
  47. package/components/tabs.d.ts +6 -1
  48. package/components/tabs.js +28 -31
  49. package/demo/components/add-to-artifact.js +6 -2
  50. package/examples/README.md +5 -2
  51. package/examples/call-logs-screen.tsx +381 -0
  52. package/examples/campaign-analysis-screen.tsx +180 -0
  53. package/examples/campaigns-screen.tsx +497 -0
  54. package/examples/guardrails-settings-screen.tsx +123 -0
  55. package/fonts/geist-variable.woff2 +0 -0
  56. package/fonts.css +12 -17
  57. package/lib/icons.js +3 -3
  58. package/llms.txt +162 -30
  59. package/manifest.json +11604 -7878
  60. package/package.json +1 -1
  61. package/styles.css +102 -59
  62. package/theme.css +81 -37
  63. package/fonts/aribau-grotesk-medium.woff2 +0 -0
  64. package/fonts/aribau-grotesk-regular.woff2 +0 -0
package/DESIGN.md CHANGED
@@ -60,41 +60,43 @@ colors:
60
60
  overlay: "color-mix(in srgb, var(--color-wg12) 50%, transparent)"
61
61
  typography:
62
62
  display:
63
- fontFamily: "\"Aribau Grotesk\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
63
+ fontFamily: "\"Season Mix\", serif"
64
64
  fontSize: "24px"
65
- fontWeight: 500
66
- lineHeight: "32px"
65
+ fontWeight: 400
66
+ lineHeight: "30px"
67
67
  title:
68
- fontFamily: "\"Aribau Grotesk\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
69
- fontSize: "18px"
70
- fontWeight: 500
71
- lineHeight: "24px"
68
+ fontFamily: "\"Season Mix\", serif"
69
+ fontSize: "20px"
70
+ fontWeight: 400
71
+ lineHeight: "28px"
72
72
  headline:
73
- fontFamily: "\"Aribau Grotesk\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
74
- fontSize: "16px"
75
- fontWeight: 500
76
- lineHeight: "22px"
73
+ fontFamily: "\"Season Mix\", serif"
74
+ fontSize: "18px"
75
+ fontWeight: 400
76
+ lineHeight: "26px"
77
77
  body:
78
- fontFamily: "\"Aribau Grotesk\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
79
- fontSize: "14px"
78
+ fontFamily: "\"Geist\", system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
79
+ fontSize: "16px"
80
80
  fontWeight: 400
81
- lineHeight: "20px"
81
+ lineHeight: "24px"
82
82
  label:
83
- fontFamily: "\"Aribau Grotesk\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
84
- fontSize: "12px"
83
+ fontFamily: "\"Geist\", system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif"
84
+ fontSize: "14px"
85
85
  fontWeight: 400
86
- lineHeight: "16px"
86
+ lineHeight: "20px"
87
87
  mono:
88
88
  fontFamily: "\"JetBrains Mono Variable\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", \"Courier New\", monospace"
89
- fontSize: "12px"
89
+ fontSize: "14px"
90
90
  fontWeight: 400
91
- lineHeight: "16px"
91
+ lineHeight: "20px"
92
92
  rounded:
93
93
  xs: "2px"
94
94
  sm: "4px"
95
95
  md: "6px"
96
96
  lg: "8px"
97
97
  xl: "8px"
98
+ card: "14px"
99
+ window: "18px"
98
100
  full: "9999px"
99
101
  spacing:
100
102
  xs: "4px"
@@ -179,16 +181,16 @@ components:
179
181
 
180
182
  TASTE.md puts it in one line: On a quiet, neutral ground, colour and motion are spent like money — rarely, deliberately, and only where the eye should go.
181
183
 
182
- ADS 1.0.8 is a library first and a look second. 323 components across 10 families (Primitives / utilities, Foundations, Form controls, Overlays, Data display, Feedback, Navigation, Layout, AI Elements, Chat) ship from one barrel, and every product that consumes them inherits the judgment encoded in 56 semantic tokens and 11 principles rather than re-deciding it per screen.
184
+ ADS 1.0.10 is a library first and a look second. 335 components across 10 families (Primitives / utilities, Foundations, Form controls, Overlays, Data display, Feedback, Navigation, Layout, AI Elements, Chat) ship from one barrel, and every product that consumes them inherits the judgment encoded in 56 semantic tokens and 17 principles rather than re-deciding it per screen.
183
185
 
184
186
  The ground is warm grey and the ink is warm near-black. One hue is spent by default, the Studio teal CTA family, and four more carry semantic status. Everything else stays neutral. A consumer product may narrow that further, and one already does: Baby Sentinel drops the teal and runs every action on ink.
185
187
 
186
188
  **Key characteristics:**
187
189
  - Warm-grey canvas, white sheet, quiet tiles. Every surface belongs to exactly one plane
188
- - One typeface at two real weights on a 5-step ladder (24px / 18px / 16px / 14px / 12px), with mono reserved for literal code
190
+ - One typeface at two real weights on a 5-step ladder (24px / 20px / 18px / 16px / 14px), with mono reserved for literal code
189
191
  - Fills and elevation separate surfaces roughly 75/25 against strokes
190
192
  - Every surface defaults to `shadow-0`; elevation means "you can act on this"
191
- - Radius runs 2px / 4px / 6px / 8px / 8px, with a pill reserved for chips and avatars
193
+ - Radius runs 2px / 4px / 6px / 8px / 8px / 14px / 18px, with a pill reserved for chips and avatars
192
194
 
193
195
  ## Colors
194
196
 
@@ -322,30 +324,30 @@ The warm-grey ground everything else stands on.
322
324
 
323
325
  ### Named rules
324
326
 
325
- **The Fills Over Strokes Rule.** Delineate SURFACES (cards, panels, sidebars, page regions) with color contrast, elevation, and spacing — not borders. Keep no more than two persistent neutral layers in one composition: panels establish hierarchy; embedded items express state. Aim for ~75% shadow/fill, 25% strokes: form controls and floating menus keep a subtle border so they read as interactive; surfaces do not.
327
+ **The Fills Over Strokes Rule.** Delineate SURFACES (cards, panels, sidebars, page regions) with color contrast, elevation, and spacing — not decorative borders. Keep no more than two persistent neutral layers in one composition: panels establish hierarchy; embedded items express state. Aim for ~75% shadow/fill, 25% strokes: form controls and floating menus keep a subtle border so they read as interactive. Two kinds of 1px WG4 line follow opposite rules (the window panel style, user-mandated 2026-11-12): STRUCTURAL hairlines that split the page into its parts — under a header bar, under a DrawerHeader and above a DrawerFooter, between the page and an attached Drawer, between the sidebar and the page — run full width, wall to wall; ROW dividers between items inside one region stay inset to the content.
326
328
 
327
329
  ## Typography
328
330
 
329
- **Display font:** Aribau Grotesk
330
- **Body font:** Aribau Grotesk
331
+ **Display font:** Season Mix
332
+ **Body font:** Geist
331
333
  **Mono font:** JetBrains Mono Variable
332
334
 
333
- **Character:** one grotesk doing everything at two real weights. Hierarchy comes from size, ink strength and spacing. There is no second family and no heavy weight to reach for.
335
+ **Character:** Season Mix sets heading roles (page, drawer, modal, section and chart titles); Geist carries body, labels, controls, rows and data values. Two real weights; hierarchy comes from size, ink strength and spacing, never a heavy weight.
334
336
 
335
337
  ### Hierarchy
336
338
 
337
- - **Display** (500, 24px / 32px): page-level moments only.
338
- - **Title** (500, 18px / 24px): page titles.
339
- - **Headline** (500, 16px / 22px): emphasized body and section headings.
340
- - **Body** (400, 14px / 20px): the default, all reading text and control labels.
341
- - **Label** (400, 12px / 16px): the floor: captions, secondary text, table cells.
342
- - **Mono** (400, 12px / 16px): literal source code, and nothing else.
339
+ - **Display** (400, 24px / 30px): page-level moments only.
340
+ - **Title** (400, 20px / 28px): page titles.
341
+ - **Headline** (400, 18px / 26px): emphasized body and section headings.
342
+ - **Body** (400, 16px / 24px): the default, all reading text and control labels.
343
+ - **Label** (400, 14px / 20px): the floor: captions, secondary text, table cells.
344
+ - **Mono** (400, 14px / 20px): literal source code, and nothing else.
343
345
 
344
346
  ### Named rules
345
347
 
346
- **The One Face Rule.** One face (Aribau Grotesk), two real weights, a five-step ladder.
348
+ **The Two Face Rule.** Two faces with fixed jobs (Season Mix for headings, Geist for everything else), two real weights, one scale.
347
349
 
348
- **The 12px Floor Rule.** Nothing renders below `--text-s` (12px). Density comes from spacing and layout, never from shrinking type.
350
+ **The 14px Floor Rule.** Nothing renders below `--text-s` (14px). Density comes from spacing and layout, never from shrinking type.
349
351
 
350
352
  ## Layout
351
353
 
@@ -353,7 +355,7 @@ The shell owns the frame and screens own their content. ADS targets landscape ta
353
355
 
354
356
  ### Named rules
355
357
 
356
- **The Shell Owns the Frame Rule.** The demo shell applies the screen frame (inset on top/right/bottom, flush-left against the sidebar). Screens never add their own outer padding — a screen's root is flex h-full flex-col and fills the frame with Panel(s).
358
+ **The Shell Owns the Frame Rule.** The shell renders one app window (user-mandated 2026-11-12): a rounded-window (18px) surface with a 1px WG4 edge and shadow-window, inset 12px on the bg-surface (WG3) backdrop. The sidebar (bg-background) and the White PageBody sit inside it, split by one full-height WG4 hairline. Page headers are BodyPageHeader bars — 64px, 20px (text-xl) Medium title, actions trailing, full-width hairline below. A record's detail panel is an inline Drawer attached to the page: flush beside the content, one full-height hairline, no gap, radius or shadow. Screens never add their own outer padding, radius, card or shadow — a screen's root is flex h-full flex-col and fills the page.
357
359
 
358
360
  **The One Thing Per Screen Rule.** A screen carries ONLY what the user needs to move forward: one title, the inputs, one primary action. Page, section, panel, modal, dialog, drawer, and popover headers are title-only by default; add a description only when the user explicitly asks for one. Never maximalist.
359
361
 
@@ -361,23 +363,26 @@ The shell owns the frame and screens own their content. ADS targets landscape ta
361
363
 
362
364
  ## Elevation & Depth
363
365
 
364
- Flat by default with tonal layering. Depth comes from the plane system, warm grey behind and white in front. Five shadows only: `--shadow-0` (flat, every in-page surface), `--shadow-float` (toasts, toolbars, composer dock), `--shadow-overlay` (modals, dialogs, overlay drawers), and the two table scroll-hint smudges.
366
+ Flat by default with tonal layering. Depth comes from the plane system: a grey backdrop, one app window resting on it, white page inside. Eight shadows only: `--shadow-0` (flat — panels, rows, controls, the attached Drawer), `--shadow-float` (toasts, toolbars, composer dock), `--shadow-overlay` (modals, dialogs, overlay drawers), the two table scroll-hint smudges, `--shadow-card` (resting cards), `--shadow-window` (the app window) and `--shadow-nav-active` (the active sidebar item).
365
367
 
366
368
  ### Shadow vocabulary
367
369
 
368
370
  - **0** (`--shadow-0`): `none`
371
+ - **card** (`--shadow-card`): `0 1px 2px rgb(32 32 32 / 0.04), 0 8px 24px -12px rgb(32 32 32 / 0.10)`
369
372
  - **float** (`--shadow-float`): `0 0 0 1px rgb(32 32 32 / 0.04), 0 2px 4px rgb(32 32 32 / 0.04), 0 8px 20px -4px rgb(32 32 32 / 0.10)`
373
+ - **nav-active** (`--shadow-nav-active`): `0 1px 2px rgb(32 32 32 / 0.08), 0 4px 12px -4px rgb(32 32 32 / 0.14)`
370
374
  - **overlay** (`--shadow-overlay`): `0 0 0 1px rgb(32 32 32 / 0.04), 0 4px 8px rgb(32 32 32 / 0.04), 0 16px 36px -8px rgb(32 32 32 / 0.14)`
371
375
  - **table-smudge-left** (`--shadow-table-smudge-left`): `8px 0 24px -14px rgb(32 32 32 / 0.05)`
372
376
  - **table-smudge-right** (`--shadow-table-smudge-right`): `-8px 0 24px -14px rgb(32 32 32 / 0.05)`
377
+ - **window** (`--shadow-window`): `0 1px 2px rgb(32 32 32 / 0.05), 0 24px 48px -24px rgb(32 32 32 / 0.18)`
373
378
 
374
379
  ### Named rules
375
380
 
376
- **The Elevation Is Actionability Rule.** ADS has five shadow tokens: shadow-0 (flat, the default), shadow-float, shadow-overlay, and the two table scroll-hint smudges. Only a layer that floats above the page may carry float or overlay; everything in the page is flat.
381
+ **The Elevation Is Actionability Rule.** ADS has eight shadow tokens: shadow-0 (flat, the default), shadow-float, shadow-overlay, the two table scroll-hint smudges, the two resting-surface lifts shadow-card and shadow-window, and shadow-nav-active for the active sidebar item (user-mandated 2026-11-12). Only a layer that floats above the page may carry float or overlay; resting cards (Card, stat/chart cards, ConnectorCard) carry shadow-card with rounded-card (14px) and a 1px WG4 edge; the app window carries shadow-window with rounded-window (18px); everything else in the page is flat.
377
382
 
378
383
  ## Shapes
379
384
 
380
- Soft geometry on a 5-step ladder: `xs` 2px, `sm` 4px, `md` 6px, `lg` 8px, `xl` 8px, plus a pill at `full` (9999px) for chips and avatars. Tailwind's larger aliases (`2xl`, `3xl`) collapse onto the top step, so a stray `rounded-3xl` cannot exceed it.
385
+ Soft geometry on a 7-step ladder: `xs` 2px, `sm` 4px, `md` 6px, `lg` 8px, `xl` 8px, `card` 14px, `window` 18px, plus a pill at `full` (9999px) for chips and avatars. `card` and `window` are surface radii for resting cards and the app window only. Tailwind's larger aliases (`2xl`, `3xl`) collapse onto the `lg` control step, so a stray `rounded-3xl` cannot exceed it.
381
386
 
382
387
  ### Named rules
383
388
 
@@ -385,7 +390,7 @@ Soft geometry on a 5-step ladder: `xs` 2px, `sm` 4px, `md` 6px, `lg` 8px, `xl` 8
385
390
 
386
391
  ## Components
387
392
 
388
- The catalog is the contract: 323 components, 10 hooks and 98 utilities behind one barrel (`@actioneer/ads`). Import exact, unaliased names. Native elements are for semantic structure only: `<div>`, `<form>`, `<main>`, `<section>`. Never recreate a visual control.
393
+ The catalog is the contract: 335 components, 10 hooks and 98 utilities behind one barrel (`@actioneer/ads`). Import exact, unaliased names. Native elements are for semantic structure only: `<div>`, `<form>`, `<main>`, `<section>`. Never recreate a visual control.
389
394
 
390
395
  ### Sourcing a component
391
396
 
@@ -513,9 +518,9 @@ Hand-rolling a visual control, or importing a UI library at a call site, is neve
513
518
 
514
519
  **The ADS First Rule.** Every UI decision stays synchronized with ADS. Search the full catalog before deciding a component is missing, and use the exact exported name from the public barrel. When the barrel genuinely has no primitive, the sanctioned fallback is a shadcn/ui component vendored into src/components/ui through the repo's components.json recipe — never a hand-rolled control and never a UI library imported at a call site. Enforced by `npm run lint:ads`.
515
520
 
516
- **The Back Leads Rule.** A page-level Back control sits immediately before the page title in the leading header cluster. It is icon-only with an accessible label and never appears on the right with forward/page actions.
521
+ **The Back Leads Rule.** A record or drill-down page (a campaign inside Voice campaigns, a connector inside Data Integrations) is titled by its Breadcrumbs trail — parent / record at 18px, the header-bar title size, in the 64px header bar — with no back arrow and no repeated H1; the page's one primary action sits at the right end of the same row. A standalone page-level Back control, when one is needed outside a hierarchy, sits immediately before the title and never on the right.
517
522
 
518
- **The Tabs Lead, Search Trails Rule.** When tabs and a search field control the same content, they share one responsive control row: the tab list leads with its triggers left-aligned at their natural width over a baseline that fades from WG3 to transparent, and search sits at the trailing edge. Stack only when the container cannot fit both.
523
+ **The Tabs Lead, Search Trails Rule.** When tabs and a search field control the same content, they share one responsive control row: the tab list leads with its triggers left-aligned at their natural width over a baseline that fades from WG3 to transparent, and search sits at the trailing edge. On collection screens the trailing controls are icon triggers — SearchDisclosure (expands into the field) then Filters — and tab labels are 16px with a quieter count after each label.
519
524
 
520
525
  ## Do's and Don'ts
521
526
 
@@ -526,8 +531,12 @@ Every line below is a `do` or `dont` recorded on a principle in `ads-manifest.js
526
531
  - **Do** establish hierarchy with at most two persistent layers: bg-background for the screen ground and bg-card/bg-elevated for the primary surface. *(fills-over-strokes)*
527
532
  - **Do** inside a white Panel or Card, keep rows and items transparent at rest; group them with spacing or inset dividers, use bg-muted for hover/selection, and reserve bg-surface for one continuous semantic recess such as an editor or table tray. *(fills-over-strokes)*
528
533
  - **Do** use border-border for outline buttons and lightweight action rows; reserve border-input for form controls (input, select, textarea, checkbox, radio, segmented-control track) and floating menus (dropdown, popover, listboxes). *(fills-over-strokes)*
529
- - **Do** keep strokes on focus rings, the outline Button/Badge/Chip variants, Kbd, Divider, the Tree connector rail, and functional color swatches. *(fills-over-strokes)*
530
- - **Do** keep cards, panels, chips, buttons, tabs, fields, thumbs, and other in-page surfaces at shadow-0; separate them with fill, border, or spacing. *(elevation-is-actionability)*
534
+ - **Do** keep strokes on focus rings, the outline Button/Badge/Chip variants, Kbd, Divider, the Tree connector rail, functional color swatches, the WG4 edge of resting cards and the app window, and the structural hairlines. *(fills-over-strokes)*
535
+ - **Do** run structural hairlines edge to edge: the 64px header bar's bottom line, the DrawerHeader/DrawerFooter lines, the full-height line on an attached Drawer's leading edge, the sidebar's edge inside the app window. *(fills-over-strokes)*
536
+ - **Do** inset row dividers: put the padding on the container and border-b on each row, so the line stops at the content edge. *(fills-over-strokes)*
537
+ - **Do** keep panels, rows, chips, buttons, tabs, fields, thumbs, the attached Drawer and other in-page surfaces at shadow-0; separate them with fill, hairlines, or spacing. *(elevation-is-actionability)*
538
+ - **Do** give resting cards rounded-card + border border-border + shadow-card, and the app window rounded-window + border border-border + shadow-window. *(elevation-is-actionability)*
539
+ - **Do** lift the active sidebar item (its White fill) with shadow-nav-active; keep hover flat. *(elevation-is-actionability)*
531
540
  - **Do** use shadow-float for floating layers that sit over content: toasts, toolbars, the chat composer dock, and any menu or popover that needs lift beyond its border. *(elevation-is-actionability)*
532
541
  - **Do** use shadow-overlay for modals, dialogs, and overlay drawers, so a dialog always reads above a menu. *(elevation-is-actionability)*
533
542
  - **Do** treat motion as part of every ADS component import: preserve the component's built-in motion, and import any composition-level preset from the ADS root barrel. *(motion-follows-guide)*
@@ -538,8 +547,9 @@ Every line below is a `do` or `dont` recorded on a principle in `ads-manifest.js
538
547
  - **Do** import icons from the @/lib/icons registry; wrap a new Phosphor component with createIcon there before using it. *(icons-phosphor-regular)*
539
548
  - **Do** let icons default to size='1em' so they scale with text; size with a size prop or size-* class. *(icons-phosphor-regular)*
540
549
  - **Do** render weight='fill' for the active or selected state, and weight='bold' for the rare heavier stroke. *(icons-phosphor-regular)*
541
- - **Do** make a screen root flex h-full flex-col and fill the frame with Panel(s). *(shell-owns-the-frame)*
542
- - **Do** let one place (the shell) own the inset so every screen aligns to the rail. *(shell-owns-the-frame)*
550
+ - **Do** make a screen root flex h-full flex-col and fill the page with Panel(s); the shell renders them flush. *(shell-owns-the-frame)*
551
+ - **Do** let one place (the shell) own the window, its radius, edge and shadow. *(shell-owns-the-frame)*
552
+ - **Do** open record details in an attached Drawer beside the list, inside the same white page. *(shell-owns-the-frame)*
543
553
  - **Do** use rounded-dot for every indicator/status dot. *(square-status-dots)*
544
554
  - **Do** use an existing ADS component by its exact exported name from the package barrel; compose existing components for layout-only patterns. *(ads-first)*
545
555
  - **Do** search the whole catalog before concluding a component is missing — the documented specimens are a sample, not the inventory. *(ads-first)*
@@ -550,30 +560,71 @@ Every line below is a `do` or `dont` recorded on a principle in `ads-manifest.js
550
560
  - **Do** prefer more, smaller steps over dense ones; ask one thing at a time. *(minimal-information)*
551
561
  - **Do** derive instead of asking whenever possible (a name from a work email, the company from its domain). *(minimal-information)*
552
562
  - **Do** prefer clear text-only buttons; add an icon only when it communicates essential meaning the label cannot carry alone, or for a compact conventional icon-only control. *(minimal-information)*
553
- - **Do** render multi-step flows full-screen — a single content container that holds the inputs and its own Back/Continue is fine. *(minimal-information)*
563
+ - **Do** open a multi-step create task launched from a collection (New campaign) as a Modal over that list: title-only header, one scrolling form (for New campaign: Campaign name, Caller numbers, Audience), and Cancel + Create together in the footer — the user never loses their place. Add a Stepper only when the task truly has separate steps. Every modal is fixed-height (sm 320px, md/lg/xl 640px — the Modal default) so the frame never resizes: the header and footer stay put and ModalBody scrolls; see principle create-modals. Standalone onboarding flows render full-screen with a single content container holding the inputs and its own Back/Continue. *(minimal-information)*
554
564
  - **Do** use @container on the pane and @lg:/@xl: variants on children inside components. *(responsive-container-fluid)*
555
565
  - **Do** set pane widths in shells/pages through width tokens (thread-max-width, sidebar-width, aside-width, connector-card-min/-max). *(responsive-container-fluid)*
556
566
  - **Do** cap prose at its measure; let tables/charts/tool cards break out wider. Use auto-fill + minmax(min, 1fr) for grids. *(responsive-container-fluid)*
557
- - **Do** use PageHeader with onBack so the left arrow is anchored directly before the title. *(leading-back-navigation)*
558
- - **Do** reserve the trailing header area for forward or page-level actions such as Add, Save, or Share. *(leading-back-navigation)*
559
- - **Do** keep tab triggers left-aligned at their natural width, then place the scoped search field opposite them in the same toolbar. *(tabs-search-opposed)*
567
+ - **Do** title a drill-down with Breadcrumbs (20px trail, the same size as the header-bar title: muted parent links, Medium current item) and put its primary action (e.g. Pause campaign) right-aligned on the same row. *(leading-back-navigation)*
568
+ - **Do** use PageHeader with onBack only for a standalone page that has no parent hierarchy to show. *(leading-back-navigation)*
569
+ - **Do** reserve the trailing header area for forward or page-level actions such as Add, Save, Pause, or Share. *(leading-back-navigation)*
570
+ - **Do** keep tab triggers left-aligned at their natural width, then place the scoped search opposite them in the same toolbar. *(tabs-search-opposed)*
571
+ - **Do** on collection screens use SearchDisclosure followed by Filters as 32px icon triggers at the trailing edge; keep the page header for the title and one primary action only. *(tabs-search-opposed)*
572
+ - **Do** give each status tab its count (All 42 · Active 14 · Paused 4); the count renders Regular in tertiary grey, teal on the active tab. *(tabs-search-opposed)*
560
573
  - **Do** on constrained containers, stack tabs first and search second while preserving their relationship. *(tabs-search-opposed)*
574
+ - **Do** size the page so the rows fill the panel to its 24px bottom inset and Pagination sits at the bottom; let only the rows scroll. *(collection-screens)*
575
+ - **Do** show row checkboxes only on hover/focus (and while checked or mixed); keep the header select-all checkbox always visible. *(collection-screens)*
576
+ - **Do** when a row's details are open, mark it with TableRow isActive (the soft-teal fade) — no radio and no selection column for that purpose. *(collection-screens)*
577
+ - **Do** put status in a Chip using business language (see status-language) and keep cell values at text-m. *(collection-screens)*
578
+ - **Do** give a rate column a small Progress meter (size sm, ~48px) before the right-aligned percentage, and give a record with a recent trend a "Last 7 days" column holding a dotless Sparkline (height 28, dot={false}); a record with no calls yet shows an em dash and "No calls yet" in secondary text. *(collection-screens)*
579
+ - **Do** show the owner as plain text — no avatar in the cell. *(collection-screens)*
580
+ - **Do** show the customer's workspace logo in the Sidebar brand slot (SidebarWorkspace) when the product runs inside a customer workspace. *(collection-screens)*
581
+ - **Do** compose DrawerHeader → DrawerKicker + DrawerTitle + DrawerMeta so status never floats on its own; the header's full-width hairline grounds it. *(record-detail-panels)*
582
+ - **Do** let DrawerBody scroll (it is overflow-y-auto) — never clip the panel with overflow hidden. *(record-detail-panels)*
583
+ - **Do** group numbers in small flat stat tiles (WG1 fill + 1px WG4 edge, rounded-card, 12px padding, no shadow, 8px apart) and a trend in its own chart tile. *(record-detail-panels)*
584
+ - **Do** list attributes as a DetailList: sentence-case labels ("Type of loan", never type_of_loan), values right-aligned and wrapping, Not stated in secondary grey when empty (tertiary fails AA at 16px). *(record-detail-panels)*
585
+ - **Do** count what a section holds in its title ("0 of 10") instead of a separate summary line. *(record-detail-panels)*
586
+ - **Do** put rarely-needed data (call metadata) in a Collapsible at the end of the body, collapsed. *(record-detail-panels)*
587
+ - **Do** add a DrawerFooter only when its action exists — Open full view only when the dedicated page exists. *(record-detail-panels)*
588
+ - **Do** spell out the scope under the tabs with a FilterBar: joined 32px segments showing their current values — period preset + resolved dates leading, a second filter (cohort) trailing. *(analytics-surfaces)*
589
+ - **Do** stat card: label (16px secondary) → number (text-4xl, 35px, tightened tracking) with its scope ("of 4,800 calls", 14px secondary) 4px apart, a Sparkline of its recent trend beside the number (right ~third of the card), then a Progress bar 12px below that fills the card width. *(analytics-surfaces)*
590
+ - **Do** chart card: title (18px Medium) + scope subtitle (14px secondary) on the left, the legend right-aligned on the same row, ~40px from the title block to the plot. *(analytics-surfaces)*
591
+ - **Do** bar charts print each bar's value; stacked bars print each segment that fits plus the stack total. Line charts print every point's value in the series colour (secondary grey for light amber/grey series). *(analytics-surfaces)*
592
+ - **Do** keep LineChart's default gradientFill: a wash under each observed line fading top-to-bottom from 20% of the series colour to nothing (forecasts stay unfilled). *(analytics-surfaces)*
593
+ - **Do** size charts so the cards end on the panel's 24px bottom inset. *(analytics-surfaces)*
594
+ - **Do** say Active, not Dialing; name the tab Active too (All · Draft · Active · Paused · Completed). *(status-language)*
595
+ - **Do** use Chip tones consistently for the same status everywhere (table cell, detail header, filters). *(status-language)*
596
+ - **Do** use the plain Chip appearance (square dot + 16px text) where a column of filled chips would shout louder than the record names. *(status-language)*
597
+ - **Do** keep Chip labels Regular weight — 14px (text-s) in solid and outline chips, 16px (text-m) in plain ones. *(status-language)*
598
+ - **Do** use the Modal default height; pick size by width only (sm confirmation, md form, lg/xl dense content). *(create-modals)*
599
+ - **Do** put 'optional / you can change this later' notes in Field info, not in a helper line under the control. *(create-modals)*
600
+ - **Do** put a source switch (SegmentedControl size=sm, e.g. CSV upload | Empty audience) in Field actions on the label row. *(create-modals)*
601
+ - **Do** use FileDropzone layout='inline' (bare icon · label + hint · Browse) when the upload shares the form with other fields. *(create-modals)*
602
+ - **Do** let the user pick one saved record from a Table with selectionMode='single' (radio + soft-teal fade) under a search field, with the unit named once in the column header. *(create-modals)*
603
+ - **Do** render icons bare in muted grey: in upload strips, empty states, labels and list rows. *(create-modals)*
604
+ - **Do** give each group w-full; let SettingRow stretch to the panel width. *(settings-screens)*
605
+ - **Do** use the control that matches the setting: Switch for on/off, an outline Button showing the current value for pickers ("10:00 – 19:00"), a Badge for a fixed threshold. *(settings-screens)*
606
+ - **Do** keep the leading glyph bare and muted; rows separate with inset hairlines. *(settings-screens)*
561
607
 
562
608
  ### Don't:
563
609
 
564
610
  - **Don't** add a border to a surface to separate or group it — use fill/elevation/spacing instead. *(fills-over-strokes)*
565
611
  - **Don't** alternate persistent neutral fills inside one panel (for example white Panel → grey item → white child); flatten the item or promote the recess to the container. *(fills-over-strokes)*
566
612
  - **Don't** give a filled control no stroke on the tinted canvas — it disappears; it needs a crisp edge. *(fills-over-strokes)*
613
+ - **Don't** stop a structural hairline short of the walls, or run a row divider full-bleed (divide-y on a container whose rows carry their own px-*). *(fills-over-strokes)*
567
614
  - **Don't** put a shadow on an in-page surface for decoration or to signal that it is clickable. *(elevation-is-actionability)*
568
615
  - **Don't** use raw shadow values, inset glows, or the removed numbered levels and elevation hook. *(elevation-is-actionability)*
569
616
  - **Don't** invent a third floating level; stack order comes from float vs overlay. *(elevation-is-actionability)*
617
+ - **Don't** put shadow-card on a panel, a row, a detail-panel stat tile or the attached Drawer; put shadow-window on anything but the app window. *(elevation-is-actionability)*
570
618
  - **Don't** invent raw spring values inside a component, recreate a global duration/easing token scale, use an unbounded all-property transition, or start an entrance at scale zero. *(motion-follows-guide)*
571
619
  - **Don't** override or duplicate the motion already shipped inside an imported ADS component. *(motion-follows-guide)*
572
620
  - **Don't** animate keyboard-initiated or 100×/day actions (e.g. a command-palette toggle). *(motion-follows-guide)*
573
621
  - **Don't** add motion to a component only because it is visible; static structure and decorative chrome stay still. *(motion-follows-guide)*
574
622
  - **Don't** import from an icon package directly, or install a second icon set. *(icons-phosphor-regular)*
575
623
  - **Don't** leave an active nav item's icon at the regular stroke weight. *(icons-phosphor-regular)*
576
- - **Don't** add outer padding on a screen, leaving a tinted-canvas gap on the left. *(shell-owns-the-frame)*
624
+ - **Don't** set a glyph on a background tile, circle, or outlined square — icons sit bare in muted grey (avatars and brand logos keep their frames). *(icons-phosphor-regular)*
625
+ - **Don't** add outer padding, a radius or a shadow on a screen, or wrap a screen in its own card. *(shell-owns-the-frame)*
626
+ - **Don't** float a detail panel as a separate card with a strip of canvas between it and the list. *(shell-owns-the-frame)*
627
+ - **Don't** put the sidebar outside the window, directly on the backdrop. *(shell-owns-the-frame)*
577
628
  - **Don't** use rounded-full on an indicator dot (the one exception is a Radio control's inner dot). *(square-status-dots)*
578
629
  - **Don't** deep-import, alias, default/namespace-import, or hand-roll a local copy of a component. *(ads-first)*
579
630
  - **Don't** import a visual component directly from the web or an npm UI library at a call site. *(ads-first)*
@@ -584,8 +635,32 @@ Every line below is a `do` or `dont` recorded on a principle in `ads-manifest.js
584
635
  - **Don't** nest panel/step-rail chrome inside a multi-step flow. *(minimal-information)*
585
636
  - **Don't** use sm:/md: viewport variants inside src/components, or hardcode a pane width there. *(responsive-container-fluid)*
586
637
  - **Don't** cap a grid at a hard column count so it stops growing on wide displays. *(responsive-container-fluid)*
638
+ - **Don't** stack a back arrow and a large H1 above a page whose parent is already known — the breadcrumb says both. *(leading-back-navigation)*
587
639
  - **Don't** place Back on the right side of a page header. *(leading-back-navigation)*
588
640
  - **Don't** render a redundant visible Back label when the standard left arrow and accessible label are sufficient. *(leading-back-navigation)*
589
641
  - **Don't** stretch the tab track to fill the space between its labels and the search field. *(tabs-search-opposed)*
590
642
  - **Don't** put a scoped search field on a separate row above its tabs when both controls fit side by side. *(tabs-search-opposed)*
591
643
  - **Don't** place search before tabs in the reading or keyboard order. *(tabs-search-opposed)*
644
+ - **Don't** put a Search button or a full-width search field in the page header of a collection screen. *(tabs-search-opposed)*
645
+ - **Don't** leave an empty band under a short table, or float Pagination mid-panel. *(collection-screens)*
646
+ - **Don't** use single-selection radios to mean 'this row is open'. *(collection-screens)*
647
+ - **Don't** show a checkbox on every resting row of a multi-select table. *(collection-screens)*
648
+ - **Don't** put a profile image beside every owner name, or draw an empty meter or a flat sparkline for a record with no data. *(collection-screens)*
649
+ - **Don't** open record details in a modal over the list you are scanning. *(collection-screens)*
650
+ - **Don't** mix attributes (owner) into the metric grid. *(record-detail-panels)*
651
+ - **Don't** repeat a fact: what the header says (status, time, length) never appears again in stat cards or write-back rows. *(record-detail-panels)*
652
+ - **Don't** add placeholder footer buttons (Open full view, Call again) for a record that has no page or action behind them. *(record-detail-panels)*
653
+ - **Don't** show raw field keys, monospace labels, or a dash pattern like "— not stated" for empty values. *(record-detail-panels)*
654
+ - **Don't** separate stats with vertical hairlines while their bars run into the next stat — proximity breaks. *(analytics-surfaces)*
655
+ - **Don't** hide numbers behind a hover tooltip. *(analytics-surfaces)*
656
+ - **Don't** give a legend its own row under the subtitle when it fits beside the title. *(analytics-surfaces)*
657
+ - **Don't** put the same spacing between a label and its value as between two separate cards. *(analytics-surfaces)*
658
+ - **Don't** set a Chip label in Medium; the tone, dot and edge already carry the emphasis. *(status-language)*
659
+ - **Don't** colour a status with the primary teal — it competes with the primary action and the selected-row fade. *(status-language)*
660
+ - **Don't** use different words for the same state in the tab, the chip and the filter. *(status-language)*
661
+ - **Don't** pass height='content' or let a modal resize as sections appear or disappear. *(create-modals)*
662
+ - **Don't** stack a tall drop target, a search, and an unlabeled list one after another inside a fixed-height form. *(create-modals)*
663
+ - **Don't** hand-roll a pick-one list with justify-between rows or an 'or' divider between alternatives. *(create-modals)*
664
+ - **Don't** put an icon on a grey tile, a circle, or an outlined square that adds nothing but a background. *(create-modals)*
665
+ - **Don't** cap a settings group at a fixed width (720px) and leave the right half of the panel empty. *(settings-screens)*
666
+ - **Don't** put a save button per group, or icons on tiles. *(settings-screens)*
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @actioneer/ads
2
2
 
3
3
  The **Actioneer Design System**: React 19 components, Tailwind CSS v4 tokens,
4
- bundled Aribau Grotesk fonts, and accessible interactions with built-in motion.
4
+ bundled Geist body font, Season Mix heading tokens, and accessible interactions with built-in motion.
5
5
 
6
6
  [Browse components in Storybook](https://actioneer-ads-storybook.himanshu-563.workers.dev).
7
7
 
@@ -48,8 +48,10 @@ Tailwind:
48
48
  `tailwindcss`:
49
49
 
50
50
  - the `@theme` **design tokens** (colors, typography, spacing, and radii),
51
- - the **Aribau Grotesk** `@font-face` rules (served package-relative — no
52
- `/public` copy needed), and
51
+ - the **Geist** `@font-face` rule (served package-relative — no `/public` copy
52
+ needed). Headings use `--font-heading: "Season Mix"`; Season Mix is licensed
53
+ and not bundled, so declare its `@font-face` in your app from your licensed
54
+ file (without it, headings fall back to the platform serif), and
53
55
  - a baked `@source "./**/*.{js,mjs}"` so Tailwind scans ADS's own class usage and
54
56
  generates its utilities in your build.
55
57
 
@@ -158,9 +160,31 @@ Reference the semantic tokens (e.g. `bg-brand`, `text-muted-foreground`,
158
160
 
159
161
  **Sample screens:** full screens built only from ADS exports ship as readable
160
162
  source in `node_modules/@actioneer/ads/examples/` (`AgentsScreen`,
161
- `SettingsScreen`, `WorkspaceHeaderScreen`). Open them to see how components are
162
- meant to be composed, or copy one into your app and swap in your own data; the
163
- folder's `README.md` lists what each one shows.
163
+ `SettingsScreen`, `WorkspaceHeaderScreen`, `CampaignsScreen`,
164
+ `CampaignAnalysisScreen`). Open them to see how components are meant to be
165
+ composed, or copy one into your app and swap in your own data; the folder's
166
+ `README.md` lists what each one shows.
167
+
168
+ **Screen recipes:** when you build a screen CLOSE to a collection (a list of
169
+ records with a detail panel and a create modal) or a record's analytics page,
170
+ start from `CampaignsScreen` / `CampaignAnalysisScreen` and follow the
171
+ `collection-screens`, `record-detail-panels`, `analytics-surfaces`,
172
+ `status-language`, `create-modals` and `settings-screens` principles
173
+ (`get_principles`) and the `collection-screen` / `record-analytics` /
174
+ `create-modal` / `record-call-logs` / `settings-screen` productions
175
+ (`get_ui_grammar`). A record's call logs start from `CallLogsScreen`; a
176
+ settings page from `GuardrailsSettingsScreen`. In short: rows fill the panel
177
+ with pagination at the bottom, row checkboxes on hover, `TableRow isActive` for
178
+ the open row, status under the detail title, outlined stat and chart cards with
179
+ legends in the title row and printed values, a 16px breadcrumb titling
180
+ drill-downs, business-language statuses, and fixed-height modals (sm 320px,
181
+ md–xl 640px). Inside a create modal (`NewCampaignModal`): optional guidance in
182
+ a `Field info` icon, a source switch in `Field actions`, an inline
183
+ `FileDropzone`, a single-select `Table` to reuse a saved record, and bare
184
+ icons with no tile behind them. Record panels: a grounded `DrawerHeader`
185
+ (`DrawerKicker`, `DrawerTitle`, `DrawerMeta`), a scrolling `DrawerBody` of
186
+ `DrawerSection`s, `DetailList` attributes with Not stated empties, each fact
187
+ once, and no footer unless its action exists. Settings groups fill the panel.
164
188
 
165
189
  Shared motion presets are named exports from `@actioneer/ads`. Components already
166
190
  include their own interaction motion; use the exported presets for additional
@@ -203,7 +227,7 @@ every consuming repo honest.
203
227
 
204
228
  ## Documentation and license
205
229
 
206
- - [Storybook: components and recipes](https://actioneer-ads-storybook.himanshu-563.workers.dev)
230
+ - [Storybook: components](https://actioneer-ads-storybook.himanshu-563.workers.dev)
207
231
  - [ADS MCP: component discovery for coding agents](https://www.npmjs.com/package/@actioneer/ads-mcp)
208
232
  - Bundled references: `@actioneer/ads/manifest.json`, `@actioneer/ads/llms.txt`,
209
233
  and `@actioneer/ads/DESIGN.md`.
@@ -1224,7 +1224,7 @@ const ce = p.forwardRef(
1224
1224
  {
1225
1225
  className: g(
1226
1226
  A({ tone: a.value.tone }),
1227
- "mt-1 text-3xl font-normal tracking-tight"
1227
+ "mt-1 text-4xl font-normal"
1228
1228
  ),
1229
1229
  children: a.value.value
1230
1230
  }
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  var c = Object.defineProperty;
3
3
  var l = (r, t, o) => t in r ? c(r, t, { enumerable: !0, configurable: !0, writable: !0, value: o }) : r[t] = o;
4
- var p = (r, t, o) => l(r, typeof t != "symbol" ? t + "" : t, o);
5
- import { jsxs as n, jsx as e } from "react/jsx-runtime";
4
+ var n = (r, t, o) => l(r, typeof t != "symbol" ? t + "" : t, o);
5
+ import { jsxs as p, jsx as e } from "react/jsx-runtime";
6
6
  import { useComposerRuntime as d, useComposer as u } from "@assistant-ui/react";
7
7
  import "../../lib/cn.js";
8
8
  import "../../lib/interactive.js";
@@ -39,6 +39,8 @@ import "react-aria-components";
39
39
  import "../autocomplete.js";
40
40
  import "../slider.js";
41
41
  import "../segmented.js";
42
+ import "../filters.js";
43
+ import "../filter-bar.js";
42
44
  import "../modal.js";
43
45
  import "../drawer.js";
44
46
  import "../popover.js";
@@ -59,6 +61,8 @@ import "../avatar.js";
59
61
  import "../badge.js";
60
62
  import "../chip.js";
61
63
  import "../timeline.js";
64
+ import "../detail-list.js";
65
+ import "../sparkline.js";
62
66
  import "../alert.js";
63
67
  import "../surface-loader.js";
64
68
  import "../pixel-blast-svg.js";
@@ -127,7 +131,7 @@ const m = "application/x-actioneer-harness", S = [
127
131
  instructions: "Write a concise executive brief: lead with the decision or insight, use only decision-relevant evidence, and end with the recommended next action."
128
132
  }
129
133
  ];
130
- function wi() {
134
+ function Di() {
131
135
  const r = d(), t = u((i) => i.attachments), o = new Set(
132
136
  t.filter((i) => i.contentType === m).map((i) => i.name)
133
137
  ), s = (i) => {
@@ -135,8 +139,8 @@ function wi() {
135
139
  new File([i.instructions], `${i.label}.harness`, { type: m })
136
140
  );
137
141
  };
138
- return /* @__PURE__ */ n(b, { children: [
139
- /* @__PURE__ */ e(v, { children: /* @__PURE__ */ n(
142
+ return /* @__PURE__ */ p(b, { children: [
143
+ /* @__PURE__ */ e(v, { children: /* @__PURE__ */ p(
140
144
  "button",
141
145
  {
142
146
  type: "button",
@@ -155,7 +159,7 @@ function wi() {
155
159
  ]
156
160
  }
157
161
  ) }),
158
- /* @__PURE__ */ n(y, { side: "top", align: "start", className: "w-56", autoFocusFirstItem: !1, children: [
162
+ /* @__PURE__ */ p(y, { side: "top", align: "start", className: "w-56", autoFocusFirstItem: !1, children: [
159
163
  /* @__PURE__ */ e(x, { children: "Add a harness" }),
160
164
  S.map((i) => /* @__PURE__ */ e(
161
165
  w,
@@ -170,9 +174,9 @@ function wi() {
170
174
  ] })
171
175
  ] });
172
176
  }
173
- class Si {
177
+ class Ei {
174
178
  constructor() {
175
- p(this, "accept", m);
179
+ n(this, "accept", m);
176
180
  }
177
181
  async add(t) {
178
182
  return {
@@ -203,7 +207,7 @@ ${o}
203
207
  }
204
208
  }
205
209
  export {
206
- wi as ComposerHarness,
210
+ Di as ComposerHarness,
207
211
  m as HARNESS_MIME,
208
- Si as HarnessAttachmentAdapter
212
+ Ei as HarnessAttachmentAdapter
209
213
  };
@@ -6,10 +6,15 @@ export interface BodyPageHeaderProps extends Omit<React.HTMLAttributes<HTMLEleme
6
6
  actions?: React.ReactNode;
7
7
  }
8
8
  /**
9
- * Borderless header for a PageBody destination.
9
+ * Header bar for a PageBody destination (the window panel style,
10
+ * user-mandated 2026-11-12).
10
11
  *
11
- * The header owns a 24px inset on every side and keeps the page title at the
12
- * canonical 24px title-1 step. Destination icons and compact metadata belong in the
13
- * sidebar or content, not beside the title.
12
+ * A 64px bar: the 18px page title at the leading edge, page actions (and any
13
+ * quiet "Updated 2m ago" meta) at the trailing edge, 24px side insets, and a
14
+ * full-width WG4 hairline under it that separates the bar from the page
15
+ * content. The hairline is structural, so it runs edge to edge; row dividers
16
+ * inside the content stay inset. The bar owns the 24px gap to the content
17
+ * below (mb-6), so content never pads its own top.
18
+ * Destination icons belong in the sidebar, not beside the title.
14
19
  */
15
20
  export declare const BodyPageHeader: React.ForwardRefExoticComponent<BodyPageHeaderProps & React.RefAttributes<HTMLElement>>;
@@ -1,24 +1,24 @@
1
1
  "use client";
2
- import { jsxs as i, jsx as r } from "react/jsx-runtime";
3
- import * as d from "react";
4
- import { cn as n } from "../lib/cn.js";
5
- const l = d.forwardRef(({ className: a, title: t, actions: e, ...s }, m) => /* @__PURE__ */ i(
2
+ import { jsxs as s, jsx as r } from "react/jsx-runtime";
3
+ import * as i from "react";
4
+ import { cn as o } from "../lib/cn.js";
5
+ const n = i.forwardRef(({ className: a, title: t, actions: e, ...d }, m) => /* @__PURE__ */ s(
6
6
  "header",
7
7
  {
8
8
  ref: m,
9
9
  "data-slot": "body-page-header",
10
- className: n(
11
- "flex shrink-0 items-center justify-between gap-4 p-6",
10
+ className: o(
11
+ "mb-6 flex h-16 shrink-0 items-center justify-between gap-4 border-b border-border px-6",
12
12
  a
13
13
  ),
14
- ...s,
14
+ ...d,
15
15
  children: [
16
- /* @__PURE__ */ r("h1", { className: "min-w-0 truncate text-2xl font-medium text-on-warm-grey-primary", children: t }),
16
+ /* @__PURE__ */ r("h1", { className: "m-0 min-w-0 truncate text-xl font-medium text-on-white-primary", children: t }),
17
17
  e ? /* @__PURE__ */ r("div", { className: "flex shrink-0 items-center gap-2", children: e }) : null
18
18
  ]
19
19
  }
20
20
  ));
21
- l.displayName = "BodyPageHeader";
21
+ n.displayName = "BodyPageHeader";
22
22
  export {
23
- l as BodyPageHeader
23
+ n as BodyPageHeader
24
24
  };