markupr 2.1.8 → 2.5.0

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 (300) hide show
  1. package/README.md +292 -15
  2. package/dist/cli/index.mjs +3593 -0
  3. package/dist/main/index.mjs +743 -220
  4. package/dist/mcp/index.mjs +4053 -0
  5. package/package.json +32 -7
  6. package/.claude/commands/review-feedback.md +0 -47
  7. package/.eslintrc.json +0 -35
  8. package/.github/CODEOWNERS +0 -16
  9. package/.github/FUNDING.yml +0 -1
  10. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -56
  11. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -54
  12. package/.github/PULL_REQUEST_TEMPLATE.md +0 -89
  13. package/.github/dependabot.yml +0 -70
  14. package/.github/workflows/ci.yml +0 -184
  15. package/.github/workflows/deploy-landing.yml +0 -134
  16. package/.github/workflows/nightly.yml +0 -288
  17. package/.github/workflows/release.yml +0 -318
  18. package/CHANGELOG.md +0 -127
  19. package/CLAUDE.md +0 -137
  20. package/CODE_OF_CONDUCT.md +0 -9
  21. package/CONTRIBUTING.md +0 -390
  22. package/PRODUCT_VISION.md +0 -277
  23. package/SECURITY.md +0 -51
  24. package/SIGNING_INSTRUCTIONS.md +0 -284
  25. package/assets/DMG_BACKGROUND_INSTRUCTIONS.md +0 -130
  26. package/assets/svg-source/dmg-background.svg +0 -70
  27. package/assets/svg-source/icon.svg +0 -20
  28. package/assets/svg-source/tray-icon-processing.svg +0 -7
  29. package/assets/svg-source/tray-icon-recording.svg +0 -7
  30. package/assets/svg-source/tray-icon.svg +0 -6
  31. package/assets/tray-complete.png +0 -0
  32. package/assets/tray-complete@2x.png +0 -0
  33. package/assets/tray-completeTemplate.png +0 -0
  34. package/assets/tray-completeTemplate@2x.png +0 -0
  35. package/assets/tray-error.png +0 -0
  36. package/assets/tray-error@2x.png +0 -0
  37. package/assets/tray-errorTemplate.png +0 -0
  38. package/assets/tray-errorTemplate@2x.png +0 -0
  39. package/assets/tray-icon-processing.png +0 -0
  40. package/assets/tray-icon-processing@2x.png +0 -0
  41. package/assets/tray-icon-processingTemplate.png +0 -0
  42. package/assets/tray-icon-processingTemplate@2x.png +0 -0
  43. package/assets/tray-icon-recording.png +0 -0
  44. package/assets/tray-icon-recording@2x.png +0 -0
  45. package/assets/tray-icon-recordingTemplate.png +0 -0
  46. package/assets/tray-icon-recordingTemplate@2x.png +0 -0
  47. package/assets/tray-icon.png +0 -0
  48. package/assets/tray-icon@2x.png +0 -0
  49. package/assets/tray-iconTemplate.png +0 -0
  50. package/assets/tray-iconTemplate@2x.png +0 -0
  51. package/assets/tray-idle.png +0 -0
  52. package/assets/tray-idle@2x.png +0 -0
  53. package/assets/tray-idleTemplate.png +0 -0
  54. package/assets/tray-idleTemplate@2x.png +0 -0
  55. package/assets/tray-processing-0.png +0 -0
  56. package/assets/tray-processing-0@2x.png +0 -0
  57. package/assets/tray-processing-0Template.png +0 -0
  58. package/assets/tray-processing-0Template@2x.png +0 -0
  59. package/assets/tray-processing-1.png +0 -0
  60. package/assets/tray-processing-1@2x.png +0 -0
  61. package/assets/tray-processing-1Template.png +0 -0
  62. package/assets/tray-processing-1Template@2x.png +0 -0
  63. package/assets/tray-processing-2.png +0 -0
  64. package/assets/tray-processing-2@2x.png +0 -0
  65. package/assets/tray-processing-2Template.png +0 -0
  66. package/assets/tray-processing-2Template@2x.png +0 -0
  67. package/assets/tray-processing-3.png +0 -0
  68. package/assets/tray-processing-3@2x.png +0 -0
  69. package/assets/tray-processing-3Template.png +0 -0
  70. package/assets/tray-processing-3Template@2x.png +0 -0
  71. package/assets/tray-processing.png +0 -0
  72. package/assets/tray-processing@2x.png +0 -0
  73. package/assets/tray-processingTemplate.png +0 -0
  74. package/assets/tray-processingTemplate@2x.png +0 -0
  75. package/assets/tray-recording.png +0 -0
  76. package/assets/tray-recording@2x.png +0 -0
  77. package/assets/tray-recordingTemplate.png +0 -0
  78. package/assets/tray-recordingTemplate@2x.png +0 -0
  79. package/build/DMG_BACKGROUND_SPEC.md +0 -50
  80. package/build/dmg-background.png +0 -0
  81. package/build/dmg-background@2x.png +0 -0
  82. package/build/entitlements.mac.inherit.plist +0 -27
  83. package/build/entitlements.mac.plist +0 -41
  84. package/build/favicon-16.png +0 -0
  85. package/build/favicon-180.png +0 -0
  86. package/build/favicon-192.png +0 -0
  87. package/build/favicon-32.png +0 -0
  88. package/build/favicon-48.png +0 -0
  89. package/build/favicon-512.png +0 -0
  90. package/build/favicon-64.png +0 -0
  91. package/build/icon-128.png +0 -0
  92. package/build/icon-16.png +0 -0
  93. package/build/icon-24.png +0 -0
  94. package/build/icon-256.png +0 -0
  95. package/build/icon-32.png +0 -0
  96. package/build/icon-48.png +0 -0
  97. package/build/icon-64.png +0 -0
  98. package/build/icon.icns +0 -0
  99. package/build/icon.ico +0 -0
  100. package/build/icon.iconset/icon_128x128.png +0 -0
  101. package/build/icon.iconset/icon_128x128@2x.png +0 -0
  102. package/build/icon.iconset/icon_16x16.png +0 -0
  103. package/build/icon.iconset/icon_16x16@2x.png +0 -0
  104. package/build/icon.iconset/icon_256x256.png +0 -0
  105. package/build/icon.iconset/icon_256x256@2x.png +0 -0
  106. package/build/icon.iconset/icon_32x32.png +0 -0
  107. package/build/icon.iconset/icon_32x32@2x.png +0 -0
  108. package/build/icon.iconset/icon_512x512.png +0 -0
  109. package/build/icon.iconset/icon_512x512@2x.png +0 -0
  110. package/build/icon.png +0 -0
  111. package/build/installer-header.bmp +0 -0
  112. package/build/installer-header.png +0 -0
  113. package/build/installer-sidebar.bmp +0 -0
  114. package/build/installer-sidebar.png +0 -0
  115. package/build/installer.nsh +0 -45
  116. package/build/overlay-processing.png +0 -0
  117. package/build/overlay-recording.png +0 -0
  118. package/build/toolbar-record.png +0 -0
  119. package/build/toolbar-screenshot.png +0 -0
  120. package/build/toolbar-settings.png +0 -0
  121. package/build/toolbar-stop.png +0 -0
  122. package/dist/preload/index.mjs +0 -907
  123. package/dist/renderer/assets/index-CCmUjl9K.js +0 -19495
  124. package/dist/renderer/assets/index-CUqz_Gs6.css +0 -2270
  125. package/dist/renderer/index.html +0 -27
  126. package/docs/AI_AGENT_QUICKSTART.md +0 -42
  127. package/docs/AI_PIPELINE_DESIGN.md +0 -595
  128. package/docs/API.md +0 -514
  129. package/docs/ARCHITECTURE.md +0 -460
  130. package/docs/CONFIGURATION.md +0 -336
  131. package/docs/DEVELOPMENT.md +0 -508
  132. package/docs/EXPORT_FORMATS.md +0 -451
  133. package/docs/GETTING_STARTED.md +0 -236
  134. package/docs/KEYBOARD_SHORTCUTS.md +0 -334
  135. package/docs/TROUBLESHOOTING.md +0 -418
  136. package/docs/landing/index.html +0 -672
  137. package/docs/landing/script.js +0 -342
  138. package/docs/landing/styles.css +0 -1543
  139. package/electron-builder.yml +0 -140
  140. package/electron.vite.config.ts +0 -63
  141. package/railway.json +0 -12
  142. package/scripts/build.mjs +0 -51
  143. package/scripts/generate-icons.mjs +0 -314
  144. package/scripts/generate-installer-images.cjs +0 -253
  145. package/scripts/generate-tray-icons.mjs +0 -258
  146. package/scripts/notarize.cjs +0 -180
  147. package/scripts/one-click-clean-test.sh +0 -147
  148. package/scripts/postinstall.mjs +0 -36
  149. package/scripts/setup-markupr.sh +0 -55
  150. package/setup +0 -17
  151. package/site/index.html +0 -1835
  152. package/site/package.json +0 -11
  153. package/site/railway.json +0 -12
  154. package/site/server.js +0 -31
  155. package/src/main/AutoUpdater.ts +0 -392
  156. package/src/main/CrashRecovery.ts +0 -655
  157. package/src/main/ErrorHandler.ts +0 -703
  158. package/src/main/HotkeyManager.ts +0 -399
  159. package/src/main/MenuManager.ts +0 -529
  160. package/src/main/PermissionManager.ts +0 -420
  161. package/src/main/SessionController.ts +0 -1465
  162. package/src/main/TrayManager.ts +0 -540
  163. package/src/main/ai/AIPipelineManager.ts +0 -199
  164. package/src/main/ai/ClaudeAnalyzer.ts +0 -339
  165. package/src/main/ai/ImageOptimizer.ts +0 -176
  166. package/src/main/ai/StructuredMarkdownBuilder.ts +0 -379
  167. package/src/main/ai/index.ts +0 -16
  168. package/src/main/ai/types.ts +0 -258
  169. package/src/main/analysis/ClarificationGenerator.ts +0 -385
  170. package/src/main/analysis/FeedbackAnalyzer.ts +0 -531
  171. package/src/main/analysis/index.ts +0 -19
  172. package/src/main/audio/AudioCapture.ts +0 -978
  173. package/src/main/audio/audioUtils.ts +0 -100
  174. package/src/main/audio/index.ts +0 -20
  175. package/src/main/capture/index.ts +0 -1
  176. package/src/main/index.ts +0 -1693
  177. package/src/main/ipc/captureHandlers.ts +0 -272
  178. package/src/main/ipc/index.ts +0 -45
  179. package/src/main/ipc/outputHandlers.ts +0 -302
  180. package/src/main/ipc/sessionHandlers.ts +0 -56
  181. package/src/main/ipc/settingsHandlers.ts +0 -471
  182. package/src/main/ipc/types.ts +0 -56
  183. package/src/main/ipc/windowHandlers.ts +0 -277
  184. package/src/main/output/ClipboardService.ts +0 -369
  185. package/src/main/output/ExportService.ts +0 -539
  186. package/src/main/output/FileManager.ts +0 -416
  187. package/src/main/output/MarkdownGenerator.ts +0 -791
  188. package/src/main/output/MarkdownPatcher.ts +0 -299
  189. package/src/main/output/index.ts +0 -186
  190. package/src/main/output/sessionAdapter.ts +0 -207
  191. package/src/main/output/templates/html-template.ts +0 -553
  192. package/src/main/pipeline/FrameExtractor.ts +0 -330
  193. package/src/main/pipeline/PostProcessor.ts +0 -399
  194. package/src/main/pipeline/TranscriptAnalyzer.ts +0 -226
  195. package/src/main/pipeline/index.ts +0 -36
  196. package/src/main/platform/WindowsTaskbar.ts +0 -600
  197. package/src/main/platform/index.ts +0 -16
  198. package/src/main/settings/SettingsManager.ts +0 -730
  199. package/src/main/settings/index.ts +0 -19
  200. package/src/main/transcription/ModelDownloadManager.ts +0 -494
  201. package/src/main/transcription/TierManager.ts +0 -219
  202. package/src/main/transcription/TranscriptionRecoveryService.ts +0 -340
  203. package/src/main/transcription/WhisperService.ts +0 -748
  204. package/src/main/transcription/index.ts +0 -56
  205. package/src/main/transcription/types.ts +0 -135
  206. package/src/main/windows/PopoverManager.ts +0 -284
  207. package/src/main/windows/TaskbarIntegration.ts +0 -452
  208. package/src/main/windows/index.ts +0 -23
  209. package/src/preload/index.ts +0 -1047
  210. package/src/renderer/App.tsx +0 -515
  211. package/src/renderer/AppWrapper.tsx +0 -28
  212. package/src/renderer/assets/logo-dark.svg +0 -7
  213. package/src/renderer/assets/logo.svg +0 -7
  214. package/src/renderer/audio/AudioCaptureRenderer.ts +0 -454
  215. package/src/renderer/capture/ScreenRecordingRenderer.ts +0 -492
  216. package/src/renderer/components/AnnotationOverlay.tsx +0 -836
  217. package/src/renderer/components/AudioWaveform.tsx +0 -811
  218. package/src/renderer/components/ClarificationQuestions.tsx +0 -656
  219. package/src/renderer/components/CountdownTimer.tsx +0 -495
  220. package/src/renderer/components/CrashRecoveryDialog.tsx +0 -632
  221. package/src/renderer/components/DonateButton.tsx +0 -127
  222. package/src/renderer/components/ErrorBoundary.tsx +0 -308
  223. package/src/renderer/components/ExportDialog.tsx +0 -872
  224. package/src/renderer/components/HotkeyHint.tsx +0 -261
  225. package/src/renderer/components/KeyboardShortcuts.tsx +0 -787
  226. package/src/renderer/components/ModelDownloadDialog.tsx +0 -844
  227. package/src/renderer/components/Onboarding.tsx +0 -1830
  228. package/src/renderer/components/ProcessingOverlay.tsx +0 -157
  229. package/src/renderer/components/RecordingOverlay.tsx +0 -423
  230. package/src/renderer/components/SessionHistory.tsx +0 -1746
  231. package/src/renderer/components/SessionReview.tsx +0 -1321
  232. package/src/renderer/components/SettingsPanel.tsx +0 -217
  233. package/src/renderer/components/Skeleton.tsx +0 -347
  234. package/src/renderer/components/StatusIndicator.tsx +0 -86
  235. package/src/renderer/components/ThemeProvider.tsx +0 -429
  236. package/src/renderer/components/Tooltip.tsx +0 -370
  237. package/src/renderer/components/TranscriptionPreview.tsx +0 -183
  238. package/src/renderer/components/TranscriptionTierSelector.tsx +0 -640
  239. package/src/renderer/components/UpdateNotification.tsx +0 -377
  240. package/src/renderer/components/WindowSelector.tsx +0 -947
  241. package/src/renderer/components/index.ts +0 -99
  242. package/src/renderer/components/primitives/ApiKeyInput.tsx +0 -98
  243. package/src/renderer/components/primitives/ColorPicker.tsx +0 -65
  244. package/src/renderer/components/primitives/DangerButton.tsx +0 -45
  245. package/src/renderer/components/primitives/DirectoryPicker.tsx +0 -41
  246. package/src/renderer/components/primitives/Dropdown.tsx +0 -34
  247. package/src/renderer/components/primitives/KeyRecorder.tsx +0 -117
  248. package/src/renderer/components/primitives/SettingsSection.tsx +0 -32
  249. package/src/renderer/components/primitives/Slider.tsx +0 -43
  250. package/src/renderer/components/primitives/Toggle.tsx +0 -36
  251. package/src/renderer/components/primitives/index.ts +0 -10
  252. package/src/renderer/components/settings/AdvancedTab.tsx +0 -174
  253. package/src/renderer/components/settings/AppearanceTab.tsx +0 -77
  254. package/src/renderer/components/settings/GeneralTab.tsx +0 -40
  255. package/src/renderer/components/settings/HotkeysTab.tsx +0 -79
  256. package/src/renderer/components/settings/RecordingTab.tsx +0 -84
  257. package/src/renderer/components/settings/index.ts +0 -9
  258. package/src/renderer/components/settings/settingsStyles.ts +0 -673
  259. package/src/renderer/components/settings/tabConfig.tsx +0 -85
  260. package/src/renderer/components/settings/useSettingsPanel.ts +0 -447
  261. package/src/renderer/contexts/ProcessingContext.tsx +0 -227
  262. package/src/renderer/contexts/RecordingContext.tsx +0 -683
  263. package/src/renderer/contexts/UIContext.tsx +0 -326
  264. package/src/renderer/contexts/index.ts +0 -24
  265. package/src/renderer/donateMessages.ts +0 -69
  266. package/src/renderer/hooks/index.ts +0 -75
  267. package/src/renderer/hooks/useAnimation.tsx +0 -544
  268. package/src/renderer/hooks/useTheme.ts +0 -313
  269. package/src/renderer/index.html +0 -26
  270. package/src/renderer/main.tsx +0 -52
  271. package/src/renderer/styles/animations.css +0 -1093
  272. package/src/renderer/styles/app-shell.css +0 -662
  273. package/src/renderer/styles/globals.css +0 -515
  274. package/src/renderer/styles/theme.ts +0 -578
  275. package/src/renderer/types/electron.d.ts +0 -385
  276. package/src/shared/hotkeys.ts +0 -283
  277. package/src/shared/types.ts +0 -809
  278. package/tests/clipboard.test.ts +0 -228
  279. package/tests/e2e/criticalPaths.test.ts +0 -594
  280. package/tests/feedbackAnalyzer.test.ts +0 -303
  281. package/tests/integration/sessionFlow.test.ts +0 -583
  282. package/tests/markdownGenerator.test.ts +0 -418
  283. package/tests/output.test.ts +0 -96
  284. package/tests/setup.ts +0 -486
  285. package/tests/unit/appIntegration.test.ts +0 -676
  286. package/tests/unit/appViewState.test.ts +0 -281
  287. package/tests/unit/audioIpcChannels.test.ts +0 -17
  288. package/tests/unit/exportService.test.ts +0 -492
  289. package/tests/unit/hotkeys.test.ts +0 -92
  290. package/tests/unit/navigationPreload.test.ts +0 -94
  291. package/tests/unit/onboardingFlow.test.ts +0 -345
  292. package/tests/unit/permissionManager.test.ts +0 -175
  293. package/tests/unit/permissionManagerExpanded.test.ts +0 -296
  294. package/tests/unit/screenRecordingRenderer.test.ts +0 -368
  295. package/tests/unit/sessionController.test.ts +0 -515
  296. package/tests/unit/tierManager.test.ts +0 -61
  297. package/tests/unit/tierManagerExpanded.test.ts +0 -142
  298. package/tests/unit/transcriptAnalyzer.test.ts +0 -64
  299. package/tsconfig.json +0 -25
  300. package/vitest.config.ts +0 -46
