@rtkelly-labs/design-system 0.0.0-bootstrap.0 → 0.16.0-dev.0.37dedac

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 (284) hide show
  1. package/CHANGELOG.md +347 -0
  2. package/LICENSE +21 -0
  3. package/README.md +317 -2
  4. package/dist/components/AlertDialog.js +95 -0
  5. package/dist/components/AlertDialog.js.map +1 -0
  6. package/dist/components/AppShell.js +261 -0
  7. package/dist/components/AppShell.js.map +1 -0
  8. package/dist/components/AsciiDivider.js +8 -0
  9. package/dist/components/AsciiDivider.js.map +1 -0
  10. package/dist/components/AsciiGauge.js +130 -0
  11. package/dist/components/AsciiGauge.js.map +1 -0
  12. package/dist/components/Avatar.js +87 -0
  13. package/dist/components/Avatar.js.map +1 -0
  14. package/dist/components/Badge.js +25 -0
  15. package/dist/components/Badge.js.map +1 -0
  16. package/dist/components/BarChart.js +288 -0
  17. package/dist/components/BarChart.js.map +1 -0
  18. package/dist/components/BracketText.js +23 -0
  19. package/dist/components/BracketText.js.map +1 -0
  20. package/dist/components/BulletChart.js +50 -0
  21. package/dist/components/BulletChart.js.map +1 -0
  22. package/dist/components/Button.js +130 -0
  23. package/dist/components/Button.js.map +1 -0
  24. package/dist/components/Card.js +181 -0
  25. package/dist/components/Card.js.map +1 -0
  26. package/dist/components/ChartTooltip.js +54 -0
  27. package/dist/components/ChartTooltip.js.map +1 -0
  28. package/dist/components/Checkbox.js +55 -0
  29. package/dist/components/Checkbox.js.map +1 -0
  30. package/dist/components/ColophonCard.js +122 -0
  31. package/dist/components/ColophonCard.js.map +1 -0
  32. package/dist/components/DataTable.js +299 -0
  33. package/dist/components/DataTable.js.map +1 -0
  34. package/dist/components/Divider.js +42 -0
  35. package/dist/components/Divider.js.map +1 -0
  36. package/dist/components/Drawer.js +81 -0
  37. package/dist/components/Drawer.js.map +1 -0
  38. package/dist/components/EmptyState.js +39 -0
  39. package/dist/components/EmptyState.js.map +1 -0
  40. package/dist/components/ErrorSummary.js +122 -0
  41. package/dist/components/ErrorSummary.js.map +1 -0
  42. package/dist/components/Fieldset.js +37 -0
  43. package/dist/components/Fieldset.js.map +1 -0
  44. package/dist/components/Glyph.js +60 -0
  45. package/dist/components/Glyph.js.map +1 -0
  46. package/dist/components/Input.js +111 -0
  47. package/dist/components/Input.js.map +1 -0
  48. package/dist/components/LinkProvider.js +76 -0
  49. package/dist/components/LinkProvider.js.map +1 -0
  50. package/dist/components/Menu.js +144 -0
  51. package/dist/components/Menu.js.map +1 -0
  52. package/dist/components/MetadataGrid.js +184 -0
  53. package/dist/components/MetadataGrid.js.map +1 -0
  54. package/dist/components/MobileNav.js +120 -0
  55. package/dist/components/MobileNav.js.map +1 -0
  56. package/dist/components/Modal.js +79 -0
  57. package/dist/components/Modal.js.map +1 -0
  58. package/dist/components/NerdIcon.js +177 -0
  59. package/dist/components/NerdIcon.js.map +1 -0
  60. package/dist/components/NoteBlock.js +84 -0
  61. package/dist/components/NoteBlock.js.map +1 -0
  62. package/dist/components/PageHeader.js +34 -0
  63. package/dist/components/PageHeader.js.map +1 -0
  64. package/dist/components/PageTitle.js +21 -0
  65. package/dist/components/PageTitle.js.map +1 -0
  66. package/dist/components/Pagination.js +96 -0
  67. package/dist/components/Pagination.js.map +1 -0
  68. package/dist/components/Popover.js +87 -0
  69. package/dist/components/Popover.js.map +1 -0
  70. package/dist/components/Progress.js +83 -0
  71. package/dist/components/Progress.js.map +1 -0
  72. package/dist/components/RadioGroup.js +74 -0
  73. package/dist/components/RadioGroup.js.map +1 -0
  74. package/dist/components/ReportDocument.js +75 -0
  75. package/dist/components/ReportDocument.js.map +1 -0
  76. package/dist/components/SectionContainer.js +18 -0
  77. package/dist/components/SectionContainer.js.map +1 -0
  78. package/dist/components/Select.js +199 -0
  79. package/dist/components/Select.js.map +1 -0
  80. package/dist/components/SiteFooter.js +29 -0
  81. package/dist/components/SiteFooter.js.map +1 -0
  82. package/dist/components/SiteHeader.js +59 -0
  83. package/dist/components/SiteHeader.js.map +1 -0
  84. package/dist/components/SiteNav.js +141 -0
  85. package/dist/components/SiteNav.js.map +1 -0
  86. package/dist/components/Skeleton.js +51 -0
  87. package/dist/components/Skeleton.js.map +1 -0
  88. package/dist/components/SocialIcon.js +77 -0
  89. package/dist/components/SocialIcon.js.map +1 -0
  90. package/dist/components/Sparkline.js +104 -0
  91. package/dist/components/Sparkline.js.map +1 -0
  92. package/dist/components/Spinner.js +65 -0
  93. package/dist/components/Spinner.js.map +1 -0
  94. package/dist/components/StatCard.js +53 -0
  95. package/dist/components/StatCard.js.map +1 -0
  96. package/dist/components/StatusPage.js +99 -0
  97. package/dist/components/StatusPage.js.map +1 -0
  98. package/dist/components/Swatch.js +55 -0
  99. package/dist/components/Swatch.js.map +1 -0
  100. package/dist/components/Switch.js +53 -0
  101. package/dist/components/Switch.js.map +1 -0
  102. package/dist/components/TLDR.js +53 -0
  103. package/dist/components/TLDR.js.map +1 -0
  104. package/dist/components/Table.js +155 -0
  105. package/dist/components/Table.js.map +1 -0
  106. package/dist/components/Tabs.js +325 -0
  107. package/dist/components/Tabs.js.map +1 -0
  108. package/dist/components/Tag.js +19 -0
  109. package/dist/components/Tag.js.map +1 -0
  110. package/dist/components/TelemetryGauge.js +112 -0
  111. package/dist/components/TelemetryGauge.js.map +1 -0
  112. package/dist/components/ThemeProvider.js +87 -0
  113. package/dist/components/ThemeProvider.js.map +1 -0
  114. package/dist/components/TickerTape.js +196 -0
  115. package/dist/components/TickerTape.js.map +1 -0
  116. package/dist/components/Toast.js +200 -0
  117. package/dist/components/Toast.js.map +1 -0
  118. package/dist/components/Tooltip.js +69 -0
  119. package/dist/components/Tooltip.js.map +1 -0
  120. package/dist/components/TrackCard.js +206 -0
  121. package/dist/components/TrackCard.js.map +1 -0
  122. package/dist/components/blog/BlogPost.js +116 -0
  123. package/dist/components/blog/BlogPost.js.map +1 -0
  124. package/dist/components/blog/LoremIpsumPost.js +62 -0
  125. package/dist/components/blog/LoremIpsumPost.js.map +1 -0
  126. package/dist/components/blog/author.js +25 -0
  127. package/dist/components/blog/author.js.map +1 -0
  128. package/dist/components/booleanControl.js +50 -0
  129. package/dist/components/booleanControl.js.map +1 -0
  130. package/dist/components/dialogPopupRef.js +28 -0
  131. package/dist/components/dialogPopupRef.js.map +1 -0
  132. package/dist/components/dialogSurface.js +75 -0
  133. package/dist/components/dialogSurface.js.map +1 -0
  134. package/dist/components/docs/AnchorHeading.js +80 -0
  135. package/dist/components/docs/AnchorHeading.js.map +1 -0
  136. package/dist/components/docs/Breadcrumbs.js +26 -0
  137. package/dist/components/docs/Breadcrumbs.js.map +1 -0
  138. package/dist/components/docs/CodeBlock.js +63 -0
  139. package/dist/components/docs/CodeBlock.js.map +1 -0
  140. package/dist/components/docs/CodeTabs.js +71 -0
  141. package/dist/components/docs/CodeTabs.js.map +1 -0
  142. package/dist/components/docs/DocPager.js +28 -0
  143. package/dist/components/docs/DocPager.js.map +1 -0
  144. package/dist/components/docs/DocsHeader.js +124 -0
  145. package/dist/components/docs/DocsHeader.js.map +1 -0
  146. package/dist/components/docs/DocsLayout.js +61 -0
  147. package/dist/components/docs/DocsLayout.js.map +1 -0
  148. package/dist/components/docs/DocsLinkProvider.js +25 -0
  149. package/dist/components/docs/DocsLinkProvider.js.map +1 -0
  150. package/dist/components/docs/DocsSidebar.js +147 -0
  151. package/dist/components/docs/DocsSidebar.js.map +1 -0
  152. package/dist/components/docs/Prose.js +56 -0
  153. package/dist/components/docs/Prose.js.map +1 -0
  154. package/dist/components/docs/TableOfContents.js +71 -0
  155. package/dist/components/docs/TableOfContents.js.map +1 -0
  156. package/dist/components/docs/codeTabsStore.js +75 -0
  157. package/dist/components/docs/codeTabsStore.js.map +1 -0
  158. package/dist/components/docs/figures/ActivityGrid.js +116 -0
  159. package/dist/components/docs/figures/ActivityGrid.js.map +1 -0
  160. package/dist/components/docs/figures/BeforeAfter.js +64 -0
  161. package/dist/components/docs/figures/BeforeAfter.js.map +1 -0
  162. package/dist/components/docs/figures/ChangeSummary.js +74 -0
  163. package/dist/components/docs/figures/ChangeSummary.js.map +1 -0
  164. package/dist/components/docs/figures/Decision.js +34 -0
  165. package/dist/components/docs/figures/Decision.js.map +1 -0
  166. package/dist/components/docs/figures/DrawingSheet.js +37 -0
  167. package/dist/components/docs/figures/DrawingSheet.js.map +1 -0
  168. package/dist/components/docs/figures/FigureFrame.js +226 -0
  169. package/dist/components/docs/figures/FigureFrame.js.map +1 -0
  170. package/dist/components/docs/figures/FileChangeTree.js +134 -0
  171. package/dist/components/docs/figures/FileChangeTree.js.map +1 -0
  172. package/dist/components/docs/figures/FlowDiagram.js +82 -0
  173. package/dist/components/docs/figures/FlowDiagram.js.map +1 -0
  174. package/dist/components/docs/figures/GanttChart.js +79 -0
  175. package/dist/components/docs/figures/GanttChart.js.map +1 -0
  176. package/dist/components/docs/figures/IndexedSection.js +45 -0
  177. package/dist/components/docs/figures/IndexedSection.js.map +1 -0
  178. package/dist/components/docs/figures/MetricStrip.js +23 -0
  179. package/dist/components/docs/figures/MetricStrip.js.map +1 -0
  180. package/dist/components/docs/figures/Steps.js +58 -0
  181. package/dist/components/docs/figures/Steps.js.map +1 -0
  182. package/dist/components/docs/figures/SwatchCompare.js +42 -0
  183. package/dist/components/docs/figures/SwatchCompare.js.map +1 -0
  184. package/dist/components/docs/figures/TermGrid.js +22 -0
  185. package/dist/components/docs/figures/TermGrid.js.map +1 -0
  186. package/dist/components/docs/figures/Terminal.js +37 -0
  187. package/dist/components/docs/figures/Terminal.js.map +1 -0
  188. package/dist/components/docs/figures/Timeline.js +61 -0
  189. package/dist/components/docs/figures/Timeline.js.map +1 -0
  190. package/dist/components/docs/figures/TokenTable.js +42 -0
  191. package/dist/components/docs/figures/TokenTable.js.map +1 -0
  192. package/dist/components/docs/figures/TreeDiagram.js +76 -0
  193. package/dist/components/docs/figures/TreeDiagram.js.map +1 -0
  194. package/dist/components/docs/figures/UptimeStrip.js +68 -0
  195. package/dist/components/docs/figures/UptimeStrip.js.map +1 -0
  196. package/dist/components/docs/figures/index.js +24 -0
  197. package/dist/components/docs/figures/index.js.map +1 -0
  198. package/dist/components/docs/index.js +15 -0
  199. package/dist/components/docs/index.js.map +1 -0
  200. package/dist/components/docs/mdxComponents.js +108 -0
  201. package/dist/components/docs/mdxComponents.js.map +1 -0
  202. package/dist/components/experiments/DesignSandbox.js +206 -0
  203. package/dist/components/experiments/DesignSandbox.js.map +1 -0
  204. package/dist/components/experiments/ExperimentsView.js +138 -0
  205. package/dist/components/experiments/ExperimentsView.js.map +1 -0
  206. package/dist/components/fieldFrame.js +200 -0
  207. package/dist/components/fieldFrame.js.map +1 -0
  208. package/dist/components/floatingSurface.js +35 -0
  209. package/dist/components/floatingSurface.js.map +1 -0
  210. package/dist/components/marketing/CTASection.js +47 -0
  211. package/dist/components/marketing/CTASection.js.map +1 -0
  212. package/dist/components/marketing/FeatureGrid.js +49 -0
  213. package/dist/components/marketing/FeatureGrid.js.map +1 -0
  214. package/dist/components/marketing/Hero.js +44 -0
  215. package/dist/components/marketing/Hero.js.map +1 -0
  216. package/dist/components/marketing/PricingGrid.js +65 -0
  217. package/dist/components/marketing/PricingGrid.js.map +1 -0
  218. package/dist/components/marketing/SectionGrid.js +51 -0
  219. package/dist/components/marketing/SectionGrid.js.map +1 -0
  220. package/dist/components/paginationRange.js +28 -0
  221. package/dist/components/paginationRange.js.map +1 -0
  222. package/dist/components/portalTheme.js +14 -0
  223. package/dist/components/portalTheme.js.map +1 -0
  224. package/dist/components/saas/SaasLandingPage.js +201 -0
  225. package/dist/components/saas/SaasLandingPage.js.map +1 -0
  226. package/dist/components/siteNavContext.js +24 -0
  227. package/dist/components/siteNavContext.js.map +1 -0
  228. package/dist/components/slides/Slide.js +84 -0
  229. package/dist/components/slides/Slide.js.map +1 -0
  230. package/dist/components/slides/SlideDeck.js +228 -0
  231. package/dist/components/slides/SlideDeck.js.map +1 -0
  232. package/dist/components/themeInitScript.js +16 -0
  233. package/dist/components/themeInitScript.js.map +1 -0
  234. package/dist/components/themeScope.js +9 -0
  235. package/dist/components/themeScope.js.map +1 -0
  236. package/dist/components/toastLifetime.js +21 -0
  237. package/dist/components/toastLifetime.js.map +1 -0
  238. package/dist/fonts/DSSymbols-OFL.txt +98 -0
  239. package/dist/fonts/DSSymbols-Regular.woff2 +0 -0
  240. package/dist/fonts/SymbolsNerdFontMono-Regular.woff2 +0 -0
  241. package/dist/hooks/useActiveHeading.js +59 -0
  242. package/dist/hooks/useActiveHeading.js.map +1 -0
  243. package/dist/hooks/useCopyToClipboard.js +58 -0
  244. package/dist/hooks/useCopyToClipboard.js.map +1 -0
  245. package/dist/index.d.ts +6394 -0
  246. package/dist/index.js +117 -0
  247. package/dist/index.js.map +1 -0
  248. package/dist/lib/accentClasses.js +87 -0
  249. package/dist/lib/accentClasses.js.map +1 -0
  250. package/dist/lib/recipe.js +33 -0
  251. package/dist/lib/recipe.js.map +1 -0
  252. package/dist/lib/slug.js +39 -0
  253. package/dist/lib/slug.js.map +1 -0
  254. package/dist/lib/theme.js +76 -0
  255. package/dist/lib/theme.js.map +1 -0
  256. package/dist/lib/tokenRules.js +51 -0
  257. package/dist/lib/tokenRules.js.map +1 -0
  258. package/dist/prose.css +766 -0
  259. package/dist/styles.css +203 -0
  260. package/dist/theme/ansi.js +77 -0
  261. package/dist/theme/ansi.js.map +1 -0
  262. package/dist/theme/contrast.js +342 -0
  263. package/dist/theme/contrast.js.map +1 -0
  264. package/dist/theme/levels.js +234 -0
  265. package/dist/theme/levels.js.map +1 -0
  266. package/dist/theme/media.js +115 -0
  267. package/dist/theme/media.js.map +1 -0
  268. package/dist/theme/recommended.js +105 -0
  269. package/dist/theme/recommended.js.map +1 -0
  270. package/dist/theme.css +538 -0
  271. package/dist/tokens.js +1 -0
  272. package/dist/tokens.js.map +1 -0
  273. package/package.json +209 -6
  274. package/skills/design-system-usage/SKILL.md +75 -0
  275. package/terminal/rtk-midnight.alacritty.toml +33 -0
  276. package/terminal/rtk-midnight.ghostty.conf +24 -0
  277. package/terminal/rtk-midnight.itermcolors +167 -0
  278. package/terminal/rtk-midnight.windows-terminal.json +23 -0
  279. package/terminal/rtk-sketch.alacritty.toml +33 -0
  280. package/terminal/rtk-sketch.ghostty.conf +24 -0
  281. package/terminal/rtk-sketch.itermcolors +167 -0
  282. package/terminal/rtk-sketch.windows-terminal.json +23 -0
  283. package/tokens/palette.midnight.tokens.json +477 -0
  284. package/tokens/palette.sketch.tokens.json +477 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/marketing/Hero.tsx"],"sourcesContent":["import { forwardRef, useId } from 'react';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { recipe } from '../../lib/recipe';\nimport { PageTitle } from '../PageTitle';\nimport type { SectionAlign } from './SectionGrid';\n\nexport interface HeroProps\n extends Omit<HTMLAttributes<HTMLElement>, 'title' | 'children' | 'className'> {\n /** The page's headline, rendered as its one `<h1>` through `PageTitle`. */\n title: ReactNode;\n /** A line under the headline, in the primary accent. */\n subtitle?: string;\n /**\n * A short marker above the headline — normally a `Badge`: a release, a\n * category, a status.\n */\n eyebrow?: ReactNode;\n /**\n * The calls to action, in a row under the headline that wraps on a narrow\n * screen — normally one or two `Button`s, the first the one you want pressed.\n */\n actions?: ReactNode;\n /**\n * The hero's media, below the actions — a screenshot, a terminal, a code\n * sample. Give it its own width; the hero does not constrain it.\n */\n children?: ReactNode;\n /** Draw the headline's brackets. On by default, as `PageTitle` does. */\n bracketed?: boolean;\n /** Where the headline and actions sit — `center` (the default) or `start`. */\n align?: SectionAlign;\n /** Merged into the `<section>`'s classes. */\n className?: string;\n}\n\nconst hero = recipe({\n slots: {\n root: 'mb-16',\n eyebrow: 'mb-6 inline-block',\n actions: 'mt-10 flex flex-wrap gap-5',\n media: 'mt-14',\n },\n variants: {\n align: {\n center: { root: 'text-center', actions: 'justify-center' },\n start: { root: 'text-left', actions: 'justify-start' },\n },\n },\n defaultVariants: { align: 'center' },\n});\n\n/**\n * The opening section of a marketing page: an eyebrow, the headline, a line\n * under it, the calls to action and an optional piece of media.\n *\n * Every slot is the caller's. The hero holds no copy, no product name and no\n * default buttons, so one component opens a product launch and a project site\n * without either knowing about the other. The headline is the page's `<h1>`,\n * and it names the section.\n *\n * @example\n * ```tsx\n * <Hero\n * eyebrow={<Badge accent=\"primary\">NEW</Badge>}\n * title=\"Ship the thing\"\n * subtitle=\"One line on why.\"\n * actions={<Button href=\"/start\" variant=\"tertiary\" bracketed size=\"lg\">START</Button>}\n * />\n * ```\n */\nexport const Hero = forwardRef<HTMLElement, HeroProps>(function Hero(\n { title, subtitle, eyebrow, actions, children, bracketed = true, align = 'center', className, ...props },\n ref,\n) {\n const styles = hero({ align });\n const titleId = useId();\n\n return (\n <section\n ref={ref}\n data-slot=\"hero\"\n aria-labelledby={titleId}\n {...props}\n className={styles.root({ class: className })}\n >\n {eyebrow ? (\n <span data-slot=\"hero-eyebrow\" className={styles.eyebrow()}>\n {eyebrow}\n </span>\n ) : null}\n <PageTitle id={titleId} subtitle={subtitle} bracketed={bracketed}>\n {title}\n </PageTitle>\n {actions ? (\n <div data-slot=\"hero-actions\" className={styles.actions()}>\n {actions}\n </div>\n ) : null}\n {children ? (\n <div data-slot=\"hero-media\" className={styles.media()}>\n {children}\n </div>\n ) : null}\n </section>\n );\n});\n"],"mappings":";AAAA,SAAS,YAAY,aAAa;AAElC,SAAS,cAAc;AACvB,SAAS,iBAAiB;AA2EtB,SAQI,KARJ;AA3CJ,IAAM,OAAO,OAAO;AAAA,EAClB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,MACL,QAAQ,EAAE,MAAM,eAAe,SAAS,iBAAiB;AAAA,MACzD,OAAO,EAAE,MAAM,aAAa,SAAS,gBAAgB;AAAA,IACvD;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,OAAO,SAAS;AACrC,CAAC;AAqBM,IAAM,OAAO,WAAmC,SAASA,MAC9D,EAAE,OAAO,UAAU,SAAS,SAAS,UAAU,YAAY,MAAM,QAAQ,UAAU,WAAW,GAAG,MAAM,GACvG,KACA;AACA,QAAM,SAAS,KAAK,EAAE,MAAM,CAAC;AAC7B,QAAM,UAAU,MAAM;AAEtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAU;AAAA,MACV,mBAAiB;AAAA,MAChB,GAAG;AAAA,MACJ,WAAW,OAAO,KAAK,EAAE,OAAO,UAAU,CAAC;AAAA,MAE1C;AAAA,kBACC,oBAAC,UAAK,aAAU,gBAAe,WAAW,OAAO,QAAQ,GACtD,mBACH,IACE;AAAA,QACJ,oBAAC,aAAU,IAAI,SAAS,UAAoB,WACzC,iBACH;AAAA,QACC,UACC,oBAAC,SAAI,aAAU,gBAAe,WAAW,OAAO,QAAQ,GACrD,mBACH,IACE;AAAA,QACH,WACC,oBAAC,SAAI,aAAU,cAAa,WAAW,OAAO,MAAM,GACjD,UACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ,CAAC;","names":["Hero"]}
@@ -0,0 +1,65 @@
1
+ // src/components/marketing/PricingGrid.tsx
2
+ import { forwardRef } from "react";
3
+ import { Check } from "lucide-react";
4
+ import { recipe } from "../../lib/recipe.js";
5
+ import { ACCENT_CLASS_MAPS, accentTextClass, canonicalAccent } from "../../lib/accentClasses.js";
6
+ import { Card } from "../Card.js";
7
+ import { SectionGrid } from "./SectionGrid.js";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var PricingGrid = forwardRef(function PricingGrid2(props, ref) {
10
+ return /* @__PURE__ */ jsx(SectionGrid, { ref, slot: "pricing-grid", ...props });
11
+ });
12
+ var pricingTier = recipe({
13
+ slots: {
14
+ root: "relative flex flex-col justify-between",
15
+ badge: "absolute -top-3 left-4 inline-block px-2 py-[0.15rem] font-mono text-[0.65rem] font-bold uppercase text-content-inverse",
16
+ name: "mb-2 font-display text-[1.1rem] font-extrabold uppercase text-content-primary",
17
+ priceRow: "mb-4",
18
+ price: "font-display text-[3rem] font-black",
19
+ period: "font-mono text-[0.85rem] text-content-muted",
20
+ description: "mb-6 font-mono text-[0.75rem] leading-[1.5] text-content-primary opacity-80",
21
+ features: "m-0 mb-6 list-none p-0",
22
+ feature: "mb-[0.6rem] flex items-start gap-2 font-mono text-[0.8rem] text-content-primary opacity-90",
23
+ tick: "mt-[2px] shrink-0 text-intent-success",
24
+ action: "mt-auto border-t border-edge-strong pt-4"
25
+ }
26
+ });
27
+ var PricingTier = forwardRef(function PricingTier2({ name, price, period, description, features, accent = "primary", badge, action, className, ...props }, ref) {
28
+ const styles = pricingTier();
29
+ return /* @__PURE__ */ jsxs(
30
+ Card,
31
+ {
32
+ ref,
33
+ variant: "panel",
34
+ accent,
35
+ "data-slot": "pricing-tier",
36
+ ...props,
37
+ className: styles.root({ class: className }),
38
+ children: [
39
+ badge ? (
40
+ // Hung on the card's top edge rather than set in its flow: in flow it
41
+ // pushes this tier's name, price and list below its neighbours'.
42
+ /* @__PURE__ */ jsx("span", { "data-slot": "pricing-tier-badge", className: styles.badge({ class: ACCENT_CLASS_MAPS.FILL[canonicalAccent(accent)] }), children: badge })
43
+ ) : null,
44
+ /* @__PURE__ */ jsxs("div", { "data-slot": "pricing-tier-body", children: [
45
+ /* @__PURE__ */ jsx("h3", { className: styles.name(), children: name }),
46
+ /* @__PURE__ */ jsxs("div", { "data-slot": "pricing-tier-price", className: styles.priceRow(), children: [
47
+ /* @__PURE__ */ jsx("span", { className: styles.price({ class: accentTextClass(accent) }), children: price }),
48
+ period ? /* @__PURE__ */ jsx("span", { className: styles.period(), children: period }) : null
49
+ ] }),
50
+ description ? /* @__PURE__ */ jsx("p", { className: styles.description(), children: description }) : null,
51
+ features && features.length > 0 ? /* @__PURE__ */ jsx("ul", { "data-slot": "pricing-tier-features", className: styles.features(), children: features.map((item, i) => /* @__PURE__ */ jsxs("li", { className: styles.feature(), children: [
52
+ /* @__PURE__ */ jsx(Check, { size: 14, "aria-hidden": "true", className: styles.tick() }),
53
+ /* @__PURE__ */ jsx("span", { children: item })
54
+ ] }, typeof item === "string" ? item : i)) }) : null
55
+ ] }),
56
+ action ? /* @__PURE__ */ jsx("div", { "data-slot": "pricing-tier-action", className: styles.action(), children: action }) : null
57
+ ]
58
+ }
59
+ );
60
+ });
61
+ export {
62
+ PricingGrid,
63
+ PricingTier
64
+ };
65
+ //# sourceMappingURL=PricingGrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/marketing/PricingGrid.tsx"],"sourcesContent":["import { forwardRef } from 'react';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { Check } from 'lucide-react';\nimport { recipe } from '../../lib/recipe';\nimport { ACCENT_CLASS_MAPS, accentTextClass, canonicalAccent } from '../../lib/accentClasses';\nimport { Card } from '../Card';\nimport { SectionGrid } from './SectionGrid';\nimport type { SectionAlign, SectionColumns } from './SectionGrid';\n\nexport interface PricingGridProps\n extends Omit<HTMLAttributes<HTMLElement>, 'title' | 'className'> {\n /**\n * The section's heading, rendered as its `<h2>`. When present the section is\n * named by it, so it becomes a landmark a screen reader can list.\n */\n title?: ReactNode;\n /** A line under the heading — billing terms, a trial, a guarantee. */\n description?: ReactNode;\n /**\n * How many columns at desktop widths: `2`, `3` (the default) or `4`. Match\n * it to the number of tiers. Below `md` the tiers stack in one column.\n */\n columns?: SectionColumns;\n /** Where the heading block sits — `center` (the default) or `start`. */\n align?: SectionAlign;\n /** The tiers — normally `PricingTier`s. Every cell in a row stretches to the tallest. */\n children?: ReactNode;\n /** Merged into the `<section>`'s classes. Spacing between sections is the page's to set. */\n className?: string;\n}\n\n/**\n * A marketing section of price plans: an optional heading and line, over a\n * grid of {@link PricingTier} cells stretched to one height so their actions\n * line up.\n *\n * Holds no prices, plan names or currency. The same grid is a SaaS plan table,\n * a consulting rate card or a donation ladder.\n *\n * @example\n * ```tsx\n * <PricingGrid title=\"Pricing\" columns={2}>\n * <PricingTier name=\"Solo\" price=\"$0\" period=\"/month\" features={['One workspace']} />\n * <PricingTier name=\"Team\" price=\"$12\" period=\"/seat\" badge=\"POPULAR\" accent=\"tertiary\" />\n * </PricingGrid>\n * ```\n */\nexport const PricingGrid = forwardRef<HTMLElement, PricingGridProps>(function PricingGrid(props, ref) {\n return <SectionGrid ref={ref} slot=\"pricing-grid\" {...props} />;\n});\n\n/**\n * The roles a tier's accent may take. The emphasis roles less `quiet`, because\n * the accent colours the price and is the natural `variant` for the tier's\n * `Button`, and `Button` omits `quiet`: inverse text on a quiet fill is not a\n * gated pair.\n */\nexport type PricingTierAccent = 'primary' | 'secondary' | 'tertiary';\n\n/**\n * One tier as data — the shape `SaasLandingPage`'s `pricingTiers` takes.\n *\n * It shares the component's name because it was public under that name before\n * the component existed: the type `PricingTier` is this record, the value\n * `PricingTier` is the component. `highlighted` and `ctaText` are the landing\n * page's own vocabulary; the component takes a `badge` and an `action` instead,\n * so that no label is decided on the caller's behalf.\n */\nexport interface PricingTier {\n name: string;\n price: string;\n period?: string;\n description: string;\n features: string[];\n /**\n * The tier's accent, used three ways — `Card accent`, the price, and\n * `Button variant` for the CTA. See {@link PricingTierAccent}.\n */\n accent: PricingTierAccent;\n highlighted?: boolean;\n ctaText?: string;\n}\n\nexport interface PricingTierProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** The plan's name, set as the tier's `<h3>`. */\n name: ReactNode;\n /** The price as displayed — currency and all, since only the caller knows the locale. */\n price: ReactNode;\n /** What the price is per — `/month`, `/seat`, `one-off`. Set small, in the muted ink. */\n period?: ReactNode;\n /** A line on who the plan is for. */\n description?: ReactNode;\n /** What the plan includes, one entry per line, each ticked. */\n features?: readonly ReactNode[];\n /**\n * The semantic accent for the stripe and the price. `primary` by default.\n * Pass the same role as the action `Button`'s `variant` so the tier reads as\n * one colour.\n */\n accent?: PricingTierAccent;\n /**\n * A label in the tier's header strip — `POPULAR`, `BEST VALUE`. None by\n * default: which tier is recommended, and what that is called, is the page's\n * decision.\n */\n badge?: string;\n /**\n * The call to action, pinned to the bottom of the tier below a rule — a\n * `Button`, normally in the tier's accent and full width. Omit it for a\n * tier with nothing to choose.\n */\n action?: ReactNode;\n /** Merged into the tier's classes. */\n className?: string;\n}\n\nconst pricingTier = recipe({\n slots: {\n root: 'relative flex flex-col justify-between',\n badge: 'absolute -top-3 left-4 inline-block px-2 py-[0.15rem] font-mono text-[0.65rem] font-bold uppercase text-content-inverse',\n name: 'mb-2 font-display text-[1.1rem] font-extrabold uppercase text-content-primary',\n priceRow: 'mb-4',\n price: 'font-display text-[3rem] font-black',\n period: 'font-mono text-[0.85rem] text-content-muted',\n description: 'mb-6 font-mono text-[0.75rem] leading-[1.5] text-content-primary opacity-80',\n features: 'm-0 mb-6 list-none p-0',\n feature: 'mb-[0.6rem] flex items-start gap-2 font-mono text-[0.8rem] text-content-primary opacity-90',\n tick: 'mt-[2px] shrink-0 text-intent-success',\n action: 'mt-auto border-t border-edge-strong pt-4',\n },\n});\n\n/**\n * One plan in a {@link PricingGrid}: name, price, a line about it, a ticked\n * feature list, and an action pinned to the bottom.\n *\n * A `Card` panel in a column, so the action sits on the tier's floor however\n * long its list is, and tiers of different lengths in one row keep their\n * actions level. Holds no labels of its own: the badge, the action's text and\n * whether there is one at all come from the caller.\n *\n * @example\n * ```tsx\n * <PricingTier\n * name=\"Team\"\n * price=\"$12\"\n * period=\"/seat\"\n * description=\"For a team that ships together.\"\n * features={['Shared workspaces', 'Audit log']}\n * accent=\"tertiary\"\n * badge=\"POPULAR\"\n * action={<Button variant=\"tertiary\" bracketed className=\"w-full justify-center\">START</Button>}\n * />\n * ```\n */\nexport const PricingTier = forwardRef<HTMLDivElement, PricingTierProps>(function PricingTier(\n { name, price, period, description, features, accent = 'primary', badge, action, className, ...props },\n ref,\n) {\n const styles = pricingTier();\n return (\n <Card\n ref={ref}\n variant=\"panel\"\n accent={accent}\n data-slot=\"pricing-tier\"\n {...props}\n className={styles.root({ class: className })}\n >\n {badge ? (\n // Hung on the card's top edge rather than set in its flow: in flow it\n // pushes this tier's name, price and list below its neighbours'.\n <span data-slot=\"pricing-tier-badge\" className={styles.badge({ class: ACCENT_CLASS_MAPS.FILL[canonicalAccent(accent)] })}>\n {badge}\n </span>\n ) : null}\n <div data-slot=\"pricing-tier-body\">\n <h3 className={styles.name()}>{name}</h3>\n <div data-slot=\"pricing-tier-price\" className={styles.priceRow()}>\n <span className={styles.price({ class: accentTextClass(accent) })}>{price}</span>\n {period ? <span className={styles.period()}>{period}</span> : null}\n </div>\n {description ? <p className={styles.description()}>{description}</p> : null}\n {features && features.length > 0 ? (\n <ul data-slot=\"pricing-tier-features\" className={styles.features()}>\n {features.map((item, i) => (\n <li key={typeof item === 'string' ? item : i} className={styles.feature()}>\n <Check size={14} aria-hidden=\"true\" className={styles.tick()} />\n <span>{item}</span>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n {action ? (\n <div data-slot=\"pricing-tier-action\" className={styles.action()}>\n {action}\n </div>\n ) : null}\n </Card>\n );\n});\n"],"mappings":";AAAA,SAAS,kBAAkB;AAE3B,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,mBAAmB,iBAAiB,uBAAuB;AACpE,SAAS,YAAY;AACrB,SAAS,mBAAmB;AA0CnB,cAkID,YAlIC;AADF,IAAM,cAAc,WAA0C,SAASA,aAAY,OAAO,KAAK;AACpG,SAAO,oBAAC,eAAY,KAAU,MAAK,gBAAgB,GAAG,OAAO;AAC/D,CAAC;AAmED,IAAM,cAAc,OAAO;AAAA,EACzB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ;AAAA,EACV;AACF,CAAC;AAyBM,IAAM,cAAc,WAA6C,SAASC,aAC/E,EAAE,MAAM,OAAO,QAAQ,aAAa,UAAU,SAAS,WAAW,OAAO,QAAQ,WAAW,GAAG,MAAM,GACrG,KACA;AACA,QAAM,SAAS,YAAY;AAC3B,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR;AAAA,MACA,aAAU;AAAA,MACT,GAAG;AAAA,MACJ,WAAW,OAAO,KAAK,EAAE,OAAO,UAAU,CAAC;AAAA,MAE1C;AAAA;AAAA;AAAA;AAAA,UAGC,oBAAC,UAAK,aAAU,sBAAqB,WAAW,OAAO,MAAM,EAAE,OAAO,kBAAkB,KAAK,gBAAgB,MAAM,CAAC,EAAE,CAAC,GACpH,iBACH;AAAA,YACE;AAAA,QACJ,qBAAC,SAAI,aAAU,qBACb;AAAA,8BAAC,QAAG,WAAW,OAAO,KAAK,GAAI,gBAAK;AAAA,UACpC,qBAAC,SAAI,aAAU,sBAAqB,WAAW,OAAO,SAAS,GAC7D;AAAA,gCAAC,UAAK,WAAW,OAAO,MAAM,EAAE,OAAO,gBAAgB,MAAM,EAAE,CAAC,GAAI,iBAAM;AAAA,YACzE,SAAS,oBAAC,UAAK,WAAW,OAAO,OAAO,GAAI,kBAAO,IAAU;AAAA,aAChE;AAAA,UACC,cAAc,oBAAC,OAAE,WAAW,OAAO,YAAY,GAAI,uBAAY,IAAO;AAAA,UACtE,YAAY,SAAS,SAAS,IAC7B,oBAAC,QAAG,aAAU,yBAAwB,WAAW,OAAO,SAAS,GAC9D,mBAAS,IAAI,CAAC,MAAM,MACnB,qBAAC,QAA6C,WAAW,OAAO,QAAQ,GACtE;AAAA,gCAAC,SAAM,MAAM,IAAI,eAAY,QAAO,WAAW,OAAO,KAAK,GAAG;AAAA,YAC9D,oBAAC,UAAM,gBAAK;AAAA,eAFL,OAAO,SAAS,WAAW,OAAO,CAG3C,CACD,GACH,IACE;AAAA,WACN;AAAA,QACC,SACC,oBAAC,SAAI,aAAU,uBAAsB,WAAW,OAAO,OAAO,GAC3D,kBACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ,CAAC;","names":["PricingGrid","PricingTier"]}
@@ -0,0 +1,51 @@
1
+ // src/components/marketing/SectionGrid.tsx
2
+ import { forwardRef, useId } from "react";
3
+ import { recipe } from "../../lib/recipe.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ var sectionGrid = recipe({
6
+ slots: {
7
+ root: "my-12",
8
+ header: "mb-12",
9
+ heading: "font-display text-[2rem] font-black uppercase text-content-primary",
10
+ description: "mt-4 max-w-2xl font-mono text-sm leading-relaxed text-content-secondary",
11
+ grid: "grid grid-cols-1 items-stretch gap-8"
12
+ },
13
+ variants: {
14
+ align: {
15
+ center: { header: "text-center", description: "mx-auto" },
16
+ start: { header: "text-left" }
17
+ },
18
+ columns: {
19
+ 2: { grid: "md:grid-cols-2" },
20
+ 3: { grid: "md:grid-cols-3" },
21
+ 4: { grid: "md:grid-cols-2 lg:grid-cols-4" }
22
+ }
23
+ },
24
+ defaultVariants: { align: "center", columns: 3 }
25
+ });
26
+ var SectionGrid = forwardRef(function SectionGrid2({ title, description, columns = 3, align = "center", children, className, slot, ...props }, ref) {
27
+ const styles = sectionGrid({ align, columns });
28
+ const headingId = useId();
29
+ const hasHeader = Boolean(title) || Boolean(description);
30
+ return /* @__PURE__ */ jsxs(
31
+ "section",
32
+ {
33
+ ref,
34
+ "data-slot": slot,
35
+ "aria-labelledby": title ? headingId : void 0,
36
+ ...props,
37
+ className: styles.root({ class: className }),
38
+ children: [
39
+ hasHeader ? /* @__PURE__ */ jsxs("div", { "data-slot": `${slot}-header`, className: styles.header(), children: [
40
+ title ? /* @__PURE__ */ jsx("h2", { id: headingId, className: styles.heading(), children: title }) : null,
41
+ description ? /* @__PURE__ */ jsx("p", { className: styles.description(), children: description }) : null
42
+ ] }) : null,
43
+ /* @__PURE__ */ jsx("div", { "data-slot": `${slot}-items`, className: styles.grid(), children })
44
+ ]
45
+ }
46
+ );
47
+ });
48
+ export {
49
+ SectionGrid
50
+ };
51
+ //# sourceMappingURL=SectionGrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/marketing/SectionGrid.tsx"],"sourcesContent":["import { forwardRef, useId } from 'react';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { recipe } from '../../lib/recipe';\n\n/** How many columns a marketing grid settles into at desktop widths. */\nexport type SectionColumns = 2 | 3 | 4;\n\n/** Where a section's heading block sits. */\nexport type SectionAlign = 'center' | 'start';\n\nexport interface SectionGridProps\n extends Omit<HTMLAttributes<HTMLElement>, 'title' | 'className'> {\n /** The section's `<h2>`. When present it also names the section. */\n title?: ReactNode;\n /** A line under the heading. */\n description?: ReactNode;\n /** Desktop column count. The grid is a single column below `md` whatever this says. */\n columns?: SectionColumns;\n /** Where the heading and description sit. Centred by default. */\n align?: SectionAlign;\n /** The grid's cells. */\n children?: ReactNode;\n /** Merged into the `<section>`'s classes. */\n className?: string;\n /** The `data-slot` prefix, so each public section keeps its own names. */\n slot: string;\n}\n\n// Shared by `FeatureGrid` and `PricingGrid`: one heading block over one grid.\n// The column strings are written out in full because Tailwind's scanner reads\n// source text, and a template such as `md:grid-cols-${n}` generates no CSS.\nconst sectionGrid = recipe({\n slots: {\n root: 'my-12',\n header: 'mb-12',\n heading: 'font-display text-[2rem] font-black uppercase text-content-primary',\n description: 'mt-4 max-w-2xl font-mono text-sm leading-relaxed text-content-secondary',\n grid: 'grid grid-cols-1 items-stretch gap-8',\n },\n variants: {\n align: {\n center: { header: 'text-center', description: 'mx-auto' },\n start: { header: 'text-left' },\n },\n columns: {\n 2: { grid: 'md:grid-cols-2' },\n 3: { grid: 'md:grid-cols-3' },\n 4: { grid: 'md:grid-cols-2 lg:grid-cols-4' },\n },\n },\n defaultVariants: { align: 'center', columns: 3 },\n});\n\n/**\n * The layout `FeatureGrid` and `PricingGrid` share: an optional heading block\n * over a responsive grid. Internal — the two public sections differ only in\n * what they put in the cells, so they are one implementation with two names.\n */\nexport const SectionGrid = forwardRef<HTMLElement, SectionGridProps>(function SectionGrid(\n { title, description, columns = 3, align = 'center', children, className, slot, ...props },\n ref,\n) {\n const styles = sectionGrid({ align, columns });\n const headingId = useId();\n const hasHeader = Boolean(title) || Boolean(description);\n\n return (\n <section\n ref={ref}\n data-slot={slot}\n aria-labelledby={title ? headingId : undefined}\n {...props}\n className={styles.root({ class: className })}\n >\n {hasHeader ? (\n <div data-slot={`${slot}-header`} className={styles.header()}>\n {title ? (\n <h2 id={headingId} className={styles.heading()}>\n {title}\n </h2>\n ) : null}\n {description ? <p className={styles.description()}>{description}</p> : null}\n </div>\n ) : null}\n <div data-slot={`${slot}-items`} className={styles.grid()}>\n {children}\n </div>\n </section>\n );\n});\n"],"mappings":";AAAA,SAAS,YAAY,aAAa;AAElC,SAAS,cAAc;AAyEf,SAEI,KAFJ;AA5CR,IAAM,cAAc,OAAO;AAAA,EACzB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,MACL,QAAQ,EAAE,QAAQ,eAAe,aAAa,UAAU;AAAA,MACxD,OAAO,EAAE,QAAQ,YAAY;AAAA,IAC/B;AAAA,IACA,SAAS;AAAA,MACP,GAAG,EAAE,MAAM,iBAAiB;AAAA,MAC5B,GAAG,EAAE,MAAM,iBAAiB;AAAA,MAC5B,GAAG,EAAE,MAAM,gCAAgC;AAAA,IAC7C;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,OAAO,UAAU,SAAS,EAAE;AACjD,CAAC;AAOM,IAAM,cAAc,WAA0C,SAASA,aAC5E,EAAE,OAAO,aAAa,UAAU,GAAG,QAAQ,UAAU,UAAU,WAAW,MAAM,GAAG,MAAM,GACzF,KACA;AACA,QAAM,SAAS,YAAY,EAAE,OAAO,QAAQ,CAAC;AAC7C,QAAM,YAAY,MAAM;AACxB,QAAM,YAAY,QAAQ,KAAK,KAAK,QAAQ,WAAW;AAEvD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAW;AAAA,MACX,mBAAiB,QAAQ,YAAY;AAAA,MACpC,GAAG;AAAA,MACJ,WAAW,OAAO,KAAK,EAAE,OAAO,UAAU,CAAC;AAAA,MAE1C;AAAA,oBACC,qBAAC,SAAI,aAAW,GAAG,IAAI,WAAW,WAAW,OAAO,OAAO,GACxD;AAAA,kBACC,oBAAC,QAAG,IAAI,WAAW,WAAW,OAAO,QAAQ,GAC1C,iBACH,IACE;AAAA,UACH,cAAc,oBAAC,OAAE,WAAW,OAAO,YAAY,GAAI,uBAAY,IAAO;AAAA,WACzE,IACE;AAAA,QACJ,oBAAC,SAAI,aAAW,GAAG,IAAI,UAAU,WAAW,OAAO,KAAK,GACrD,UACH;AAAA;AAAA;AAAA,EACF;AAEJ,CAAC;","names":["SectionGrid"]}
@@ -0,0 +1,28 @@
1
+ // src/components/paginationRange.ts
2
+ function paginationSlots(siblings = 1) {
3
+ return 5 + siblings * 2;
4
+ }
5
+ function range(from, to) {
6
+ return Array.from({ length: Math.max(0, to - from + 1) }, (_, i) => from + i);
7
+ }
8
+ function paginationRange(totalPages, currentPage, siblings = 1) {
9
+ const total = Math.floor(totalPages);
10
+ if (!(total >= 1)) return [];
11
+ const slots = paginationSlots(siblings);
12
+ if (total <= slots) return range(1, total);
13
+ const current = Math.min(Math.max(Math.floor(currentPage) || 1, 1), total);
14
+ const windowStart = Math.max(Math.min(current - siblings, total - 2 - siblings * 2), 3);
15
+ const windowEnd = Math.min(Math.max(current + siblings, 3 + siblings * 2), total - 2);
16
+ return [
17
+ 1,
18
+ windowStart > 3 ? "ellipsis-start" : 2,
19
+ ...range(windowStart, windowEnd),
20
+ windowEnd < total - 2 ? "ellipsis-end" : total - 1,
21
+ total
22
+ ];
23
+ }
24
+ export {
25
+ paginationRange,
26
+ paginationSlots
27
+ };
28
+ //# sourceMappingURL=paginationRange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/paginationRange.ts"],"sourcesContent":["/**\n * Which pages a `Pagination` strip shows, and where the gaps go.\n *\n * The one property worth specifying rather than discovering: **the number of\n * items is a function of `totalPages` alone.** It never depends on where the\n * current page is. A strip that grows and shrinks as the reader clicks moves\n * the control they are about to press out from under the pointer, which is\n * worse than having no numbers at all.\n *\n * The shape is first page, last page, a window of `siblings` either side of the\n * current page, and at most two ellipses:\n *\n * ```\n * 1 2 3 4 5 … 20 current near the start\n * 1 … 6 7 8 … 20 current in the middle\n * 1 … 16 17 18 19 20 current near the end\n * ```\n *\n * Seven slots with the default single sibling. When the window touches an end\n * the slot an ellipsis would have taken is given to a page instead, which is\n * what keeps the count fixed. An ellipsis therefore always stands for **two or\n * more** pages: hiding exactly one page behind `…` costs the same slot as\n * showing it.\n *\n * With `totalPages` at or under the slot count every page is shown and there is\n * no ellipsis — `1 2 3 4 5 6 7` is shorter to read than any abbreviation of it.\n */\n\n/** A page number, or one of the two gaps. Each gap has its own key. */\nexport type PaginationRangeItem = number | 'ellipsis-start' | 'ellipsis-end';\n\n/** The fixed item count for a strip with `siblings` pages either side of current. */\nexport function paginationSlots(siblings = 1): number {\n // first + last + two gaps + current + the window either side of it\n return 5 + siblings * 2;\n}\n\nfunction range(from: number, to: number): number[] {\n return Array.from({ length: Math.max(0, to - from + 1) }, (_, i) => from + i);\n}\n\n/**\n * The items to render, in order.\n *\n * `currentPage` is clamped into `1..totalPages`, so an out-of-range value\n * renders the nearest real page rather than a strip with no current item.\n * A `totalPages` below 1 has no pages and returns an empty list.\n */\nexport function paginationRange(\n totalPages: number,\n currentPage: number,\n siblings = 1,\n): PaginationRangeItem[] {\n const total = Math.floor(totalPages);\n if (!(total >= 1)) return [];\n\n const slots = paginationSlots(siblings);\n if (total <= slots) return range(1, total);\n\n const current = Math.min(Math.max(Math.floor(currentPage) || 1, 1), total);\n\n // The window, slid so it never runs into the first or last page. Pushing it\n // inward at the ends is what hands the unused ellipsis slot to a page.\n const windowStart = Math.max(Math.min(current - siblings, total - 2 - siblings * 2), 3);\n const windowEnd = Math.min(Math.max(current + siblings, 3 + siblings * 2), total - 2);\n\n return [\n 1,\n windowStart > 3 ? 'ellipsis-start' : 2,\n ...range(windowStart, windowEnd),\n windowEnd < total - 2 ? 'ellipsis-end' : total - 1,\n total,\n ];\n}\n"],"mappings":";AAgCO,SAAS,gBAAgB,WAAW,GAAW;AAEpD,SAAO,IAAI,WAAW;AACxB;AAEA,SAAS,MAAM,MAAc,IAAsB;AACjD,SAAO,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,KAAK,OAAO,CAAC,EAAE,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC;AAC9E;AASO,SAAS,gBACd,YACA,aACA,WAAW,GACY;AACvB,QAAM,QAAQ,KAAK,MAAM,UAAU;AACnC,MAAI,EAAE,SAAS,GAAI,QAAO,CAAC;AAE3B,QAAM,QAAQ,gBAAgB,QAAQ;AACtC,MAAI,SAAS,MAAO,QAAO,MAAM,GAAG,KAAK;AAEzC,QAAM,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,WAAW,KAAK,GAAG,CAAC,GAAG,KAAK;AAIzE,QAAM,cAAc,KAAK,IAAI,KAAK,IAAI,UAAU,UAAU,QAAQ,IAAI,WAAW,CAAC,GAAG,CAAC;AACtF,QAAM,YAAY,KAAK,IAAI,KAAK,IAAI,UAAU,UAAU,IAAI,WAAW,CAAC,GAAG,QAAQ,CAAC;AAEpF,SAAO;AAAA,IACL;AAAA,IACA,cAAc,IAAI,mBAAmB;AAAA,IACrC,GAAG,MAAM,aAAa,SAAS;AAAA,IAC/B,YAAY,QAAQ,IAAI,iBAAiB,QAAQ;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,14 @@
1
+ "use client";
2
+
3
+ // src/components/portalTheme.ts
4
+ import { useContext } from "react";
5
+ import { THEME_ATTRIBUTE } from "./themeInitScript.js";
6
+ import { ScopedLevelContext } from "./themeScope.js";
7
+ function usePortalThemeAttribute() {
8
+ const level = useContext(ScopedLevelContext);
9
+ return level ? { [THEME_ATTRIBUTE]: level } : {};
10
+ }
11
+ export {
12
+ usePortalThemeAttribute
13
+ };
14
+ //# sourceMappingURL=portalTheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/portalTheme.ts"],"sourcesContent":["'use client';\n\nimport { useContext } from 'react';\nimport { THEME_ATTRIBUTE } from './themeInitScript';\nimport { ScopedLevelContext } from './themeScope';\n\n// The Level for a surface portalled out of its provider's subtree.\n//\n// Base UI portals a popup to `body`, outside a `<ThemeProvider scoped>`\n// wrapper's `[data-theme]`, so its role colours resolved from the document's\n// Level rather than the trigger's. Stamping the scoped provider's Level on the\n// portal root puts it back under the right `[data-theme]` selector.\n//\n// Under an unscoped provider the attribute is omitted: that provider writes\n// `documentElement` in an effect, a paint after render, so stamping its state\n// would switch the portal early and could override the init script's Level\n// during hydration. The portal inherits the document's Level instead. Not\n// exported from the package.\nexport function usePortalThemeAttribute(): Record<string, string> {\n const level = useContext(ScopedLevelContext);\n return level ? { [THEME_ATTRIBUTE]: level } : {};\n}\n"],"mappings":";;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AAc5B,SAAS,0BAAkD;AAChE,QAAM,QAAQ,WAAW,kBAAkB;AAC3C,SAAO,QAAQ,EAAE,CAAC,eAAe,GAAG,MAAM,IAAI,CAAC;AACjD;","names":[]}
@@ -0,0 +1,201 @@
1
+ // src/components/saas/SaasLandingPage.tsx
2
+ import { ArrowRight, Zap, Shield, Cpu } from "lucide-react";
3
+ import { Button } from "../Button.js";
4
+ import { Badge } from "../Badge.js";
5
+ import { AsciiDivider } from "../AsciiDivider.js";
6
+ import { Hero } from "../marketing/Hero.js";
7
+ import { FeatureGrid, Feature } from "../marketing/FeatureGrid.js";
8
+ import { PricingGrid, PricingTier } from "../marketing/PricingGrid.js";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ var DEFAULT_PRICING_TIERS = [
11
+ {
12
+ name: "STARTER",
13
+ price: "$0",
14
+ period: "/month",
15
+ description: "Local-first storage and the core workflow, for a single user",
16
+ features: ["Single Workspace", "Local-First Storage", "Core Workflow", "Community Support"],
17
+ accent: "primary",
18
+ ctaText: "START FREE"
19
+ },
20
+ {
21
+ name: "PRO ENGINE",
22
+ price: "$29",
23
+ period: "/month",
24
+ description: "Automation, scheduled jobs, and versioned backups",
25
+ features: ["Everything in Starter", "Automation Rules", "Scheduled Jobs", "Versioned Backups", "Usage Analytics"],
26
+ accent: "tertiary",
27
+ highlighted: true,
28
+ ctaText: "DEPLOY PRO ENGINE"
29
+ },
30
+ {
31
+ name: "ENTERPRISE DECK",
32
+ price: "$99",
33
+ period: "/month",
34
+ description: "Multi-workspace deployment, SSO, and a custom SLA",
35
+ features: ["Unlimited Workspaces", "SSO & Audit Log", "Custom Design System Themes", "Dedicated Support & SLAs", "Self-Hosted Options"],
36
+ accent: "secondary",
37
+ ctaText: "CONTACT SALES"
38
+ }
39
+ ];
40
+ var DEFAULT_DEPLOY_LOG = `$ platform deploy --environment production
41
+ [\u2713] Connecting to local datastore... OK
42
+ [\u2713] Verifying 1,420 records against checksum... OK
43
+ [\u2713] Running automation rules... APPLIED
44
+ [\u2713] Versioned backup written to ./backups/2026-01-01/
45
+ [*] Surface ready! Server active on http://localhost:8000`;
46
+ var MONO = "var(--ds-font-mono)";
47
+ var DIVIDER_STYLE = {
48
+ margin: "3rem 0",
49
+ color: "var(--ds-accent-primary)",
50
+ fontFamily: MONO,
51
+ fontSize: "0.85rem"
52
+ };
53
+ var FEATURES = [
54
+ {
55
+ title: "REAL-TIME SYNC",
56
+ accent: "primary",
57
+ icon: /* @__PURE__ */ jsx(Cpu, { size: 28 }),
58
+ desc: "Automatic delta reconciliation between live banking APIs and local single-player SQLite databases."
59
+ },
60
+ {
61
+ title: "RULE ENGINE",
62
+ accent: "tertiary",
63
+ icon: /* @__PURE__ */ jsx(Zap, { size: 28 }),
64
+ desc: "Custom automated regex & payee matching rules to categorize statement imports effortlessly."
65
+ },
66
+ {
67
+ title: "DRIVE BACKUPS",
68
+ accent: "secondary",
69
+ icon: /* @__PURE__ */ jsx(Shield, { size: 28 }),
70
+ desc: "Periodic, atomic SQLite file backups synced directly to Google Drive without external vendor lock-in."
71
+ }
72
+ ];
73
+ function DeployPreview({ log }) {
74
+ return /* @__PURE__ */ jsxs(
75
+ "div",
76
+ {
77
+ style: {
78
+ maxWidth: "800px",
79
+ margin: "0 auto",
80
+ border: "3px solid var(--ds-border-strong)",
81
+ backgroundColor: "var(--ds-surface-base)",
82
+ boxShadow: "8px 8px 0px 0px var(--ds-accent-primary)",
83
+ overflow: "hidden"
84
+ },
85
+ children: [
86
+ /* @__PURE__ */ jsxs(
87
+ "div",
88
+ {
89
+ style: {
90
+ padding: "0.6rem 1rem",
91
+ borderBottom: "2px solid var(--ds-border-strong)",
92
+ backgroundColor: "var(--ds-text-primary)",
93
+ color: "var(--ds-surface-base)",
94
+ fontFamily: MONO,
95
+ fontWeight: 800,
96
+ fontSize: "0.85rem",
97
+ display: "flex",
98
+ justifyContent: "space-between",
99
+ alignItems: "center"
100
+ },
101
+ children: [
102
+ /* @__PURE__ */ jsx("span", { children: "// TERMINAL_ENGINE_DEMO.sh" }),
103
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
104
+ /* @__PURE__ */ jsx("span", { style: { width: "8px", height: "8px", backgroundColor: "var(--ds-intent-success)", display: "inline-block" } }),
105
+ "STATUS: ACTIVE"
106
+ ] })
107
+ ]
108
+ }
109
+ ),
110
+ /* @__PURE__ */ jsx(
111
+ "pre",
112
+ {
113
+ tabIndex: 0,
114
+ role: "region",
115
+ "aria-label": "Deployment log",
116
+ style: {
117
+ padding: "1.5rem",
118
+ fontFamily: MONO,
119
+ fontSize: "0.9rem",
120
+ color: "var(--ds-intent-success)",
121
+ textAlign: "left",
122
+ overflowX: "auto",
123
+ margin: 0,
124
+ lineHeight: 1.6
125
+ },
126
+ children: log
127
+ }
128
+ )
129
+ ]
130
+ }
131
+ );
132
+ }
133
+ var SaasLandingPage = ({
134
+ title = "HIGH-PERFORMANCE BRUTALIST SAAS PLATFORM",
135
+ subtitle = "Ship faster with real-time sync, automated rules, and zero-compromise design",
136
+ pricingTiers = DEFAULT_PRICING_TIERS,
137
+ deployLog = DEFAULT_DEPLOY_LOG
138
+ }) => {
139
+ return /* @__PURE__ */ jsxs(
140
+ "div",
141
+ {
142
+ "data-slot": "saas-landing-page",
143
+ style: {
144
+ maxWidth: "1200px",
145
+ margin: "0 auto",
146
+ padding: "3rem 1.5rem",
147
+ color: "var(--ds-text-primary)",
148
+ fontFamily: "var(--ds-font-body)"
149
+ },
150
+ children: [
151
+ /* @__PURE__ */ jsx(
152
+ Hero,
153
+ {
154
+ eyebrow: /* @__PURE__ */ jsx(Badge, { accent: "primary", children: "\u26A1 NEXT-GEN SAAS INFRASTRUCTURE" }),
155
+ title,
156
+ subtitle,
157
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
158
+ /* @__PURE__ */ jsxs(Button, { variant: "tertiary", bracketed: true, size: "lg", children: [
159
+ "LAUNCH APPLICATION ",
160
+ /* @__PURE__ */ jsx(ArrowRight, { size: 18 })
161
+ ] }),
162
+ /* @__PURE__ */ jsx(Button, { variant: "default", bracketed: true, size: "lg", children: "EXPLORE ARCHITECTURE" })
163
+ ] }),
164
+ children: deployLog ? /* @__PURE__ */ jsx(DeployPreview, { log: deployLog }) : null
165
+ }
166
+ ),
167
+ /* @__PURE__ */ jsx(AsciiDivider, { style: DIVIDER_STYLE }),
168
+ /* @__PURE__ */ jsx(FeatureGrid, { title: "[ BUILT FOR EXTREME PERFORMANCE & CONTROL ]", children: FEATURES.map((feature) => /* @__PURE__ */ jsx(Feature, { title: feature.title, accent: feature.accent, icon: feature.icon, children: feature.desc }, feature.title)) }),
169
+ /* @__PURE__ */ jsx(AsciiDivider, { style: DIVIDER_STYLE }),
170
+ /* @__PURE__ */ jsx(PricingGrid, { title: "[ TRANSPARENT PRICING ]", children: pricingTiers.map((tier) => /* @__PURE__ */ jsx(
171
+ PricingTier,
172
+ {
173
+ name: tier.name,
174
+ price: tier.price,
175
+ period: tier.period,
176
+ description: tier.description,
177
+ features: tier.features,
178
+ accent: tier.accent,
179
+ badge: tier.highlighted ? "POPULAR" : void 0,
180
+ action: /* @__PURE__ */ jsx(
181
+ Button,
182
+ {
183
+ variant: tier.accent,
184
+ bracketed: true,
185
+ style: { width: "100%", justifyContent: "center", display: "flex" },
186
+ children: tier.ctaText || "SELECT PLAN"
187
+ }
188
+ )
189
+ },
190
+ tier.name
191
+ )) })
192
+ ]
193
+ }
194
+ );
195
+ };
196
+ export {
197
+ DEFAULT_DEPLOY_LOG,
198
+ DEFAULT_PRICING_TIERS,
199
+ SaasLandingPage
200
+ };
201
+ //# sourceMappingURL=SaasLandingPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/saas/SaasLandingPage.tsx"],"sourcesContent":["import type React from 'react';\nimport { ArrowRight, Zap, Shield, Cpu } from 'lucide-react';\nimport { Button } from '../Button';\nimport { Badge } from '../Badge';\nimport { AsciiDivider } from '../AsciiDivider';\nimport { Hero } from '../marketing/Hero';\nimport { FeatureGrid, Feature } from '../marketing/FeatureGrid';\nimport { PricingGrid, PricingTier } from '../marketing/PricingGrid';\nimport type { PricingTier as PricingTierData } from '../marketing/PricingGrid';\n\n/**\n * Placeholder pricing, deliberately generic.\n *\n * This is a layout in a shared package, so the sample copy has to demonstrate\n * the *shape* — three tiers, one highlighted, escalating feature lists — without\n * describing a particular product. The previous defaults were lifted from a\n * personal-finance app (bank reconciliation, sync engines) and read as that\n * product's marketing site rather than as a design-system example.\n */\nexport const DEFAULT_PRICING_TIERS: PricingTierData[] = [\n {\n name: 'STARTER',\n price: '$0',\n period: '/month',\n description: 'Local-first storage and the core workflow, for a single user',\n features: ['Single Workspace', 'Local-First Storage', 'Core Workflow', 'Community Support'],\n accent: 'primary',\n ctaText: 'START FREE'\n },\n {\n name: 'PRO ENGINE',\n price: '$29',\n period: '/month',\n description: 'Automation, scheduled jobs, and versioned backups',\n features: ['Everything in Starter', 'Automation Rules', 'Scheduled Jobs', 'Versioned Backups', 'Usage Analytics'],\n accent: 'tertiary',\n highlighted: true,\n ctaText: 'DEPLOY PRO ENGINE'\n },\n {\n name: 'ENTERPRISE DECK',\n price: '$99',\n period: '/month',\n description: 'Multi-workspace deployment, SSO, and a custom SLA',\n features: ['Unlimited Workspaces', 'SSO & Audit Log', 'Custom Design System Themes', 'Dedicated Support & SLAs', 'Self-Hosted Options'],\n accent: 'secondary',\n ctaText: 'CONTACT SALES'\n }\n];\n\n/**\n * Sample terminal output for the deploy section. A prop rather than inline JSX\n * so a consumer can show their own build, and generic for the same reason as the\n * pricing above.\n */\nexport const DEFAULT_DEPLOY_LOG = `$ platform deploy --environment production\n[✓] Connecting to local datastore... OK\n[✓] Verifying 1,420 records against checksum... OK\n[✓] Running automation rules... APPLIED\n[✓] Versioned backup written to ./backups/2026-01-01/\n[*] Surface ready! Server active on http://localhost:8000`;\n\nexport interface SaasLandingPageProps {\n /** The hero's headline. */\n title?: string;\n /** The line under the headline. */\n subtitle?: string;\n /** The plans, as data — one `PricingTier` each. `DEFAULT_PRICING_TIERS` by default. */\n pricingTiers?: PricingTierData[];\n /** Terminal output for the deploy section. Pass `''` to hide it. */\n deployLog?: string;\n}\n\nconst MONO = 'var(--ds-font-mono)';\n\nconst DIVIDER_STYLE: React.CSSProperties = {\n margin: '3rem 0',\n color: 'var(--ds-accent-primary)',\n fontFamily: MONO,\n fontSize: '0.85rem',\n};\n\n// This page's feature copy. It lives here rather than in `Feature`, which holds\n// none: the landing page is one composition of the marketing sections, and its\n// words are its own.\nconst FEATURES = [\n {\n title: 'REAL-TIME SYNC',\n accent: 'primary',\n icon: <Cpu size={28} />,\n desc: 'Automatic delta reconciliation between live banking APIs and local single-player SQLite databases.',\n },\n {\n title: 'RULE ENGINE',\n accent: 'tertiary',\n icon: <Zap size={28} />,\n desc: 'Custom automated regex & payee matching rules to categorize statement imports effortlessly.',\n },\n {\n title: 'DRIVE BACKUPS',\n accent: 'secondary',\n icon: <Shield size={28} />,\n desc: 'Periodic, atomic SQLite file backups synced directly to Google Drive without external vendor lock-in.',\n },\n] as const;\n\n// The hero's media: a terminal window replaying `deployLog`. Specific to this\n// page, so it is passed to `Hero` as children rather than being a slot of it.\n// The log scrolls at narrow widths, which is why the `<pre>` takes a tabIndex —\n// see `CodeBlock`.\nfunction DeployPreview({ log }: { log: string }) {\n return (\n <div\n style={{\n maxWidth: '800px',\n margin: '0 auto',\n border: '3px solid var(--ds-border-strong)',\n backgroundColor: 'var(--ds-surface-base)',\n boxShadow: '8px 8px 0px 0px var(--ds-accent-primary)',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n padding: '0.6rem 1rem',\n borderBottom: '2px solid var(--ds-border-strong)',\n backgroundColor: 'var(--ds-text-primary)',\n color: 'var(--ds-surface-base)',\n fontFamily: MONO,\n fontWeight: 800,\n fontSize: '0.85rem',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span>// TERMINAL_ENGINE_DEMO.sh</span>\n <span style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <span style={{ width: '8px', height: '8px', backgroundColor: 'var(--ds-intent-success)', display: 'inline-block' }} />\n STATUS: ACTIVE\n </span>\n </div>\n <pre\n tabIndex={0}\n role=\"region\"\n aria-label=\"Deployment log\"\n style={{\n padding: '1.5rem',\n fontFamily: MONO,\n fontSize: '0.9rem',\n color: 'var(--ds-intent-success)',\n textAlign: 'left',\n overflowX: 'auto',\n margin: 0,\n lineHeight: 1.6,\n }}\n >\n{log}\n </pre>\n </div>\n );\n}\n\n/**\n * A complete SaaS landing page — hero, features and pricing — composed from\n * the marketing sections: `Hero`, a `FeatureGrid` of `Feature`s and a\n * `PricingGrid` of `PricingTier`s.\n *\n * It is one composition of those parts with its copy filled in, kept for the\n * call sites that render it whole. A page that needs a different order, a\n * different set of sections or its own words should compose the sections\n * directly — the `SaaS/LandingPage` stories build two such pages from the same\n * parts.\n */\nexport const SaasLandingPage: React.FC<SaasLandingPageProps> = ({\n title = 'HIGH-PERFORMANCE BRUTALIST SAAS PLATFORM',\n subtitle = 'Ship faster with real-time sync, automated rules, and zero-compromise design',\n pricingTiers = DEFAULT_PRICING_TIERS,\n deployLog = DEFAULT_DEPLOY_LOG,\n}) => {\n return (\n <div data-slot=\"saas-landing-page\"\n style={{\n maxWidth: '1200px',\n margin: '0 auto',\n padding: '3rem 1.5rem',\n color: 'var(--ds-text-primary)',\n fontFamily: 'var(--ds-font-body)',\n }}\n >\n <Hero\n eyebrow={<Badge accent=\"primary\">⚡ NEXT-GEN SAAS INFRASTRUCTURE</Badge>}\n title={title}\n subtitle={subtitle}\n actions={\n <>\n <Button variant=\"tertiary\" bracketed size=\"lg\">\n LAUNCH APPLICATION <ArrowRight size={18} />\n </Button>\n <Button variant=\"default\" bracketed size=\"lg\">\n EXPLORE ARCHITECTURE\n </Button>\n </>\n }\n >\n {deployLog ? <DeployPreview log={deployLog} /> : null}\n </Hero>\n\n <AsciiDivider style={DIVIDER_STYLE} />\n\n <FeatureGrid title=\"[ BUILT FOR EXTREME PERFORMANCE & CONTROL ]\">\n {FEATURES.map((feature) => (\n <Feature key={feature.title} title={feature.title} accent={feature.accent} icon={feature.icon}>\n {feature.desc}\n </Feature>\n ))}\n </FeatureGrid>\n\n <AsciiDivider style={DIVIDER_STYLE} />\n\n <PricingGrid title=\"[ TRANSPARENT PRICING ]\">\n {pricingTiers.map((tier) => (\n <PricingTier\n key={tier.name}\n name={tier.name}\n price={tier.price}\n period={tier.period}\n description={tier.description}\n features={tier.features}\n accent={tier.accent}\n badge={tier.highlighted ? 'POPULAR' : undefined}\n action={\n <Button\n variant={tier.accent}\n bracketed\n style={{ width: '100%', justifyContent: 'center', display: 'flex' }}\n >\n {tier.ctaText || 'SELECT PLAN'}\n </Button>\n }\n />\n ))}\n </PricingGrid>\n </div>\n );\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ // src/components/siteNavContext.ts
4
+ import { createContext, useContext } from "react";
5
+ var SiteNavContext = createContext({
6
+ orientation: "horizontal",
7
+ inMenu: false
8
+ });
9
+ function useSiteNavContext() {
10
+ return useContext(SiteNavContext);
11
+ }
12
+ var COLLAPSE_QUERY = {
13
+ sm: "(min-width: 40rem)",
14
+ md: "(min-width: 48rem)",
15
+ lg: "(min-width: 64rem)"
16
+ };
17
+ var SiteHeaderCollapseContext = createContext(void 0);
18
+ export {
19
+ COLLAPSE_QUERY,
20
+ SiteHeaderCollapseContext,
21
+ SiteNavContext,
22
+ useSiteNavContext
23
+ };
24
+ //# sourceMappingURL=siteNavContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/siteNavContext.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext } from 'react';\nimport type { SiteHeaderCollapse } from './SiteHeader';\nimport type { SiteNavOrientation } from './SiteNav';\n\n// Where a `SiteNavItem` is being rendered, which decides what it renders as.\n//\n// A module of its own for the reason `dialogPopupRef.ts` is one: `src/index.ts`\n// re-exports `SiteNav.tsx` and `MobileNav.tsx` wholesale, and a context in\n// either would be published. Nothing exports this file.\n//\n// - `orientation` — a row in a header, or a column in a drawer or a footer.\n// - `inMenu` — inside a group's `Menu`, where an item is a menu link rather\n// than a list item.\n// - `onNavigate` — called after any item is followed. `MobileNav` sets it to\n// close its drawer: under a client-side router a followed link does not\n// unload the page, so without it the drawer would stay open over the page\n// it just navigated to.\ninterface SiteNavContextValue {\n orientation: SiteNavOrientation;\n inMenu: boolean;\n onNavigate?: () => void;\n}\n\nexport const SiteNavContext = createContext<SiteNavContextValue>({\n orientation: 'horizontal',\n inMenu: false,\n});\n\nexport function useSiteNavContext(): SiteNavContextValue {\n return useContext(SiteNavContext);\n}\n\n// The width at which a `SiteHeader` swaps its `mobileNav` slot out for `nav`,\n// given to a `MobileNav` inside that slot. The slot is hidden by CSS there, but\n// the drawer is portalled to `body` and outside it, so the `MobileNav` has to\n// close itself: the same queries Tailwind's `sm:` / `md:` / `lg:` compile to.\nexport const COLLAPSE_QUERY: Record<SiteHeaderCollapse, string> = {\n sm: '(min-width: 40rem)',\n md: '(min-width: 48rem)',\n lg: '(min-width: 64rem)',\n};\n\nexport const SiteHeaderCollapseContext = createContext<SiteHeaderCollapse | undefined>(undefined);\n"],"mappings":";;;AAEA,SAAS,eAAe,kBAAkB;AAuBnC,IAAM,iBAAiB,cAAmC;AAAA,EAC/D,aAAa;AAAA,EACb,QAAQ;AACV,CAAC;AAEM,SAAS,oBAAyC;AACvD,SAAO,WAAW,cAAc;AAClC;AAMO,IAAM,iBAAqD;AAAA,EAChE,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEO,IAAM,4BAA4B,cAA8C,MAAS;","names":[]}
@@ -0,0 +1,84 @@
1
+ // src/components/slides/Slide.tsx
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ var Slide = ({
4
+ title,
5
+ subtitle,
6
+ children
7
+ }) => {
8
+ return /* @__PURE__ */ jsxs(
9
+ "div",
10
+ {
11
+ "data-slot": "slide",
12
+ style: {
13
+ width: "100%",
14
+ height: "100%",
15
+ display: "flex",
16
+ flexDirection: "column",
17
+ justifyContent: "space-between",
18
+ padding: "3rem 4rem",
19
+ backgroundColor: "var(--ds-surface-base)",
20
+ color: "var(--ds-text-primary)",
21
+ boxSizing: "border-box"
22
+ },
23
+ children: [
24
+ title && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "2rem", borderBottom: "2px solid var(--ds-border-strong)", paddingBottom: "1rem" }, children: [
25
+ /* @__PURE__ */ jsx(
26
+ "h2",
27
+ {
28
+ style: {
29
+ fontFamily: "var(--ds-font-display)",
30
+ fontSize: "2.5rem",
31
+ fontWeight: 800,
32
+ textTransform: "uppercase",
33
+ letterSpacing: "-0.02em",
34
+ color: "var(--ds-text-primary)",
35
+ margin: 0
36
+ },
37
+ children: `[\xA0${title}\xA0]`
38
+ }
39
+ ),
40
+ subtitle && /* @__PURE__ */ jsxs(
41
+ "p",
42
+ {
43
+ style: {
44
+ fontFamily: "var(--ds-font-mono)",
45
+ fontSize: "1rem",
46
+ color: "var(--ds-accent-primary)",
47
+ marginTop: "0.5rem"
48
+ },
49
+ children: [
50
+ "> ",
51
+ subtitle
52
+ ]
53
+ }
54
+ )
55
+ ] }),
56
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, display: "flex", flexDirection: "column", justifyContent: "center" }, children }),
57
+ /* @__PURE__ */ jsxs(
58
+ "div",
59
+ {
60
+ style: {
61
+ marginTop: "2rem",
62
+ paddingTop: "1rem",
63
+ borderTop: "2px solid var(--ds-border-strong)",
64
+ display: "flex",
65
+ justifyContent: "space-between",
66
+ alignItems: "center",
67
+ fontFamily: "var(--ds-font-mono)",
68
+ fontSize: "0.75rem",
69
+ color: "var(--ds-accent-primary)"
70
+ },
71
+ children: [
72
+ /* @__PURE__ */ jsx("span", { children: "// BRUTALIST PRESENTATION ENGINE //" }),
73
+ /* @__PURE__ */ jsx("span", { children: "RYANKELLY.DEV" })
74
+ ]
75
+ }
76
+ )
77
+ ]
78
+ }
79
+ );
80
+ };
81
+ export {
82
+ Slide
83
+ };
84
+ //# sourceMappingURL=Slide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/slides/Slide.tsx"],"sourcesContent":["import type React from 'react';\n\nexport interface SlideProps {\n title?: string;\n subtitle?: string;\n children: React.ReactNode;\n /**\n * Presenter notes for this slide.\n *\n * `Slide` itself never renders them — they are for the presenter, not the\n * audience. `SlideDeck` reads them off its children and shows them in a panel\n * below the frame, toggled with `N` or the notes control, off by default. A\n * slide rendered outside a deck therefore shows nothing, which is correct.\n *\n * Whitespace-only strings count as no notes.\n */\n speakerNotes?: string;\n}\n\nexport const Slide: React.FC<SlideProps> = ({\n title,\n subtitle,\n children,\n}) => {\n return (\n <div data-slot=\"slide\"\n style={{\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'space-between',\n padding: '3rem 4rem',\n backgroundColor: 'var(--ds-surface-base)',\n color: 'var(--ds-text-primary)',\n boxSizing: 'border-box',\n }}\n >\n {/* Slide Header */}\n {title && (\n <div style={{ marginBottom: '2rem', borderBottom: '2px solid var(--ds-border-strong)', paddingBottom: '1rem' }}>\n <h2\n style={{\n fontFamily: 'var(--ds-font-display)',\n fontSize: '2.5rem',\n fontWeight: 800,\n textTransform: 'uppercase',\n letterSpacing: '-0.02em',\n color: 'var(--ds-text-primary)',\n margin: 0,\n }}\n >\n {`[\\u00a0${title}\\u00a0]`}\n </h2>\n {subtitle && (\n <p\n style={{\n fontFamily: 'var(--ds-font-mono)',\n fontSize: '1rem',\n color: 'var(--ds-accent-primary)',\n marginTop: '0.5rem',\n }}\n >\n &gt; {subtitle}\n </p>\n )}\n </div>\n )}\n\n {/* Slide Content Body */}\n <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>\n {children}\n </div>\n\n {/* Slide Footer Branding */}\n <div\n style={{\n marginTop: '2rem',\n paddingTop: '1rem',\n borderTop: '2px solid var(--ds-border-strong)',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n fontFamily: 'var(--ds-font-mono)',\n fontSize: '0.75rem',\n color: 'var(--ds-accent-primary)',\n }}\n >\n <span>// BRUTALIST PRESENTATION ENGINE //</span>\n <span>RYANKELLY.DEV</span>\n </div>\n </div>\n );\n};\n"],"mappings":";AAyCU,cAcE,YAdF;AAtBH,IAAM,QAA8B,CAAC;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,SACE;AAAA,IAAC;AAAA;AAAA,MAAI,aAAU;AAAA,MACb,OAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,eAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,iBAAiB;AAAA,QACjB,OAAO;AAAA,QACP,WAAW;AAAA,MACb;AAAA,MAGC;AAAA,iBACC,qBAAC,SAAI,OAAO,EAAE,cAAc,QAAQ,cAAc,qCAAqC,eAAe,OAAO,GAC3G;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,gBACL,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,eAAe;AAAA,gBACf,eAAe;AAAA,gBACf,OAAO;AAAA,gBACP,QAAQ;AAAA,cACV;AAAA,cAEC,kBAAU,KAAK;AAAA;AAAA,UAClB;AAAA,UACC,YACC;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,gBACL,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,OAAO;AAAA,gBACP,WAAW;AAAA,cACb;AAAA,cACD;AAAA;AAAA,gBACO;AAAA;AAAA;AAAA,UACR;AAAA,WAEJ;AAAA,QAIF,oBAAC,SAAI,OAAO,EAAE,MAAM,GAAG,SAAS,QAAQ,eAAe,UAAU,gBAAgB,SAAS,GACvF,UACH;AAAA,QAGA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,WAAW;AAAA,cACX,YAAY;AAAA,cACZ,WAAW;AAAA,cACX,SAAS;AAAA,cACT,gBAAgB;AAAA,cAChB,YAAY;AAAA,cACZ,YAAY;AAAA,cACZ,UAAU;AAAA,cACV,OAAO;AAAA,YACT;AAAA,YAEA;AAAA,kCAAC,UAAK,iDAAmC;AAAA,cACzC,oBAAC,UAAK,2BAAa;AAAA;AAAA;AAAA,QACrB;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}