package/site/index.html DELETED
@@ -1,1835 +0,0 @@
1
- <!--
2
- markupR landing page.
3
- Neo-brutalist structure, minimalist soul.
4
- Thick borders because elements should look intentional.
5
- Hard shadows because software should feel tactile.
6
- System fonts because they're already on your machine.
7
- Warm amber because cold blue is what everyone else picked.
8
- No frameworks, no tracking, no cookies, no external requests.
9
- The product is the thing that needs to be impressive — not the marketing page.
10
- But if the marketing page is also impressive, that's fine too.
11
- View the source: github.com/eddiesanjuan/markupr
12
- -->
13
- <!DOCTYPE html>
14
- <html lang="en">
15
- <head>
16
- <meta charset="UTF-8">
17
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
- <title>markupR — App feedback, made effortless</title>
19
- <meta name="description" content="markupR records your screen while you talk, then assembles structured Markdown with the right screenshots at the right moments. Launch includes both BYOK open source mode and touchless premium mode with hosted keys.">
20
- <meta name="theme-color" content="#0A0A0B" media="(prefers-color-scheme: dark)">
21
- <meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)">
22
- <meta name="theme-color" content="#0A0A0B">
23
- <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>M</text></svg>">
24
- <style>
25
- /* ========================================
26
- RESET
27
- ======================================== */
28
- *, *::before, *::after {
29
- margin: 0;
30
- padding: 0;
31
- box-sizing: border-box;
32
- }
33
-
34
- /* ========================================
35
- CUSTOM PROPERTIES — DARK MODE (default)
36
- ======================================== */
37
- :root {
38
- color-scheme: dark light;
39
-
40
- /* Typography */
41
- --font-sans: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Arial, sans-serif;
42
- --font-mono: 'SF Mono', 'Fira Code', 'Cascadia Code', 'JetBrains Mono', ui-monospace, monospace;
43
-
44
- /* Colors — Dark (default) */
45
- --bg-primary: #0A0A0B;
46
- --bg-secondary: #141415;
47
- --bg-tertiary: #1E1E20;
48
- --text-primary: #F5F5F5;
49
- --text-secondary: #A0A0A0;
50
- --text-tertiary: #666666;
51
- --border: rgba(255, 255, 255, 0.08);
52
- --border-strong: rgba(255, 255, 255, 0.16);
53
-
54
- /* Accent — Warm Amber */
55
- --accent: #F59E0B;
56
- --accent-hover: #D97706;
57
-
58
- /* Neo-brutalist shadows */
59
- --shadow-sm: 2px 2px 0 rgba(255, 255, 255, 0.12);
60
- --shadow-md: 4px 4px 0 rgba(255, 255, 255, 0.12);
61
- --shadow-lg: 6px 6px 0 rgba(255, 255, 255, 0.12);
62
-
63
- /* Syntax highlighting */
64
- --syn-keyword: #C4B5FD;
65
- --syn-string: #86EFAC;
66
- --syn-function: #93C5FD;
67
- --syn-comment: #525252;
68
- --syn-operator: #F9A8D4;
69
- --syn-default: #E5E5E5;
70
-
71
- /* Spacing */
72
- --section-gap: 160px;
73
- --content-max: 720px;
74
- --feature-max: 960px;
75
- --side-pad: clamp(1.25rem, 5vw, 2.5rem);
76
- }
77
-
78
- /* ========================================
79
- LIGHT MODE (system preference override)
80
- ======================================== */
81
- @media (prefers-color-scheme: light) {
82
- :root {
83
- --bg-primary: #FFFFFF;
84
- --bg-secondary: #F8F8F8;
85
- --bg-tertiary: #F0F0F0;
86
- --text-primary: #0A0A0A;
87
- --text-secondary: #525252;
88
- --text-tertiary: #8B8B8B;
89
- --border: rgba(0, 0, 0, 0.08);
90
- --border-strong: rgba(0, 0, 0, 0.12);
91
-
92
- --accent: #D97706;
93
- --accent-hover: #B45309;
94
-
95
- --shadow-sm: 2px 2px 0 var(--text-primary);
96
- --shadow-md: 4px 4px 0 var(--text-primary);
97
- --shadow-lg: 6px 6px 0 var(--text-primary);
98
- }
99
- }
100
-
101
- /* ========================================
102
- BASE
103
- ======================================== */
104
- html {
105
- scroll-behavior: smooth;
106
- -webkit-text-size-adjust: 100%;
107
- }
108
-
109
- @media (prefers-reduced-motion: reduce) {
110
- html {
111
- scroll-behavior: auto;
112
- }
113
- }
114
-
115
- body {
116
- font-family: var(--font-sans);
117
- font-size: 17px;
118
- line-height: 1.65;
119
- color: var(--text-secondary);
120
- background-color: var(--bg-primary);
121
- -webkit-font-smoothing: antialiased;
122
- -moz-osx-font-smoothing: grayscale;
123
- overflow-x: hidden;
124
- }
125
-
126
- ::selection {
127
- background-color: var(--accent);
128
- color: #FFFFFF;
129
- }
130
-
131
- a {
132
- color: var(--accent);
133
- text-decoration: none;
134
- transition: color 150ms ease;
135
- }
136
-
137
- a:hover {
138
- color: var(--accent-hover);
139
- }
140
-
141
- img {
142
- max-width: 100%;
143
- display: block;
144
- }
145
-
146
- /* ========================================
147
- TYPOGRAPHY
148
- ======================================== */
149
- h1, h2, h3, h4 {
150
- color: var(--text-primary);
151
- font-weight: 800;
152
- text-wrap: balance;
153
- }
154
-
155
- .display {
156
- font-size: clamp(36px, 7vw, 56px);
157
- line-height: 1.05;
158
- letter-spacing: -0.04em;
159
- font-weight: 900;
160
- }
161
-
162
- h2 {
163
- font-size: clamp(28px, 4vw, 44px);
164
- line-height: 1.1;
165
- letter-spacing: -0.03em;
166
- }
167
-
168
- h3 {
169
- font-size: clamp(20px, 3vw, 24px);
170
- line-height: 1.3;
171
- letter-spacing: -0.01em;
172
- }
173
-
174
- .overline {
175
- font-size: 12px;
176
- line-height: 1.4;
177
- font-weight: 600;
178
- letter-spacing: 0.08em;
179
- text-transform: uppercase;
180
- color: var(--accent);
181
- }
182
-
183
- .body-lg {
184
- font-size: clamp(17px, 2.5vw, 20px);
185
- line-height: 1.6;
186
- }
187
-
188
- .body-sm {
189
- font-size: 15px;
190
- line-height: 1.6;
191
- }
192
-
193
- .caption {
194
- font-size: 13px;
195
- line-height: 1.5;
196
- font-weight: 500;
197
- letter-spacing: 0.02em;
198
- color: var(--text-tertiary);
199
- }
200
-
201
- /* ========================================
202
- LAYOUT
203
- ======================================== */
204
- .container {
205
- max-width: var(--content-max);
206
- margin: 0 auto;
207
- padding-left: var(--side-pad);
208
- padding-right: var(--side-pad);
209
- }
210
-
211
- .container--wide {
212
- max-width: var(--feature-max);
213
- margin: 0 auto;
214
- padding-left: var(--side-pad);
215
- padding-right: var(--side-pad);
216
- }
217
-
218
- section {
219
- padding-top: var(--section-gap);
220
- padding-bottom: var(--section-gap);
221
- }
222
-
223
- @media (max-width: 768px) {
224
- :root {
225
- --section-gap: 96px;
226
- }
227
- }
228
-
229
- /* Section dividers — visible bones */
230
- .divider {
231
- border: none;
232
- height: 3px;
233
- background: var(--text-primary);
234
- margin: 0;
235
- opacity: 0.12;
236
- }
237
-
238
- @media (prefers-color-scheme: light) {
239
- .divider {
240
- opacity: 0.08;
241
- }
242
- }
243
-
244
- /* ========================================
245
- WORDMARK
246
- ======================================== */
247
- .wordmark {
248
- font-family: var(--font-sans);
249
- font-weight: 900;
250
- font-size: 28px;
251
- letter-spacing: -0.04em;
252
- color: var(--text-primary);
253
- display: inline-block;
254
- }
255
-
256
- .wordmark-r {
257
- color: var(--accent);
258
- }
259
-
260
- /* ========================================
261
- BUTTONS — Neo-Brutalist, Tactile
262
- ======================================== */
263
- .btn {
264
- display: inline-flex;
265
- align-items: center;
266
- gap: 8px;
267
- padding: 14px 28px;
268
- font-family: var(--font-sans);
269
- font-size: 15px;
270
- font-weight: 600;
271
- line-height: 1;
272
- border: 3px solid var(--text-primary);
273
- border-radius: 4px;
274
- cursor: pointer;
275
- transition: transform 150ms ease-out, box-shadow 150ms ease-out, background-color 150ms ease;
276
- text-decoration: none;
277
- white-space: nowrap;
278
- }
279
-
280
- .btn--primary {
281
- background: var(--text-primary);
282
- color: var(--bg-primary);
283
- box-shadow: var(--shadow-md);
284
- }
285
-
286
- .btn--primary:hover {
287
- transform: translate(2px, 2px);
288
- box-shadow: var(--shadow-sm);
289
- color: var(--bg-primary);
290
- }
291
-
292
- .btn--primary:active {
293
- transform: translate(4px, 4px);
294
- box-shadow: none;
295
- }
296
-
297
- .btn--secondary {
298
- background: var(--bg-primary);
299
- color: var(--text-primary);
300
- box-shadow: var(--shadow-md);
301
- }
302
-
303
- .btn--secondary:hover {
304
- transform: translate(2px, 2px);
305
- box-shadow: var(--shadow-sm);
306
- color: var(--text-primary);
307
- }
308
-
309
- .btn--secondary:active {
310
- transform: translate(4px, 4px);
311
- box-shadow: none;
312
- }
313
-
314
- .btn--accent {
315
- background: var(--accent);
316
- color: #FFFFFF;
317
- border-color: var(--text-primary);
318
- box-shadow: var(--shadow-md);
319
- }
320
-
321
- .btn--accent:hover {
322
- transform: translate(2px, 2px);
323
- box-shadow: var(--shadow-sm);
324
- background: var(--accent-hover);
325
- color: #FFFFFF;
326
- }
327
-
328
- .btn--accent:active {
329
- transform: translate(4px, 4px);
330
- box-shadow: none;
331
- }
332
-
333
- .btn:focus-visible {
334
- outline: 3px solid var(--accent);
335
- outline-offset: 3px;
336
- }
337
-
338
- /* ========================================
339
- KBD — Keyboard Shortcut Badges
340
- ======================================== */
341
- kbd {
342
- display: inline-flex;
343
- align-items: center;
344
- justify-content: center;
345
- min-width: 32px;
346
- padding: 4px 10px;
347
- font-family: var(--font-mono);
348
- font-size: 14px;
349
- font-weight: 500;
350
- color: var(--text-tertiary);
351
- background: var(--bg-tertiary);
352
- border: 2px solid var(--text-primary);
353
- border-radius: 4px;
354
- box-shadow: var(--shadow-sm);
355
- line-height: 1.4;
356
- }
357
-
358
- .kbd-group {
359
- display: inline-flex;
360
- align-items: center;
361
- gap: 6px;
362
- }
363
-
364
- .kbd-plus {
365
- color: var(--text-tertiary);
366
- font-size: 13px;
367
- font-weight: 500;
368
- }
369
-
370
- /* ========================================
371
- CARDS — Neo-Brutalist
372
- ======================================== */
373
- .card {
374
- background: var(--bg-secondary);
375
- border: 3px solid var(--text-primary);
376
- border-radius: 4px;
377
- padding: 32px;
378
- box-shadow: var(--shadow-lg);
379
- transition: transform 150ms ease-out, box-shadow 150ms ease-out;
380
- }
381
-
382
- .card:hover {
383
- transform: translate(2px, 2px);
384
- box-shadow: var(--shadow-md);
385
- }
386
-
387
- /* ========================================
388
- SCROLL REVEAL
389
- ======================================== */
390
- .reveal {
391
- opacity: 0;
392
- transform: translateY(24px);
393
- transition: opacity 500ms cubic-bezier(0.16, 1, 0.3, 1),
394
- transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
395
- }
396
-
397
- .reveal.visible {
398
- opacity: 1;
399
- transform: translateY(0);
400
- }
401
-
402
- .reveal-delay-1 { transition-delay: 75ms; }
403
- .reveal-delay-2 { transition-delay: 150ms; }
404
- .reveal-delay-3 { transition-delay: 225ms; }
405
- .reveal-delay-4 { transition-delay: 300ms; }
406
-
407
- @media (prefers-reduced-motion: reduce) {
408
- .reveal {
409
- opacity: 1;
410
- transform: none;
411
- transition: none;
412
- }
413
- }
414
-
415
- /* ========================================
416
- HERO
417
- ======================================== */
418
- .hero {
419
- min-height: 100dvh;
420
- display: flex;
421
- flex-direction: column;
422
- align-items: center;
423
- justify-content: center;
424
- text-align: center;
425
- padding: 96px var(--side-pad);
426
- }
427
-
428
- .hero__wordmark {
429
- font-size: clamp(32px, 5vw, 40px);
430
- margin-bottom: 32px;
431
- }
432
-
433
- .hero__headline {
434
- max-width: 700px;
435
- margin-bottom: 24px;
436
- }
437
-
438
- .hero__subtext {
439
- max-width: 560px;
440
- margin-bottom: 48px;
441
- color: var(--text-secondary);
442
- }
443
-
444
- .hero__ctas {
445
- display: flex;
446
- flex-wrap: wrap;
447
- gap: 16px;
448
- justify-content: center;
449
- margin-bottom: 32px;
450
- }
451
-
452
- .hero__support-note {
453
- margin: 0 0 28px;
454
- max-width: 560px;
455
- }
456
-
457
- .hero__shortcut {
458
- display: flex;
459
- align-items: center;
460
- gap: 12px;
461
- color: var(--text-tertiary);
462
- }
463
-
464
- .hero__shortcut-label {
465
- font-size: 13px;
466
- font-weight: 500;
467
- }
468
-
469
- @media (max-width: 480px) {
470
- .hero__ctas {
471
- flex-direction: column;
472
- align-items: center;
473
- }
474
-
475
- .hero__ctas .btn {
476
- width: 100%;
477
- justify-content: center;
478
- }
479
- }
480
-
481
- /* ========================================
482
- CORE LOOP
483
- ======================================== */
484
- .loop {
485
- background: var(--bg-secondary);
486
- }
487
-
488
- .loop__grid {
489
- display: grid;
490
- grid-template-columns: repeat(3, 1fr);
491
- gap: 24px;
492
- max-width: var(--feature-max);
493
- margin: 0 auto;
494
- }
495
-
496
- .loop__step {
497
- text-align: center;
498
- position: relative;
499
- }
500
-
501
- .loop__icon-box {
502
- width: 72px;
503
- height: 72px;
504
- margin: 0 auto 16px;
505
- display: flex;
506
- align-items: center;
507
- justify-content: center;
508
- border: 3px solid var(--text-primary);
509
- border-radius: 4px;
510
- box-shadow: var(--shadow-md);
511
- background: var(--bg-primary);
512
- color: var(--text-primary);
513
- }
514
-
515
- .loop__icon-box svg {
516
- width: 28px;
517
- height: 28px;
518
- }
519
-
520
- .loop__label {
521
- font-family: var(--font-mono);
522
- font-size: 15px;
523
- font-weight: 600;
524
- color: var(--text-primary);
525
- margin-bottom: 8px;
526
- }
527
-
528
- .loop__desc {
529
- font-size: 14px;
530
- line-height: 1.5;
531
- color: var(--text-tertiary);
532
- }
533
-
534
- /* "you" vs "markupR" role labels */
535
- .loop__role {
536
- font-family: var(--font-mono);
537
- font-size: 11px;
538
- font-weight: 700;
539
- letter-spacing: 0.06em;
540
- text-transform: uppercase;
541
- margin-bottom: 12px;
542
- height: 20px;
543
- }
544
-
545
- .loop__role--you {
546
- color: var(--accent);
547
- }
548
-
549
- .loop__role--auto {
550
- color: var(--text-tertiary);
551
- }
552
-
553
- /* Dim automatic steps */
554
- .loop__step--auto .loop__icon-box {
555
- opacity: 0.45;
556
- border-style: dashed;
557
- box-shadow: none;
558
- }
559
-
560
- .loop__step--auto .loop__label {
561
- color: var(--text-secondary);
562
- }
563
-
564
- /* Arrow between steps */
565
- .loop__step:not(:last-child)::after {
566
- content: '';
567
- position: absolute;
568
- top: 56px; /* shifted down to account for role label */
569
- right: -14px;
570
- width: 8px;
571
- height: 8px;
572
- border-right: 3px solid var(--text-primary);
573
- border-bottom: 3px solid var(--text-primary);
574
- transform: rotate(-45deg);
575
- opacity: 0.3;
576
- }
577
-
578
- /* The actual workflow — keystrokes */
579
- .keystroke-flow {
580
- max-width: var(--feature-max);
581
- margin: 56px auto 0;
582
- display: flex;
583
- align-items: center;
584
- justify-content: center;
585
- gap: 0;
586
- flex-wrap: wrap;
587
- }
588
-
589
- .keystroke-flow__step {
590
- display: flex;
591
- flex-direction: column;
592
- align-items: center;
593
- gap: 10px;
594
- padding: 0 clamp(12px, 3vw, 32px);
595
- }
596
-
597
- .keystroke-flow__keys {
598
- font-family: var(--font-mono);
599
- font-size: clamp(18px, 3.5vw, 28px);
600
- font-weight: 700;
601
- color: var(--text-primary);
602
- letter-spacing: -0.02em;
603
- }
604
-
605
- .keystroke-flow__label {
606
- font-size: 13px;
607
- font-weight: 500;
608
- color: var(--text-tertiary);
609
- }
610
-
611
- .keystroke-flow__arrow {
612
- font-size: clamp(18px, 3vw, 24px);
613
- color: var(--text-tertiary);
614
- opacity: 0.4;
615
- padding: 0 4px;
616
- margin-top: -20px; /* align with keys, not labels */
617
- }
618
-
619
- .keystroke-flow__ellipsis {
620
- font-family: var(--font-mono);
621
- font-size: clamp(18px, 3.5vw, 28px);
622
- font-weight: 700;
623
- color: var(--text-tertiary);
624
- letter-spacing: 0.15em;
625
- padding: 0 clamp(4px, 1.5vw, 12px);
626
- margin-top: -20px;
627
- }
628
-
629
- @media (max-width: 640px) {
630
- .loop__grid {
631
- grid-template-columns: 1fr 1fr;
632
- gap: 32px;
633
- }
634
-
635
- .loop__step:not(:last-child)::after {
636
- display: none;
637
- }
638
- }
639
-
640
- @media (max-width: 400px) {
641
- .loop__grid {
642
- grid-template-columns: 1fr;
643
- gap: 40px;
644
- }
645
-
646
- .loop__role {
647
- height: auto;
648
- }
649
- }
650
-
651
- @media (max-width: 480px) {
652
- .keystroke-flow {
653
- gap: 8px 0;
654
- }
655
- }
656
-
657
- /* ========================================
658
- QUICK VISUAL WALKTHROUGH
659
- ======================================== */
660
- .quickwalk {
661
- background: var(--bg-primary);
662
- }
663
-
664
- .quickwalk__panel {
665
- border: 3px solid var(--text-primary);
666
- border-radius: 4px;
667
- background: var(--bg-secondary);
668
- box-shadow: var(--shadow-lg);
669
- padding: clamp(20px, 4vw, 32px);
670
- }
671
-
672
- .quickwalk__layout {
673
- display: grid;
674
- grid-template-columns: 1.3fr 1fr;
675
- gap: clamp(20px, 3.5vw, 36px);
676
- align-items: stretch;
677
- max-width: var(--feature-max);
678
- margin: 0 auto;
679
- }
680
-
681
- .quickwalk__sim {
682
- display: flex;
683
- flex-direction: column;
684
- gap: 18px;
685
- }
686
-
687
- .quickwalk__status {
688
- display: flex;
689
- align-items: center;
690
- gap: 10px;
691
- font-family: var(--font-mono);
692
- font-size: 13px;
693
- color: var(--text-secondary);
694
- border-bottom: 2px solid var(--border);
695
- padding-bottom: 12px;
696
- }
697
-
698
- .quickwalk__status-dot {
699
- width: 10px;
700
- height: 10px;
701
- border-radius: 50%;
702
- background: #ef4444;
703
- box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2);
704
- animation: quickwalk-recording-pulse 1200ms ease-in-out infinite;
705
- }
706
-
707
- .quickwalk__status-time {
708
- margin-left: auto;
709
- color: var(--text-tertiary);
710
- letter-spacing: 0.02em;
711
- }
712
-
713
- .quickwalk__pipeline {
714
- display: grid;
715
- grid-template-columns: auto minmax(60px, 1fr) auto minmax(60px, 1fr) auto;
716
- align-items: center;
717
- gap: 8px;
718
- }
719
-
720
- .quickwalk__node {
721
- border: 2px solid var(--border-strong);
722
- border-radius: 4px;
723
- padding: 10px 12px;
724
- text-align: center;
725
- font-family: var(--font-mono);
726
- font-size: 12px;
727
- font-weight: 700;
728
- letter-spacing: 0.02em;
729
- color: var(--text-secondary);
730
- background: var(--bg-primary);
731
- }
732
-
733
- .quickwalk__node--capture {
734
- animation: quickwalk-node-cycle 6s ease-in-out infinite;
735
- }
736
-
737
- .quickwalk__node--align {
738
- animation: quickwalk-node-cycle 6s ease-in-out 2s infinite;
739
- }
740
-
741
- .quickwalk__node--report {
742
- animation: quickwalk-node-cycle 6s ease-in-out 4s infinite;
743
- }
744
-
745
- .quickwalk__pipe {
746
- position: relative;
747
- height: 2px;
748
- background: var(--border-strong);
749
- overflow: hidden;
750
- }
751
-
752
- .quickwalk__pulse {
753
- position: absolute;
754
- inset: -2px auto -2px 0;
755
- width: 28px;
756
- background: linear-gradient(90deg, transparent 0%, var(--accent) 45%, transparent 100%);
757
- transform: translateX(-120%);
758
- animation: quickwalk-flow 3s linear infinite;
759
- }
760
-
761
- .quickwalk__pulse--delay {
762
- animation-delay: 1.5s;
763
- }
764
-
765
- .quickwalk__caption {
766
- font-size: 14px;
767
- color: var(--text-secondary);
768
- }
769
-
770
- .quickwalk__result {
771
- display: flex;
772
- flex-direction: column;
773
- justify-content: center;
774
- gap: 10px;
775
- }
776
-
777
- .quickwalk__report {
778
- border: 2px solid var(--border-strong);
779
- border-radius: 4px;
780
- background: var(--bg-primary);
781
- padding: 14px;
782
- min-height: 170px;
783
- display: flex;
784
- flex-direction: column;
785
- gap: 10px;
786
- }
787
-
788
- .quickwalk__line {
789
- height: 10px;
790
- border-radius: 2px;
791
- background: rgba(255, 255, 255, 0.12);
792
- }
793
-
794
- .quickwalk__line--title {
795
- width: 74%;
796
- height: 12px;
797
- background: rgba(245, 158, 11, 0.45);
798
- animation: quickwalk-line-cycle 6s ease-in-out infinite;
799
- }
800
-
801
- .quickwalk__line--full {
802
- width: 100%;
803
- }
804
-
805
- .quickwalk__line--mid {
806
- width: 86%;
807
- }
808
-
809
- .quickwalk__line--short {
810
- width: 58%;
811
- }
812
-
813
- .quickwalk__image {
814
- margin-top: 2px;
815
- width: 100%;
816
- height: 48px;
817
- border: 2px solid var(--border-strong);
818
- border-radius: 3px;
819
- background:
820
- linear-gradient(135deg, rgba(245, 158, 11, 0.18), rgba(245, 158, 11, 0.05)),
821
- repeating-linear-gradient(
822
- -45deg,
823
- rgba(255, 255, 255, 0.08) 0px,
824
- rgba(255, 255, 255, 0.08) 8px,
825
- transparent 8px,
826
- transparent 16px
827
- );
828
- animation: quickwalk-line-cycle 6s ease-in-out 4s infinite;
829
- }
830
-
831
- .quickwalk__paste {
832
- font-family: var(--font-mono);
833
- font-size: 12px;
834
- color: var(--text-tertiary);
835
- }
836
-
837
- @keyframes quickwalk-recording-pulse {
838
- 0%, 100% { opacity: 0.55; }
839
- 50% { opacity: 1; }
840
- }
841
-
842
- @keyframes quickwalk-flow {
843
- 0% { transform: translateX(-130%); opacity: 0; }
844
- 15% { opacity: 1; }
845
- 100% { transform: translateX(240%); opacity: 0; }
846
- }
847
-
848
- @keyframes quickwalk-node-cycle {
849
- 0%, 100% {
850
- border-color: var(--border-strong);
851
- color: var(--text-secondary);
852
- box-shadow: none;
853
- }
854
- 10%, 36% {
855
- border-color: var(--accent);
856
- color: var(--text-primary);
857
- box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.18);
858
- }
859
- 50% {
860
- border-color: var(--border-strong);
861
- color: var(--text-secondary);
862
- box-shadow: none;
863
- }
864
- }
865
-
866
- @keyframes quickwalk-line-cycle {
867
- 0%, 100% { opacity: 0.35; }
868
- 16%, 44% { opacity: 1; }
869
- 58% { opacity: 0.4; }
870
- }
871
-
872
- @media (max-width: 840px) {
873
- .quickwalk__layout {
874
- grid-template-columns: 1fr;
875
- }
876
- }
877
-
878
- @media (max-width: 640px) {
879
- .quickwalk__pipeline {
880
- grid-template-columns: 1fr;
881
- gap: 10px;
882
- }
883
-
884
- .quickwalk__pipe {
885
- height: 12px;
886
- width: 2px;
887
- margin: 0 auto;
888
- }
889
-
890
- .quickwalk__pulse {
891
- inset: 0;
892
- width: 2px;
893
- height: 20px;
894
- background: linear-gradient(180deg, transparent 0%, var(--accent) 45%, transparent 100%);
895
- transform: translateY(-130%);
896
- animation-name: quickwalk-flow-mobile;
897
- }
898
- }
899
-
900
- @keyframes quickwalk-flow-mobile {
901
- 0% { transform: translateY(-130%); opacity: 0; }
902
- 15% { opacity: 1; }
903
- 100% { transform: translateY(240%); opacity: 0; }
904
- }
905
-
906
- @media (prefers-reduced-motion: reduce) {
907
- .quickwalk__status-dot,
908
- .quickwalk__node,
909
- .quickwalk__pulse,
910
- .quickwalk__line--title,
911
- .quickwalk__image {
912
- animation: none !important;
913
- }
914
- }
915
-
916
- /* ========================================
917
- WHY IT MATTERS
918
- ======================================== */
919
- .why__grid {
920
- display: grid;
921
- grid-template-columns: 1fr;
922
- gap: 32px;
923
- max-width: var(--feature-max);
924
- margin: 0 auto;
925
- }
926
-
927
- @media (min-width: 768px) {
928
- .why__grid {
929
- grid-template-columns: 1fr 1fr 1fr;
930
- }
931
- }
932
-
933
- .why__card h3 {
934
- margin-bottom: 16px;
935
- font-size: clamp(18px, 2.5vw, 22px);
936
- line-height: 1.25;
937
- }
938
-
939
- .why__card p {
940
- font-size: 15px;
941
- line-height: 1.65;
942
- color: var(--text-secondary);
943
- }
944
-
945
- /* ========================================
946
- UNDER THE HOOD
947
- ======================================== */
948
- .tech {
949
- background: var(--bg-secondary);
950
- }
951
-
952
- .tech__content {
953
- display: grid;
954
- grid-template-columns: 1fr;
955
- gap: 64px;
956
- max-width: var(--feature-max);
957
- margin: 0 auto;
958
- }
959
-
960
- @media (min-width: 768px) {
961
- .tech__content {
962
- grid-template-columns: 1fr 1fr;
963
- gap: 48px;
964
- align-items: start;
965
- }
966
- }
967
-
968
- .tech__bullets {
969
- list-style: none;
970
- }
971
-
972
- .tech__bullets li {
973
- padding: 16px 0;
974
- border-bottom: 2px solid var(--border);
975
- }
976
-
977
- .tech__bullets li:first-child {
978
- padding-top: 0;
979
- }
980
-
981
- .tech__bullets strong {
982
- color: var(--text-primary);
983
- font-weight: 700;
984
- display: block;
985
- margin-bottom: 4px;
986
- font-size: 15px;
987
- }
988
-
989
- .tech__bullets span {
990
- font-size: 15px;
991
- line-height: 1.6;
992
- color: var(--text-secondary);
993
- }
994
-
995
- /* Code Block — Neo-Brutalist */
996
- .code-block {
997
- background: #0A0A0B;
998
- border: 3px solid var(--text-primary);
999
- border-radius: 4px;
1000
- box-shadow: var(--shadow-lg);
1001
- overflow: hidden;
1002
- }
1003
-
1004
- .code-block__header {
1005
- display: flex;
1006
- align-items: center;
1007
- gap: 8px;
1008
- padding: 12px 20px;
1009
- border-bottom: 2px solid rgba(255, 255, 255, 0.08);
1010
- }
1011
-
1012
- .code-block__dot {
1013
- width: 10px;
1014
- height: 10px;
1015
- border-radius: 50%;
1016
- }
1017
-
1018
- .code-block__dot--red { background: #FF5F57; }
1019
- .code-block__dot--yellow { background: #FEBC2E; }
1020
- .code-block__dot--green { background: #28C840; }
1021
-
1022
- .code-block__title {
1023
- font-family: var(--font-mono);
1024
- font-size: 12px;
1025
- color: #666;
1026
- margin-left: 8px;
1027
- }
1028
-
1029
- .code-block pre {
1030
- padding: 24px 20px;
1031
- overflow-x: auto;
1032
- font-family: var(--font-mono);
1033
- font-size: 13px;
1034
- line-height: 1.7;
1035
- color: var(--syn-default);
1036
- -webkit-overflow-scrolling: touch;
1037
- }
1038
-
1039
- .code-block pre code {
1040
- font-family: inherit;
1041
- }
1042
-
1043
- .syn-kw { color: var(--syn-keyword); }
1044
- .syn-str { color: var(--syn-string); }
1045
- .syn-fn { color: var(--syn-function); }
1046
- .syn-cm { color: var(--syn-comment); }
1047
- .syn-op { color: var(--syn-operator); }
1048
-
1049
- /* ========================================
1050
- PREMIUM TEASER
1051
- ======================================== */
1052
- .premium__features {
1053
- display: grid;
1054
- grid-template-columns: 1fr;
1055
- gap: 24px;
1056
- max-width: var(--feature-max);
1057
- margin: 48px auto 0;
1058
- }
1059
-
1060
- @media (min-width: 768px) {
1061
- .premium__features {
1062
- grid-template-columns: 1fr 1fr 1fr;
1063
- }
1064
- }
1065
-
1066
- .premium__card h3 {
1067
- margin-bottom: 12px;
1068
- font-size: 18px;
1069
- }
1070
-
1071
- .premium__card p {
1072
- font-size: 15px;
1073
- line-height: 1.6;
1074
- color: var(--text-secondary);
1075
- }
1076
-
1077
- .premium__cta {
1078
- max-width: 640px;
1079
- margin: 48px auto 0;
1080
- display: flex;
1081
- flex-wrap: wrap;
1082
- justify-content: center;
1083
- gap: 16px;
1084
- }
1085
-
1086
- .premium__note {
1087
- margin-top: 18px;
1088
- text-align: center;
1089
- }
1090
-
1091
- @media (max-width: 480px) {
1092
- .premium__cta .btn {
1093
- width: 100%;
1094
- justify-content: center;
1095
- }
1096
- }
1097
-
1098
- /* ========================================
1099
- FOOTER
1100
- ======================================== */
1101
- .footer {
1102
- padding: 48px var(--side-pad);
1103
- text-align: center;
1104
- }
1105
-
1106
- .footer__divider {
1107
- height: 3px;
1108
- background: var(--text-primary);
1109
- opacity: 0.12;
1110
- margin-bottom: 48px;
1111
- }
1112
-
1113
- @media (prefers-color-scheme: light) {
1114
- .footer__divider {
1115
- opacity: 0.08;
1116
- }
1117
- }
1118
-
1119
- .footer__links {
1120
- display: flex;
1121
- justify-content: center;
1122
- gap: 32px;
1123
- flex-wrap: wrap;
1124
- margin-bottom: 24px;
1125
- }
1126
-
1127
- .footer__link {
1128
- display: inline-flex;
1129
- align-items: center;
1130
- gap: 8px;
1131
- font-size: 15px;
1132
- font-weight: 500;
1133
- color: var(--text-secondary);
1134
- transition: color 150ms ease;
1135
- }
1136
-
1137
- .footer__link:hover {
1138
- color: var(--text-primary);
1139
- }
1140
-
1141
- .footer__link:focus-visible {
1142
- outline: 2px solid var(--accent);
1143
- outline-offset: 4px;
1144
- border-radius: 4px;
1145
- }
1146
-
1147
- .footer__link svg {
1148
- width: 18px;
1149
- height: 18px;
1150
- }
1151
-
1152
- .footer__link--button {
1153
- border: 0;
1154
- background: transparent;
1155
- padding: 0;
1156
- cursor: pointer;
1157
- font: inherit;
1158
- }
1159
-
1160
- .footer__link--button:focus-visible {
1161
- outline: 2px solid var(--accent);
1162
- outline-offset: 4px;
1163
- border-radius: 4px;
1164
- }
1165
-
1166
- .footer__license {
1167
- font-size: 14px;
1168
- color: var(--text-tertiary);
1169
- margin-bottom: 8px;
1170
- }
1171
-
1172
- .footer__built {
1173
- font-size: 14px;
1174
- color: var(--text-tertiary);
1175
- }
1176
-
1177
- .footer__support {
1178
- font-size: 14px;
1179
- color: var(--text-tertiary);
1180
- margin-top: 8px;
1181
- }
1182
-
1183
- /* ========================================
1184
- DONATE MODAL
1185
- ======================================== */
1186
- body.donate-modal-open {
1187
- overflow: hidden;
1188
- }
1189
-
1190
- .donate-modal {
1191
- position: fixed;
1192
- inset: 0;
1193
- z-index: 2000;
1194
- display: none;
1195
- align-items: center;
1196
- justify-content: center;
1197
- padding: 24px;
1198
- background: rgba(5, 6, 9, 0.72);
1199
- backdrop-filter: blur(10px);
1200
- -webkit-backdrop-filter: blur(10px);
1201
- }
1202
-
1203
- .donate-modal.is-open {
1204
- display: flex;
1205
- }
1206
-
1207
- .donate-modal__panel {
1208
- width: min(520px, 100%);
1209
- background: linear-gradient(180deg, rgba(24, 29, 39, 0.95), rgba(14, 18, 26, 0.95));
1210
- border: 1px solid rgba(255, 255, 255, 0.16);
1211
- border-radius: 16px;
1212
- box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
1213
- padding: 26px;
1214
- }
1215
-
1216
- .donate-modal__header {
1217
- display: flex;
1218
- align-items: center;
1219
- justify-content: space-between;
1220
- gap: 16px;
1221
- margin-bottom: 10px;
1222
- }
1223
-
1224
- .donate-modal__title {
1225
- font-size: 24px;
1226
- line-height: 1.15;
1227
- letter-spacing: -0.01em;
1228
- color: var(--text-primary);
1229
- margin: 0;
1230
- }
1231
-
1232
- .donate-modal__close {
1233
- border: 1px solid rgba(255, 255, 255, 0.2);
1234
- background: rgba(255, 255, 255, 0.06);
1235
- color: var(--text-secondary);
1236
- border-radius: 999px;
1237
- width: 32px;
1238
- height: 32px;
1239
- font-size: 20px;
1240
- line-height: 1;
1241
- cursor: pointer;
1242
- transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
1243
- }
1244
-
1245
- .donate-modal__close:hover {
1246
- background: rgba(255, 255, 255, 0.14);
1247
- color: var(--text-primary);
1248
- border-color: rgba(255, 255, 255, 0.35);
1249
- }
1250
-
1251
- .donate-modal__text {
1252
- margin: 0 0 18px 0;
1253
- color: var(--text-secondary);
1254
- font-size: 15px;
1255
- line-height: 1.55;
1256
- }
1257
-
1258
- .donate-amounts {
1259
- display: grid;
1260
- grid-template-columns: repeat(3, minmax(0, 1fr));
1261
- gap: 10px;
1262
- }
1263
-
1264
- .donate-amount {
1265
- border: 1px solid rgba(255, 255, 255, 0.18);
1266
- background: rgba(255, 255, 255, 0.05);
1267
- color: var(--text-primary);
1268
- border-radius: 12px;
1269
- padding: 13px 10px;
1270
- font-size: 15px;
1271
- font-weight: 700;
1272
- letter-spacing: 0.01em;
1273
- cursor: pointer;
1274
- transition: border-color 140ms ease, background 140ms ease, transform 140ms ease;
1275
- }
1276
-
1277
- .donate-amount:hover {
1278
- border-color: rgba(255, 255, 255, 0.36);
1279
- background: rgba(255, 255, 255, 0.1);
1280
- }
1281
-
1282
- .donate-amount.is-selected {
1283
- border-color: rgba(245, 158, 11, 0.92);
1284
- background: rgba(245, 158, 11, 0.17);
1285
- color: #fff4da;
1286
- }
1287
-
1288
- .donate-modal__meta {
1289
- margin-top: 12px;
1290
- color: var(--text-tertiary);
1291
- font-size: 13px;
1292
- line-height: 1.5;
1293
- }
1294
-
1295
- .donate-modal__actions {
1296
- margin-top: 18px;
1297
- display: flex;
1298
- gap: 10px;
1299
- flex-wrap: wrap;
1300
- }
1301
-
1302
- .donate-modal__actions .btn {
1303
- border-radius: 10px;
1304
- min-height: 44px;
1305
- }
1306
-
1307
- .donate-modal__ghost {
1308
- border: 1px solid rgba(255, 255, 255, 0.2);
1309
- background: rgba(255, 255, 255, 0.05);
1310
- color: var(--text-secondary);
1311
- border-radius: 10px;
1312
- padding: 0 16px;
1313
- font-size: 14px;
1314
- font-weight: 600;
1315
- cursor: pointer;
1316
- min-height: 44px;
1317
- }
1318
-
1319
- .donate-modal__ghost:hover {
1320
- color: var(--text-primary);
1321
- border-color: rgba(255, 255, 255, 0.34);
1322
- background: rgba(255, 255, 255, 0.1);
1323
- }
1324
-
1325
- @media (max-width: 640px) {
1326
- .donate-modal__panel {
1327
- padding: 22px 18px;
1328
- }
1329
-
1330
- .donate-amounts {
1331
- grid-template-columns: repeat(3, minmax(0, 1fr));
1332
- }
1333
-
1334
- .donate-modal__actions .btn,
1335
- .donate-modal__ghost {
1336
- width: 100%;
1337
- justify-content: center;
1338
- }
1339
- }
1340
-
1341
- /* ========================================
1342
- SECTION HEADERS (centered)
1343
- ======================================== */
1344
- .section-header {
1345
- text-align: center;
1346
- margin-bottom: 48px;
1347
- }
1348
-
1349
- .section-header .overline {
1350
- margin-bottom: 16px;
1351
- }
1352
-
1353
- .section-header h2 {
1354
- margin-bottom: 16px;
1355
- }
1356
-
1357
- .section-header p {
1358
- max-width: 540px;
1359
- margin: 0 auto;
1360
- }
1361
-
1362
- /* ========================================
1363
- UTILITIES
1364
- ======================================== */
1365
- .text-center { text-align: center; }
1366
- .text-left { text-align: left; }
1367
- .mt-0 { margin-top: 0; }
1368
- </style>
1369
- </head>
1370
- <body>
1371
-
1372
- <!-- ==========================================
1373
- HERO
1374
- ========================================== -->
1375
- <section class="hero" aria-label="Hero">
1376
- <div class="wordmark hero__wordmark" aria-label="markupR">
1377
- markup<span class="wordmark-r">R</span>
1378
- </div>
1379
-
1380
- <h1 class="display hero__headline">
1381
- You found the bug.<br>markup<span class="wordmark-r">R</span> writes it up.
1382
- </h1>
1383
-
1384
- <p class="body-lg hero__subtext">
1385
- AI writes your code. You still have to review it. markupR records your screen while you talk through what you see, then assembles a structured Markdown document your AI agent can act on immediately.
1386
- </p>
1387
-
1388
- <div class="hero__ctas">
1389
- <a href="https://github.com/eddiesanjuan/markupr/releases/latest" class="btn btn--primary" target="_blank" rel="noopener noreferrer">
1390
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2a10 10 0 0 0-3.16 19.5l.9-3.5h4.52l.9 3.5A10 10 0 0 0 12 2Z"/><path d="M12 2v7"/><path d="M8 9h8"/></svg>
1391
- Download for macOS
1392
- </a>
1393
- <a href="https://github.com/eddiesanjuan/markupr" class="btn btn--secondary" target="_blank" rel="noopener noreferrer">
1394
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
1395
- View on GitHub
1396
- </a>
1397
- <button type="button" class="btn btn--secondary" data-donate-open>
1398
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78Z"/></svg>
1399
- Support Development
1400
- </button>
1401
- </div>
1402
-
1403
- <p class="caption hero__support-note">
1404
- If markupr saves you a debugging session, chip in a small donation to keep shipping updates fast.
1405
- </p>
1406
-
1407
- <div class="hero__shortcut">
1408
- <span class="hero__shortcut-label">Start with</span>
1409
- <div class="kbd-group">
1410
- <kbd>&#8984;</kbd>
1411
- <span class="kbd-plus">+</span>
1412
- <kbd>&#8679;</kbd>
1413
- <span class="kbd-plus">+</span>
1414
- <kbd>F</kbd>
1415
- </div>
1416
- </div>
1417
- </section>
1418
-
1419
- <!-- ==========================================
1420
- QUICK VISUAL WALKTHROUGH
1421
- ========================================== -->
1422
- <section class="quickwalk" aria-label="Quick visual walkthrough">
1423
- <div class="container--wide">
1424
- <div class="section-header reveal">
1425
- <p class="overline">Quick visual walkthrough</p>
1426
- <h2>See the full flow in a few seconds.</h2>
1427
- <p class="body-sm" style="max-width: 620px; margin-top: 14px;">
1428
- You record and mark shots. After you stop, markupR runs AI alignment on narration + frames and generates a clean report your agent can use.
1429
- </p>
1430
- </div>
1431
-
1432
- <div class="quickwalk__layout">
1433
- <div class="quickwalk__panel quickwalk__sim reveal reveal-delay-1" aria-label="Animated capture to report pipeline">
1434
- <div class="quickwalk__status">
1435
- <span class="quickwalk__status-dot" aria-hidden="true"></span>
1436
- <span>Session active: recording + manual shot marks</span>
1437
- <span class="quickwalk__status-time">00:42</span>
1438
- </div>
1439
-
1440
- <div class="quickwalk__pipeline" aria-hidden="true">
1441
- <div class="quickwalk__node quickwalk__node--capture">Capture</div>
1442
- <div class="quickwalk__pipe"><span class="quickwalk__pulse"></span></div>
1443
- <div class="quickwalk__node quickwalk__node--align">AI Aligns</div>
1444
- <div class="quickwalk__pipe"><span class="quickwalk__pulse quickwalk__pulse--delay"></span></div>
1445
- <div class="quickwalk__node quickwalk__node--report">Report Ready</div>
1446
- </div>
1447
-
1448
- <p class="quickwalk__caption">
1449
- Shot markers confirm instantly while you narrate. Assembly happens after stop.
1450
- </p>
1451
- </div>
1452
-
1453
- <div class="quickwalk__panel quickwalk__result reveal reveal-delay-2">
1454
- <div class="quickwalk__report" aria-hidden="true">
1455
- <div class="quickwalk__line quickwalk__line--title"></div>
1456
- <div class="quickwalk__line quickwalk__line--full"></div>
1457
- <div class="quickwalk__line quickwalk__line--mid"></div>
1458
- <div class="quickwalk__line quickwalk__line--short"></div>
1459
- <div class="quickwalk__image"></div>
1460
- </div>
1461
- <p class="quickwalk__paste">Output: AI-readable markdown ready to paste into GitHub, Linear, Slack, or your coding agent.</p>
1462
- </div>
1463
- </div>
1464
- </div>
1465
- </section>
1466
-
1467
- <hr class="divider" aria-hidden="true">
1468
-
1469
- <!-- ==========================================
1470
- CORE LOOP
1471
- ========================================== -->
1472
- <section class="loop" aria-label="How it works">
1473
- <div class="container--wide">
1474
- <div class="section-header reveal">
1475
- <p class="overline">How it works</p>
1476
- <h2>Three steps from bug to AI-ready report.</h2>
1477
- </div>
1478
-
1479
- <div class="loop__grid">
1480
- <!-- Step 1 -->
1481
- <div class="loop__step reveal reveal-delay-1">
1482
- <div class="loop__role loop__role--you">you</div>
1483
- <div class="loop__icon-box">
1484
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1485
- <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/>
1486
- <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
1487
- <line x1="12" y1="19" x2="12" y2="22"/>
1488
- </svg>
1489
- </div>
1490
- <div class="loop__label">Record + Narrate</div>
1491
- <div class="loop__desc">Start a session and describe what you see in real time.</div>
1492
- </div>
1493
-
1494
- <!-- Step 2 -->
1495
- <div class="loop__step loop__step--auto reveal reveal-delay-2">
1496
- <div class="loop__role loop__role--auto">markupR</div>
1497
- <div class="loop__icon-box">
1498
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1499
- <circle cx="12" cy="12" r="3"/>
1500
- <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z"/>
1501
- </svg>
1502
- </div>
1503
- <div class="loop__label">Mark + Assemble</div>
1504
- <div class="loop__desc">Manual shots are confirmed instantly, then AI aligns transcript + frames after stop.</div>
1505
- </div>
1506
-
1507
- <!-- Step 3 -->
1508
- <div class="loop__step reveal reveal-delay-3">
1509
- <div class="loop__role loop__role--you">you</div>
1510
- <div class="loop__icon-box">
1511
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1512
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/>
1513
- <path d="M14 2v6h6"/>
1514
- <line x1="9" y1="13" x2="15" y2="13"/>
1515
- <line x1="9" y1="17" x2="13" y2="17"/>
1516
- </svg>
1517
- </div>
1518
- <div class="loop__label">Share Report</div>
1519
- <div class="loop__desc">Open the markdown report and paste it into your tools or AI agent.</div>
1520
- </div>
1521
- </div>
1522
-
1523
- <!-- The actual user experience -->
1524
- <div class="keystroke-flow reveal">
1525
- <div class="keystroke-flow__step">
1526
- <span class="keystroke-flow__keys">&#8984;&#8679;F</span>
1527
- <span class="keystroke-flow__label">start</span>
1528
- </div>
1529
- <span class="keystroke-flow__arrow" aria-hidden="true">&#8594;</span>
1530
- <div class="keystroke-flow__step">
1531
- <span class="keystroke-flow__ellipsis" aria-hidden="true">...</span>
1532
- <span class="keystroke-flow__label">talk</span>
1533
- </div>
1534
- <span class="keystroke-flow__arrow" aria-hidden="true">&#8594;</span>
1535
- <div class="keystroke-flow__step">
1536
- <span class="keystroke-flow__keys">&#8984;&#8679;F</span>
1537
- <span class="keystroke-flow__label">stop</span>
1538
- </div>
1539
- <span class="keystroke-flow__arrow" aria-hidden="true">&#8594;</span>
1540
- <div class="keystroke-flow__step">
1541
- <span class="keystroke-flow__keys">&#8984;V</span>
1542
- <span class="keystroke-flow__label">paste into your agent</span>
1543
- </div>
1544
- </div>
1545
- </div>
1546
- </section>
1547
-
1548
- <hr class="divider" aria-hidden="true">
1549
-
1550
- <!-- ==========================================
1551
- WHY IT MATTERS
1552
- ========================================== -->
1553
- <section aria-label="Why it matters">
1554
- <div class="container--wide">
1555
- <div class="section-header reveal">
1556
- <p class="overline">Why it matters</p>
1557
- <h2>Feedback without the friction.</h2>
1558
- </div>
1559
-
1560
- <div class="why__grid">
1561
- <!-- Card 1 -->
1562
- <div class="card why__card reveal reveal-delay-1">
1563
- <h3>Stop retyping what you already see.</h3>
1564
- <p>
1565
- You found the bug. You can see it right there. Now you have to context-switch into writing mode, describe it in text, screenshot it, crop it, drag it into the right spot. markupR lets you just say it. The documentation happens behind you.
1566
- </p>
1567
- </div>
1568
-
1569
- <!-- Card 2 -->
1570
- <div class="card why__card reveal reveal-delay-2">
1571
- <h3>You talk at 150 wpm. You type at 60.</h3>
1572
- <p>
1573
- A five-minute narration produces more structured output than twenty minutes of writing. And the result is better — markupR places screenshots at the exact moments you were describing something, not wherever you remembered to paste them.
1574
- </p>
1575
- </div>
1576
-
1577
- <!-- Card 3 -->
1578
- <div class="card why__card reveal reveal-delay-3">
1579
- <h3>Choose local privacy or cloud convenience.</h3>
1580
- <p>
1581
- markupr supports both local Whisper transcription and OpenAI BYOK transcription. Stay fully local when privacy is critical, or use cloud transcription when you want fast setup and reliable results.
1582
- </p>
1583
- </div>
1584
- </div>
1585
- </div>
1586
- </section>
1587
-
1588
- <hr class="divider" aria-hidden="true">
1589
-
1590
- <!-- ==========================================
1591
- UNDER THE HOOD
1592
- ========================================== -->
1593
- <section class="tech" aria-label="Technical details">
1594
- <div class="container--wide">
1595
- <div class="section-header reveal">
1596
- <p class="overline">Under the hood</p>
1597
- <h2>Built for developers who read source.</h2>
1598
- </div>
1599
-
1600
- <div class="tech__content">
1601
- <!-- Bullets -->
1602
- <ul class="tech__bullets reveal">
1603
- <li>
1604
- <strong>Dual transcription paths</strong>
1605
- <span>Use local Whisper for on-device processing, or OpenAI BYOK for fast, reliable post-session transcription.</span>
1606
- </li>
1607
- <li>
1608
- <strong>Intelligent frame extraction</strong>
1609
- <span>Records your full screen, then uses transcript timestamps to pull the exact frames that match what you were describing.</span>
1610
- </li>
1611
- <li>
1612
- <strong>macOS native</strong>
1613
- <span>Lives in your menu bar. No dock icon. No browser tab. Launches at login if you want it to.</span>
1614
- </li>
1615
- <li>
1616
- <strong>Structured Markdown output</strong>
1617
- <span>Sections, headings, and images assembled into a document your tools already understand. Paste into GitHub, feed to Claude Code, drop into Slack.</span>
1618
- </li>
1619
- <li>
1620
- <strong>Open source, MIT licensed</strong>
1621
- <span>Read the code, fork it, break it, fix it. No telemetry, no tracking, no analytics.</span>
1622
- </li>
1623
- </ul>
1624
-
1625
- <!-- Code Block -->
1626
- <div class="code-block reveal reveal-delay-1">
1627
- <div class="code-block__header">
1628
- <span class="code-block__dot code-block__dot--red"></span>
1629
- <span class="code-block__dot code-block__dot--yellow"></span>
1630
- <span class="code-block__dot code-block__dot--green"></span>
1631
- <span class="code-block__title">session-output.md</span>
1632
- </div>
1633
- <pre><code><span class="syn-kw">## Feedback Session</span> <span class="syn-cm">— Feb 5, 2026</span>
1634
-
1635
- <span class="syn-kw">### Button sizing issue</span>
1636
- The submit button is way too small on mobile.
1637
- I'm trying to tap it and keep hitting the cancel
1638
- link underneath. Needs more vertical padding,
1639
- maybe 12px minimum tap target.
1640
- <span class="syn-fn">![Screenshot at 0:34]</span><span class="syn-str">(screenshots/fb-001.png)</span>
1641
-
1642
- <span class="syn-kw">### Loading state feels janky</span>
1643
- After the spinner disappears, the content just
1644
- pops in with no transition. There's a visible
1645
- layout shift — the sidebar jumps left by about
1646
- 20 pixels.
1647
- <span class="syn-fn">![Screenshot at 1:12]</span><span class="syn-str">(screenshots/fb-002.png)</span>
1648
-
1649
- <span class="syn-kw">### Nav highlight is wrong</span>
1650
- I'm on the Settings page but the Dashboard tab
1651
- is still highlighted. Looks like the active state
1652
- isn't updating on route change.
1653
- <span class="syn-fn">![Screenshot at 1:45]</span><span class="syn-str">(screenshots/fb-003.png)</span></code></pre>
1654
- </div>
1655
- </div>
1656
- </div>
1657
- </section>
1658
-
1659
- <hr class="divider" aria-hidden="true">
1660
-
1661
- <!-- ==========================================
1662
- PREMIUM TEASER
1663
- ========================================== -->
1664
- <section aria-label="Premium features">
1665
- <div class="container--wide">
1666
- <div class="section-header reveal">
1667
- <p class="overline">Launch Modes</p>
1668
- <h2>Same markupr output. Two ways to run it.</h2>
1669
- <p class="body-lg" style="margin-top: 16px;">
1670
- markupr open source and markupr premium run the same core pipeline: record screen + narration, transcribe, extract the right frames, and generate agent-ready markdown. Premium is the touchless path with hosted keys preconfigured. BYOK stays fully supported.
1671
- </p>
1672
- </div>
1673
-
1674
- <div class="premium__features">
1675
- <div class="card premium__card reveal reveal-delay-1">
1676
- <h3>Open source (BYOK)</h3>
1677
- <p>Bring your own OpenAI + Anthropic keys. Full control, same report quality, fully transparent stack.</p>
1678
- </div>
1679
- <div class="card premium__card reveal reveal-delay-2">
1680
- <h3>Premium (touchless)</h3>
1681
- <p>Exactly the same workflow and output, but keys are hosted for you. No API setup screens, no key management.</p>
1682
- </div>
1683
- <div class="card premium__card reveal reveal-delay-3">
1684
- <h3>Switch anytime</h3>
1685
- <p>Start with BYOK tonight, move to premium when you want convenience. No workflow migration required.</p>
1686
- </div>
1687
- </div>
1688
-
1689
- <div class="premium__cta reveal">
1690
- <a class="btn btn--primary" href="https://github.com/eddiesanjuan/markupr" target="_blank" rel="noopener noreferrer">Use BYOK Open Source</a>
1691
- <a class="btn btn--accent" href="mailto:premium@markupr.com?subject=markupr%20premium%20access">Get Premium Access</a>
1692
- </div>
1693
- <p class="caption premium__note reveal">Launch plan: both BYOK and premium are available on day one. Premium is convenience, not extra capability.</p>
1694
- </div>
1695
- </section>
1696
-
1697
- <!-- ==========================================
1698
- FOOTER
1699
- ========================================== -->
1700
- <footer class="footer" aria-label="Footer">
1701
- <div class="footer__divider"></div>
1702
-
1703
- <nav class="footer__links" aria-label="Footer navigation">
1704
- <a href="https://github.com/eddiesanjuan/markupr" class="footer__link" target="_blank" rel="noopener noreferrer">
1705
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
1706
- GitHub
1707
- </a>
1708
- <button type="button" class="footer__link footer__link--button" data-donate-open>
1709
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78Z"/></svg>
1710
- Support markupr
1711
- </button>
1712
- </nav>
1713
-
1714
- <p class="footer__license">MIT License. Free as in freedom, free as in beer.</p>
1715
- <p class="footer__built">Built by <a href="https://github.com/eddiesanjuan">Eddie San Juan</a>.</p>
1716
- <p class="footer__support">Want faster shipping? <a href="https://ko-fi.com/eddiesanjuan" target="_blank" rel="noopener noreferrer">Support markupr on Ko-fi</a>.</p>
1717
- </footer>
1718
-
1719
- <div class="donate-modal" id="donateModal" aria-hidden="true">
1720
- <div class="donate-modal__panel" role="dialog" aria-modal="true" aria-labelledby="donateModalTitle">
1721
- <div class="donate-modal__header">
1722
- <h3 class="donate-modal__title" id="donateModalTitle">Support markupr</h3>
1723
- <button type="button" class="donate-modal__close" aria-label="Close donation dialog" data-donate-close>&times;</button>
1724
- </div>
1725
- <p class="donate-modal__text">
1726
- If markupr saves you time, you can drop a quick contribution here. Pick an amount and continue straight to Ko-fi checkout.
1727
- </p>
1728
- <div class="donate-amounts" role="radiogroup" aria-label="Donation amount">
1729
- <button type="button" class="donate-amount" data-amount="5" aria-pressed="false">$5</button>
1730
- <button type="button" class="donate-amount is-selected" data-amount="10" aria-pressed="true">$10</button>
1731
- <button type="button" class="donate-amount" data-amount="25" aria-pressed="false">$25</button>
1732
- </div>
1733
- <p class="donate-modal__meta">
1734
- You will complete payment securely on Ko-fi.
1735
- </p>
1736
- <div class="donate-modal__actions">
1737
- <button type="button" class="btn btn--accent" id="donateContinue">Continue to Ko-fi</button>
1738
- <button type="button" class="donate-modal__ghost" data-donate-close>Not now</button>
1739
- </div>
1740
- </div>
1741
- </div>
1742
-
1743
- <!-- ==========================================
1744
- SCROLL REVEAL — Minimal JS
1745
- ========================================== -->
1746
- <script>
1747
- (function() {
1748
- // Respect reduced motion preference
1749
- if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
1750
-
1751
- var observer = new IntersectionObserver(function(entries) {
1752
- entries.forEach(function(entry) {
1753
- if (entry.isIntersecting) {
1754
- entry.target.classList.add('visible');
1755
- observer.unobserve(entry.target);
1756
- }
1757
- });
1758
- }, { threshold: 0.15 });
1759
-
1760
- document.querySelectorAll('.reveal').forEach(function(el) {
1761
- observer.observe(el);
1762
- });
1763
- })();
1764
-
1765
- (function() {
1766
- var modal = document.getElementById('donateModal');
1767
- var openButtons = document.querySelectorAll('[data-donate-open]');
1768
- var continueButton = document.getElementById('donateContinue');
1769
- var amountButtons = document.querySelectorAll('.donate-amount');
1770
- var closeButtons = document.querySelectorAll('[data-donate-close]');
1771
- var selectedAmount = '10';
1772
-
1773
- if (!modal || !openButtons.length || !continueButton || !amountButtons.length) return;
1774
-
1775
- function setAmount(amount) {
1776
- selectedAmount = amount;
1777
- amountButtons.forEach(function(button) {
1778
- var isSelected = button.getAttribute('data-amount') === amount;
1779
- button.classList.toggle('is-selected', isSelected);
1780
- button.setAttribute('aria-pressed', isSelected ? 'true' : 'false');
1781
- });
1782
- }
1783
-
1784
- function openModal() {
1785
- modal.classList.add('is-open');
1786
- modal.setAttribute('aria-hidden', 'false');
1787
- document.body.classList.add('donate-modal-open');
1788
- }
1789
-
1790
- function closeModal() {
1791
- modal.classList.remove('is-open');
1792
- modal.setAttribute('aria-hidden', 'true');
1793
- document.body.classList.remove('donate-modal-open');
1794
- }
1795
-
1796
- openButtons.forEach(function(button) {
1797
- button.addEventListener('click', openModal);
1798
- });
1799
- closeButtons.forEach(function(button) {
1800
- button.addEventListener('click', closeModal);
1801
- });
1802
-
1803
- modal.addEventListener('click', function(event) {
1804
- if (event.target === modal) {
1805
- closeModal();
1806
- }
1807
- });
1808
-
1809
- document.addEventListener('keydown', function(event) {
1810
- if (event.key === 'Escape' && modal.classList.contains('is-open')) {
1811
- closeModal();
1812
- }
1813
- });
1814
-
1815
- amountButtons.forEach(function(button) {
1816
- button.addEventListener('click', function() {
1817
- var amount = button.getAttribute('data-amount') || '10';
1818
- setAmount(amount);
1819
- });
1820
- });
1821
-
1822
- continueButton.addEventListener('click', function() {
1823
- var donateUrl = new URL('https://ko-fi.com/eddiesanjuan');
1824
- donateUrl.searchParams.set('amount', selectedAmount);
1825
- donateUrl.searchParams.set('hidefeed', 'true');
1826
- donateUrl.searchParams.set('utm_source', 'markupr_site');
1827
- donateUrl.searchParams.set('utm_medium', 'donation_modal');
1828
- window.open(donateUrl.toString(), '_blank', 'noopener,noreferrer');
1829
- closeModal();
1830
- });
1831
- })();
1832
- </script>
1833
-
1834
- </body>
1835
- </html>