@birdycom/omniaui 0.0.0-stage → 0.11.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 (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -2
  3. package/bin/lib/add.mjs +126 -0
  4. package/bin/lib/catalog.mjs +169 -0
  5. package/bin/lib/project.mjs +90 -0
  6. package/bin/omniaui.mjs +70 -0
  7. package/dist/code-block.css +163 -0
  8. package/dist/components-extra.css +914 -0
  9. package/dist/components.css +10081 -0
  10. package/dist/components.min.css +1 -0
  11. package/dist/data-table.d.ts +9 -0
  12. package/dist/data-table.js +505 -0
  13. package/dist/docs-ui.css +265 -0
  14. package/dist/oc-theme.css +69 -0
  15. package/dist/parts/.gitkeep +0 -0
  16. package/dist/parts/app-accordion-accent.css +60 -0
  17. package/dist/parts/app-alpha-tabs.css +68 -0
  18. package/dist/parts/app-busy-overlay.css +25 -0
  19. package/dist/parts/app-column-menu.css +85 -0
  20. package/dist/parts/app-confirm-dialog.css +70 -0
  21. package/dist/parts/app-cookie-consent.css +63 -0
  22. package/dist/parts/app-data-table.css +174 -0
  23. package/dist/parts/app-greeting.css +47 -0
  24. package/dist/parts/app-list-toolbar.css +81 -0
  25. package/dist/parts/app-page-header.css +54 -0
  26. package/dist/parts/app-severity-card.css +91 -0
  27. package/dist/parts/app-sidebar-dark.css +15 -0
  28. package/dist/parts/app-sortable-row.css +52 -0
  29. package/dist/parts/app-stat-card.css +68 -0
  30. package/dist/parts/app-table-actions.css +71 -0
  31. package/dist/parts/app-table-compact.css +54 -0
  32. package/dist/parts/app-top-progress.css +68 -0
  33. package/dist/parts/app-tree.css +104 -0
  34. package/dist/parts/app-upgrade-banner.css +90 -0
  35. package/dist/parts/app-version-banner.css +80 -0
  36. package/dist/parts/data-chart-line.css +167 -0
  37. package/dist/parts/forms-affix-input.css +75 -0
  38. package/dist/parts/forms-barcode-input.css +75 -0
  39. package/dist/parts/forms-chip-filter.css +91 -0
  40. package/dist/parts/forms-color-select.css +89 -0
  41. package/dist/parts/forms-decimal-input.css +51 -0
  42. package/dist/parts/forms-filter-counts.css +94 -0
  43. package/dist/parts/forms-language-select.css +76 -0
  44. package/dist/parts/forms-number-input.css +95 -0
  45. package/dist/parts/forms-password-requirements.css +55 -0
  46. package/dist/parts/forms-phone-input.css +85 -0
  47. package/dist/parts/forms-time-input.css +74 -0
  48. package/dist/parts/forms-toggle-field.css +69 -0
  49. package/dist/parts/media-art.css +21 -0
  50. package/dist/parts/media-attachment.css +160 -0
  51. package/dist/parts/media-browser-frame.css +73 -0
  52. package/dist/parts/media-card.css +83 -0
  53. package/dist/parts/media-chat-composer.css +90 -0
  54. package/dist/parts/media-doc-viewer.css +151 -0
  55. package/dist/parts/media-emoji-picker.css +88 -0
  56. package/dist/parts/media-entity-card.css +52 -0
  57. package/dist/parts/media-flip-card.css +78 -0
  58. package/dist/parts/media-image-zoom.css +68 -0
  59. package/dist/parts/media-lightbox.css +131 -0
  60. package/dist/parts/media-message-thread.css +136 -0
  61. package/dist/parts/media-message.css +160 -0
  62. package/dist/parts/media-row.css +78 -0
  63. package/dist/parts/media-stack-card.css +82 -0
  64. package/dist/parts/media-terminal-log.css +76 -0
  65. package/dist/parts/media-thumb.css +71 -0
  66. package/dist/parts/site-fact-list.css +25 -0
  67. package/dist/parts/site-link-arrow.css +34 -0
  68. package/dist/parts/site-marquee.css +34 -0
  69. package/dist/parts/site-next-teaser.css +75 -0
  70. package/dist/parts/site-price-card.css +75 -0
  71. package/dist/parts/site-product-mark.css +66 -0
  72. package/dist/parts/site-project-card.css +111 -0
  73. package/dist/parts/site-prose.css +124 -0
  74. package/dist/parts/site-stack-list.css +48 -0
  75. package/dist/parts/site-timeline.css +54 -0
  76. package/dist/showcase.css +404 -0
  77. package/manifest.json +1108 -0
  78. package/package.json +67 -4
  79. package/snippets/accordion-accent.html +24 -0
  80. package/snippets/accordion.html +11 -0
  81. package/snippets/affix-input.html +30 -0
  82. package/snippets/alert-dialog.html +22 -0
  83. package/snippets/alert.html +5 -0
  84. package/snippets/alpha-tabs.html +37 -0
  85. package/snippets/aspect-ratio.html +15 -0
  86. package/snippets/attachment.html +43 -0
  87. package/snippets/avatar.html +6 -0
  88. package/snippets/back-link.html +5 -0
  89. package/snippets/badge.html +5 -0
  90. package/snippets/barcode-input.html +18 -0
  91. package/snippets/breadcrumb.html +6 -0
  92. package/snippets/browser-frame.html +18 -0
  93. package/snippets/busy-overlay.html +31 -0
  94. package/snippets/button-group.html +21 -0
  95. package/snippets/button.html +8 -0
  96. package/snippets/calendar.html +43 -0
  97. package/snippets/card.html +17 -0
  98. package/snippets/carousel.html +21 -0
  99. package/snippets/chart-line.html +46 -0
  100. package/snippets/chart.html +27 -0
  101. package/snippets/chat-composer.html +21 -0
  102. package/snippets/checkbox.html +5 -0
  103. package/snippets/checklist.html +23 -0
  104. package/snippets/chip-filter.html +14 -0
  105. package/snippets/code-window.html +20 -0
  106. package/snippets/collapsible.html +19 -0
  107. package/snippets/color-field.html +13 -0
  108. package/snippets/color-select.html +18 -0
  109. package/snippets/column-menu.html +38 -0
  110. package/snippets/combobox.html +19 -0
  111. package/snippets/command.html +24 -0
  112. package/snippets/confirm-dialog.html +32 -0
  113. package/snippets/context-menu.html +25 -0
  114. package/snippets/cookie-consent.html +70 -0
  115. package/snippets/copy-field.html +19 -0
  116. package/snippets/counter.html +2 -0
  117. package/snippets/data-table.html +151 -0
  118. package/snippets/date-picker.html +32 -0
  119. package/snippets/decimal-input.html +20 -0
  120. package/snippets/definition-list.html +20 -0
  121. package/snippets/demo-panel.html +18 -0
  122. package/snippets/doc-viewer.html +58 -0
  123. package/snippets/drawer.html +30 -0
  124. package/snippets/dropdown-menu.html +27 -0
  125. package/snippets/dropdown.html +9 -0
  126. package/snippets/dropzone.html +13 -0
  127. package/snippets/emoji-picker.html +51 -0
  128. package/snippets/empty-state.html +17 -0
  129. package/snippets/entity-card.html +26 -0
  130. package/snippets/expandable-table.html +26 -0
  131. package/snippets/fact-list.html +14 -0
  132. package/snippets/filter-counts.html +30 -0
  133. package/snippets/flip-card.html +22 -0
  134. package/snippets/form-field.html +6 -0
  135. package/snippets/greeting.html +22 -0
  136. package/snippets/heading.html +14 -0
  137. package/snippets/hover-card.html +18 -0
  138. package/snippets/image-zoom.html +30 -0
  139. package/snippets/input-group.html +10 -0
  140. package/snippets/input.html +5 -0
  141. package/snippets/item.html +26 -0
  142. package/snippets/kbd.html +13 -0
  143. package/snippets/label.html +9 -0
  144. package/snippets/language-select.html +26 -0
  145. package/snippets/launcher-card.html +14 -0
  146. package/snippets/lightbox.html +50 -0
  147. package/snippets/link-arrow.html +10 -0
  148. package/snippets/list-toolbar.html +33 -0
  149. package/snippets/marquee.html +15 -0
  150. package/snippets/media-card.html +28 -0
  151. package/snippets/media-row.html +21 -0
  152. package/snippets/media-thumb.html +12 -0
  153. package/snippets/menubar.html +30 -0
  154. package/snippets/message-thread.html +123 -0
  155. package/snippets/message.html +51 -0
  156. package/snippets/modal.html +15 -0
  157. package/snippets/native-select.html +15 -0
  158. package/snippets/navmenu.html +23 -0
  159. package/snippets/next-teaser.html +15 -0
  160. package/snippets/number-input.html +27 -0
  161. package/snippets/otp-input.html +9 -0
  162. package/snippets/page-header.html +24 -0
  163. package/snippets/pagination.html +6 -0
  164. package/snippets/password-input.html +24 -0
  165. package/snippets/password-requirements.html +30 -0
  166. package/snippets/phone-input.html +22 -0
  167. package/snippets/popover.html +15 -0
  168. package/snippets/price-card.html +34 -0
  169. package/snippets/product-mark.html +13 -0
  170. package/snippets/progress.html +4 -0
  171. package/snippets/project-card.html +23 -0
  172. package/snippets/prose.html +54 -0
  173. package/snippets/radio.html +17 -0
  174. package/snippets/recovery-codes.html +20 -0
  175. package/snippets/resizable.html +16 -0
  176. package/snippets/scroll-area.html +20 -0
  177. package/snippets/search.html +8 -0
  178. package/snippets/segmented.html +11 -0
  179. package/snippets/select.html +7 -0
  180. package/snippets/separator.html +15 -0
  181. package/snippets/severity-card.html +35 -0
  182. package/snippets/sheet.html +32 -0
  183. package/snippets/sidebar-docs.html +24 -0
  184. package/snippets/sidebar.html +55 -0
  185. package/snippets/site-footer.html +6 -0
  186. package/snippets/site-header.html +26 -0
  187. package/snippets/skeleton.html +4 -0
  188. package/snippets/slider.html +17 -0
  189. package/snippets/sortable-row.html +31 -0
  190. package/snippets/spinner.html +14 -0
  191. package/snippets/stack-card.html +17 -0
  192. package/snippets/stack-list.html +16 -0
  193. package/snippets/stat-card.html +24 -0
  194. package/snippets/stepper.html +13 -0
  195. package/snippets/switch.html +6 -0
  196. package/snippets/table-actions.html +39 -0
  197. package/snippets/table-compact.html +24 -0
  198. package/snippets/table.html +29 -0
  199. package/snippets/tabs.html +9 -0
  200. package/snippets/tag-input.html +17 -0
  201. package/snippets/terminal-log.html +23 -0
  202. package/snippets/textarea.html +3 -0
  203. package/snippets/theme-switch.html +6 -0
  204. package/snippets/time-input.html +26 -0
  205. package/snippets/timeline.html +28 -0
  206. package/snippets/toast-stack.html +10 -0
  207. package/snippets/toast.html +36 -0
  208. package/snippets/toc.html +13 -0
  209. package/snippets/toggle-field.html +20 -0
  210. package/snippets/toggle.html +22 -0
  211. package/snippets/tooltip.html +4 -0
  212. package/snippets/top-progress.html +12 -0
  213. package/snippets/tree.html +38 -0
  214. package/snippets/upgrade-banner.html +28 -0
  215. package/snippets/user.html +15 -0
  216. package/snippets/version-banner.html +15 -0
@@ -0,0 +1,404 @@
1
+ /* File: packages/ui/src/showcase.css */
2
+ /* Map: accent presets -> text/eyebrow/buttons -> backdrop -> sections -> code window/browser frame ->
3
+ icon tile -> product card -> pills -> stat strip -> spotlight -> steps -> marquee -> timeline ->
4
+ fact/tag/check lists -> site header -> site footer -> reveal on scroll */
5
+ /* Showcase components — modern site/marketing building blocks, pure CSS (no JavaScript).
6
+ Colors: Omnia palette tokens only (OKLCH), via the theme tokens of components.css.
7
+ Accent per element: --oc-accent-from / --oc-accent-to (or an .oc-accent--* preset). */
8
+
9
+ :root {
10
+ --oc-accent-from: var(--oc-brand-1);
11
+ --oc-accent-to: var(--oc-brand-2);
12
+ --oc-radius-lg: 1.25rem;
13
+ --oc-radius-md: 0.75rem;
14
+ --oc-ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
15
+ }
16
+
17
+ /* Accent presets: <article class="oc-product-card oc-accent--green"> */
18
+ .oc-accent--blue { --oc-accent-from: light-dark(var(--color-vividblue-95), var(--color-vividblue-60)); --oc-accent-to: light-dark(var(--color-blue-40), var(--color-blue-20)); }
19
+ .oc-accent--cyan { --oc-accent-from: light-dark(var(--color-teal-60), var(--color-skycyan-70)); --oc-accent-to: light-dark(var(--color-teal-80), var(--color-teal-40)); }
20
+ .oc-accent--green { --oc-accent-from: light-dark(var(--color-green-60), var(--color-green-20)); --oc-accent-to: light-dark(var(--color-teal-70), var(--color-teal-50)); }
21
+ .oc-accent--orange { --oc-accent-from: light-dark(var(--color-peru-80), var(--color-orange-70)); --oc-accent-to: light-dark(var(--color-red-70), var(--color-red-40)); }
22
+ .oc-accent--red { --oc-accent-from: light-dark(var(--color-red-60), var(--color-red-30)); --oc-accent-to: light-dark(var(--color-magenta-80), var(--color-magenta-60)); }
23
+ .oc-accent--violet { --oc-accent-from: light-dark(var(--color-purple-30), var(--color-violet-70)); --oc-accent-to: light-dark(var(--color-indigo-30), var(--color-purple-30)); }
24
+ .oc-accent--yellow { --oc-accent-from: light-dark(var(--color-peru-60), var(--color-yellow-80)); --oc-accent-to: light-dark(var(--color-orange-95), var(--color-orange-80)); }
25
+
26
+ /* Text with the brand gradient, or with the element accent */
27
+ .oc-text-gradient,
28
+ .oc-text-accent {
29
+ background: var(--oc-gradient-brand);
30
+ -webkit-background-clip: text;
31
+ background-clip: text;
32
+ color: transparent;
33
+ }
34
+ .oc-text-accent { background-image: linear-gradient(100deg, var(--oc-accent-from), var(--oc-accent-to)); }
35
+
36
+ /* Small uppercase label above a title */
37
+ .oc-eyebrow {
38
+ margin: 0 0 1rem;
39
+ color: var(--oc-brand-1);
40
+ font-size: 0.8rem;
41
+ font-weight: 600;
42
+ letter-spacing: 0.14em;
43
+ text-transform: uppercase;
44
+ }
45
+
46
+ /* Buttons: pill shape, gradient fill and glass (combine with .oc-btn) */
47
+ .oc-btn--pill { border-radius: 999px; }
48
+ .oc-btn--gradient {
49
+ border-radius: 999px;
50
+ background: linear-gradient(100deg, var(--oc-accent-from), var(--oc-accent-to));
51
+ color: var(--color-white-50);
52
+ box-shadow: 0 10px 30px -10px var(--oc-accent-to);
53
+ transition: transform 0.2s var(--oc-ease-out), box-shadow 0.2s;
54
+ }
55
+ .oc-btn--gradient:hover { transform: translateY(-2px); box-shadow: 0 16px 40px -10px var(--oc-accent-to); opacity: 1; }
56
+ .oc-btn--glass {
57
+ border-radius: 999px;
58
+ border-color: var(--oc-border-strong);
59
+ background: var(--oc-surface);
60
+ color: var(--oc-foreground);
61
+ }
62
+ .oc-btn--glass:hover { background: var(--oc-surface-strong); }
63
+
64
+ /* Link with an arrow that moves on hover */
65
+ .oc-link-arrow {
66
+ display: inline-flex;
67
+ align-items: center;
68
+ gap: 0.4rem;
69
+ color: var(--oc-brand-1);
70
+ font-weight: 600;
71
+ text-decoration: none;
72
+ }
73
+ .oc-link-arrow::after { content: "→"; transition: translate 0.2s var(--oc-ease-out); }
74
+ .oc-link-arrow:hover::after { translate: 4px 0; }
75
+
76
+ /* Backdrop: slowly drifting color blobs + fine grid behind the page.
77
+ <div class="oc-backdrop" aria-hidden="true"><i class="oc-backdrop__blob"></i><i class="oc-backdrop__grid"></i></div>
78
+ .oc-backdrop--local keeps it inside a positioned section instead of the viewport. */
79
+ .oc-backdrop {
80
+ position: fixed;
81
+ inset: 0;
82
+ z-index: -1;
83
+ overflow: hidden;
84
+ pointer-events: none;
85
+ }
86
+ .oc-backdrop--local { position: absolute; }
87
+ .oc-backdrop::before,
88
+ .oc-backdrop::after,
89
+ .oc-backdrop > .oc-backdrop__blob {
90
+ content: "";
91
+ position: absolute;
92
+ inline-size: 42rem;
93
+ aspect-ratio: 1;
94
+ border-radius: 50%;
95
+ filter: blur(120px);
96
+ opacity: var(--oc-backdrop-opacity, 0.35);
97
+ animation: oc-drift 24s ease-in-out infinite alternate;
98
+ }
99
+ .oc-backdrop::before { background: var(--color-blue-40); inset-block-start: -18rem; inset-inline-start: -12rem; }
100
+ .oc-backdrop::after { background: var(--color-purple-30); inset-block-start: 10rem; inset-inline-end: -16rem; animation-delay: -8s; }
101
+ .oc-backdrop > .oc-backdrop__blob { background: var(--color-teal-40); inset-block-end: -20rem; inset-inline-start: 30%; animation-delay: -16s; opacity: calc(var(--oc-backdrop-opacity, 0.35) * 0.6); }
102
+ .oc-backdrop > .oc-backdrop__grid {
103
+ position: absolute;
104
+ inset: 0;
105
+ background-image:
106
+ linear-gradient(oklch(from var(--oc-foreground) l c h / 0.05) 1px, transparent 1px),
107
+ linear-gradient(90deg, oklch(from var(--oc-foreground) l c h / 0.05) 1px, transparent 1px);
108
+ background-size: 64px 64px;
109
+ mask-image: radial-gradient(ellipse at 50% 0%, black 20%, transparent 70%);
110
+ }
111
+ @keyframes oc-drift { to { transform: translate(6rem, 4rem) scale(1.15); } }
112
+
113
+ /* Container, section spacing and display headings */
114
+ .oc-container { inline-size: min(100% - 2.5rem, var(--oc-container, 1200px)); margin-inline: auto; }
115
+ .oc-section-pad { padding-block: clamp(4rem, 9vw, 7rem); }
116
+ .oc-section-pad--tight { padding-block: clamp(2rem, 5vw, 3.5rem); }
117
+ .oc-display {
118
+ margin: 0 0 1.5rem;
119
+ font-size: clamp(2.6rem, 5.6vw, 4.6rem);
120
+ line-height: 1.05;
121
+ letter-spacing: -0.025em;
122
+ font-weight: 700;
123
+ text-wrap: balance;
124
+ }
125
+ .oc-display--md { font-size: clamp(1.9rem, 4vw, 3rem); }
126
+ .oc-lead { margin: 0 0 1rem; color: var(--oc-muted-foreground); font-size: 1.15rem; line-height: 1.65; max-inline-size: 42rem; text-wrap: pretty; }
127
+ .oc-split { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); gap: 3rem; align-items: center; }
128
+
129
+ /* Code window: editor frame with traffic lights and a file name (always dark, like an editor) */
130
+ .oc-code-window {
131
+ position: relative;
132
+ border: 1px solid var(--oc-border-strong);
133
+ border-radius: var(--oc-radius-lg);
134
+ background: oklch(from var(--color-shallowblue-60) l c h / 0.9);
135
+ box-shadow: 0 40px 80px -30px oklch(from var(--color-black-50) l c h / 0.6);
136
+ backdrop-filter: blur(12px);
137
+ overflow: hidden;
138
+ color-scheme: dark;
139
+ }
140
+ .oc-code-window__bar { display: flex; align-items: center; gap: 0.45rem; padding: 0.8rem 1rem; border-block-end: 1px solid var(--oc-border); }
141
+ .oc-code-window__bar > i { inline-size: 0.7rem; aspect-ratio: 1; border-radius: 50%; background: var(--color-red-50); }
142
+ .oc-code-window__bar > i:nth-child(2) { background: var(--color-orange-60); }
143
+ .oc-code-window__bar > i:nth-child(3) { background: var(--color-green-20); }
144
+ .oc-code-window__title { margin-inline-start: 0.6rem; color: var(--oc-muted-foreground); font-family: var(--oc-font-mono); font-size: 0.78rem; }
145
+ .oc-code-window .oc-code-block { margin: 0; border: 0; border-radius: 0; }
146
+ .oc-code-window__body { margin: 0; padding: 1.4rem 1.5rem; font-family: var(--oc-font-mono); font-size: clamp(0.75rem, 1.3vw, 0.92rem); line-height: 1.85; color: var(--oc-foreground); overflow-x: auto; }
147
+
148
+ /* Browser frame around a screenshot */
149
+ .oc-browser-frame {
150
+ display: block;
151
+ border: 1px solid var(--oc-border-strong);
152
+ border-radius: var(--oc-radius-md);
153
+ background: var(--oc-card);
154
+ overflow: hidden;
155
+ box-shadow: 0 30px 60px -30px oklch(from var(--oc-accent-to) l c h / 0.55);
156
+ }
157
+ .oc-browser-frame__bar { display: flex; gap: 0.4rem; padding: 0.6rem 0.8rem; border-block-end: 1px solid var(--oc-border); background: var(--oc-surface-strong); }
158
+ .oc-browser-frame__bar > i { inline-size: 0.6rem; aspect-ratio: 1; border-radius: 50%; background: var(--oc-border-strong); }
159
+ .oc-browser-frame__bar > i:first-child { background: var(--oc-accent-from); }
160
+ .oc-browser-frame > img,
161
+ .oc-browser-frame__body { display: block; inline-size: 100%; aspect-ratio: 16 / 10; object-fit: cover; object-position: top; }
162
+
163
+ /* Icon tile: rounded square with an accent gradient (product logo, feature icon) */
164
+ .oc-icon-tile {
165
+ --oc-tile-size: 3rem;
166
+ display: inline-grid;
167
+ place-items: center;
168
+ inline-size: var(--oc-tile-size);
169
+ aspect-ratio: 1;
170
+ flex-shrink: 0;
171
+ border-radius: 28%;
172
+ border: 1px solid oklch(from var(--oc-accent-from) l c h / 0.4);
173
+ background:
174
+ linear-gradient(135deg, oklch(from var(--oc-accent-from) l c h / 0.3), oklch(from var(--oc-accent-to) l c h / 0.18)),
175
+ var(--oc-card);
176
+ color: var(--oc-accent-from);
177
+ font-size: calc(var(--oc-tile-size) * 0.45);
178
+ line-height: 1;
179
+ }
180
+ .oc-icon-tile > :is(img, svg) { inline-size: 58%; block-size: 58%; object-fit: contain; }
181
+ .oc-icon-tile--sm { --oc-tile-size: 2.4rem; }
182
+ .oc-icon-tile--lg { --oc-tile-size: 3.5rem; }
183
+ .oc-icon-tile--xl { --oc-tile-size: clamp(7rem, 22vw, 15rem); border-radius: 30%; box-shadow: 0 0 120px -20px var(--oc-accent-from); }
184
+ .oc-icon-tile--float { animation: oc-float 6s ease-in-out infinite; }
185
+ @keyframes oc-float { 50% { translate: 0 -12px; } }
186
+
187
+ /* Product / feature card with hover lift and accent glow */
188
+ .oc-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--oc-card-min, 17rem)), 1fr)); gap: 1.25rem; }
189
+ .oc-product-card {
190
+ position: relative;
191
+ display: flex;
192
+ flex-direction: column;
193
+ gap: 0.5rem;
194
+ padding: 1.6rem;
195
+ border: 1px solid var(--oc-border);
196
+ border-radius: var(--oc-radius-lg);
197
+ background:
198
+ radial-gradient(22rem circle at 50% 0%, oklch(from var(--oc-accent-from) l c h / var(--oc-glow, 0)), transparent 60%),
199
+ var(--oc-surface);
200
+ color: var(--oc-foreground);
201
+ text-decoration: none;
202
+ transition: transform 0.35s var(--oc-ease-out), border-color 0.35s, --oc-glow 0.35s;
203
+ }
204
+ @property --oc-glow {
205
+ syntax: "<number>";
206
+ inherits: false;
207
+ initial-value: 0;
208
+ }
209
+ .oc-product-card:hover {
210
+ --oc-glow: 0.22;
211
+ transform: translateY(-6px);
212
+ border-color: oklch(from var(--oc-accent-from) l c h / 0.55);
213
+ }
214
+ .oc-product-card__top { display: flex; justify-content: space-between; align-items: flex-start; margin-block-end: 0.75rem; }
215
+ .oc-product-card__name { margin: 0; font-size: 1.45rem; font-weight: 700; letter-spacing: -0.02em; }
216
+ .oc-product-card__tagline { margin: 0; color: var(--oc-accent-from); font-weight: 600; font-size: 0.95rem; }
217
+ .oc-product-card__summary { margin: 0; color: var(--oc-muted-foreground); font-size: 0.95rem; flex-grow: 1; }
218
+ .oc-product-card__more { display: inline-flex; align-items: center; gap: 0.4rem; margin-block-start: 0.75rem; font-weight: 600; font-size: 0.92rem; }
219
+ .oc-product-card__more::after { content: "→"; transition: translate 0.25s var(--oc-ease-out); }
220
+ .oc-product-card:hover .oc-product-card__more::after { translate: 5px 0; }
221
+
222
+ /* Pill badge (neutral) and accent pill + pulsing status dot */
223
+ .oc-pill {
224
+ display: inline-flex;
225
+ align-items: center;
226
+ gap: 0.45rem;
227
+ padding: 0.3rem 0.75rem;
228
+ border: 1px solid var(--oc-border);
229
+ border-radius: 999px;
230
+ background: var(--oc-surface);
231
+ color: var(--oc-muted-foreground);
232
+ font-size: 0.78rem;
233
+ font-weight: 600;
234
+ }
235
+ .oc-pill--accent {
236
+ border-color: oklch(from var(--oc-accent-from) l c h / 0.45);
237
+ background: oklch(from var(--oc-accent-from) l c h / 0.14);
238
+ color: var(--oc-accent-from);
239
+ }
240
+ .oc-status-dot {
241
+ inline-size: 0.5rem;
242
+ aspect-ratio: 1;
243
+ border-radius: 50%;
244
+ background: var(--oc-accent-from);
245
+ animation: oc-pulse 2s infinite;
246
+ }
247
+ @keyframes oc-pulse {
248
+ 0% { box-shadow: 0 0 0 0 oklch(from var(--oc-accent-from) l c h / 0.6); }
249
+ 70%, 100% { box-shadow: 0 0 0 8px transparent; }
250
+ }
251
+
252
+ /* Stat strip: big numbers in a bordered row (pairs with .oc-counter for counting up) */
253
+ .oc-stat-strip {
254
+ display: grid;
255
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
256
+ border-block: 1px solid var(--oc-border);
257
+ background: var(--oc-surface);
258
+ }
259
+ .oc-stat-strip__item { padding: 2rem 1.5rem; border-inline-start: 1px solid var(--oc-border); }
260
+ .oc-stat-strip__item:first-child { border-inline-start: 0; }
261
+ .oc-stat-strip__value { font-size: clamp(2.2rem, 4vw, 3rem); font-weight: 700; line-height: 1.1; }
262
+ .oc-stat-strip__suffix { color: var(--oc-brand-1); }
263
+ .oc-stat-strip__label { display: block; color: var(--oc-muted-foreground); font-size: 0.92rem; }
264
+
265
+ /* Spotlight: large highlighted panel with copy + module list */
266
+ .oc-spotlight {
267
+ display: grid;
268
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
269
+ gap: 3rem;
270
+ align-items: center;
271
+ padding: clamp(2rem, 5vw, 3.5rem);
272
+ border: 1px solid oklch(from var(--oc-accent-from) l c h / 0.4);
273
+ border-radius: calc(var(--oc-radius-lg) * 1.6);
274
+ background:
275
+ radial-gradient(36rem circle at 100% 0%, oklch(from var(--oc-accent-to) l c h / 0.16), transparent 60%),
276
+ radial-gradient(36rem circle at 0% 100%, oklch(from var(--oc-accent-from) l c h / 0.22), transparent 60%),
277
+ var(--oc-card);
278
+ }
279
+ .oc-spotlight__title { display: flex; align-items: center; gap: 1rem; margin: 1.25rem 0 0.5rem; font-size: clamp(2.2rem, 5vw, 3.5rem); line-height: 1.1; }
280
+ .oc-spotlight__tagline { color: var(--oc-accent-from); font-size: 1.2rem; font-weight: 600; }
281
+ .oc-spotlight__modules { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 0.75rem; margin: 0; padding: 0; list-style: none; }
282
+ .oc-spotlight__modules > li { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-surface); font-size: 0.95rem; }
283
+
284
+ /* Steps: numbered process with a gradient tick (numbers come from a CSS counter) */
285
+ .oc-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1.25rem; margin: 0; padding: 0; list-style: none; counter-reset: oc-step; }
286
+ .oc-steps > li { position: relative; padding: 1.75rem 1.5rem; border-block-start: 2px solid var(--oc-border-strong); counter-increment: oc-step; }
287
+ .oc-steps > li::before { content: ""; position: absolute; inset-block-start: -2px; inset-inline-start: 0; inline-size: 3rem; block-size: 2px; background: var(--oc-gradient-brand); }
288
+ .oc-steps > li::after { content: counter(oc-step, decimal-leading-zero); position: absolute; inset-block-start: 1.75rem; inset-inline-end: 1.5rem; color: var(--oc-brand-1); font-family: var(--oc-font-mono); font-size: 0.85rem; }
289
+ .oc-steps h3 { margin: 0 0 0.5rem; font-size: 1.3rem; }
290
+ .oc-steps p { margin: 0; color: var(--oc-muted-foreground); font-size: 0.95rem; }
291
+
292
+ /* Marquee: endless scrolling row (repeat the items once; pauses on hover) */
293
+ .oc-marquee { overflow: hidden; mask-image: linear-gradient(90deg, transparent, black 12%, black 88%, transparent); }
294
+ .oc-marquee__track { display: flex; gap: 1rem; inline-size: max-content; animation: oc-marquee var(--oc-marquee-duration, 40s) linear infinite; }
295
+ .oc-marquee:hover .oc-marquee__track { animation-play-state: paused; }
296
+ .oc-marquee__item { padding: 0.6rem 1.2rem; border: 1px solid var(--oc-border); border-radius: 999px; background: var(--oc-surface); color: var(--oc-muted-foreground); font-family: var(--oc-font-mono); font-size: 0.88rem; white-space: nowrap; }
297
+ @keyframes oc-marquee { to { transform: translateX(calc(-50% - 0.5rem)); } }
298
+
299
+ /* Timeline */
300
+ .oc-timeline { display: grid; gap: 0.9rem; margin: 0; padding: 0 0 0 1.25rem; list-style: none; border-inline-start: 1px solid var(--oc-border-strong); }
301
+ .oc-timeline > li { display: grid; gap: 0.15rem; }
302
+ .oc-timeline strong { color: var(--oc-brand-1); font-family: var(--oc-font-mono); font-size: 0.9rem; }
303
+ .oc-timeline span { color: var(--oc-muted-foreground); font-size: 0.95rem; }
304
+
305
+ /* Fact list: label/value rows in a card (<dl><div><dt>…</dt><dd>…</dd></div></dl>) */
306
+ .oc-fact-list { margin: 0; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-lg); background: var(--oc-surface); }
307
+ .oc-fact-list > div { display: flex; justify-content: space-between; gap: 1rem; padding: 1rem 1.25rem; border-block-start: 1px solid var(--oc-border); }
308
+ .oc-fact-list > div:first-child { border-block-start: 0; }
309
+ .oc-fact-list dt { color: var(--oc-muted-foreground); }
310
+ .oc-fact-list dd { margin: 0; text-align: end; font-weight: 600; }
311
+
312
+ /* Tag list (tech stack) and check list */
313
+ .oc-tag-list { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
314
+ .oc-tag-list > li { padding: 0.55rem 1.1rem; border: 1px solid oklch(from var(--oc-accent-from) l c h / 0.35); border-radius: 999px; background: oklch(from var(--oc-accent-from) l c h / 0.1); font-family: var(--oc-font-mono); font-size: 0.88rem; }
315
+ .oc-check-list { display: grid; gap: 0.5rem; margin: 1.1rem 0 0; padding: 0; list-style: none; font-size: 0.93rem; }
316
+ .oc-check-list > li { display: flex; gap: 0.5rem; align-items: baseline; }
317
+ .oc-check-list > li::before { content: "✓"; color: var(--oc-brand-1); font-weight: 700; }
318
+
319
+ /* Site header: sticky; turns into glass after scrolling (scroll-driven animation, no JS).
320
+ Mobile menu: <button popovertarget="site-nav"> + <nav id="site-nav" popover> — no JS either. */
321
+ .oc-site-header {
322
+ position: sticky;
323
+ inset-block-start: 0;
324
+ z-index: 50;
325
+ border-block-end: 1px solid transparent;
326
+ }
327
+ @supports (animation-timeline: scroll()) {
328
+ .oc-site-header {
329
+ animation: oc-header-glass linear both;
330
+ animation-timeline: scroll(root);
331
+ animation-range: 0 120px;
332
+ }
333
+ }
334
+ @keyframes oc-header-glass {
335
+ to {
336
+ background: oklch(from var(--oc-background) l c h / 0.72);
337
+ border-block-end-color: var(--oc-border);
338
+ backdrop-filter: blur(16px) saturate(1.4);
339
+ }
340
+ }
341
+ .oc-site-header__inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; block-size: 4.5rem; }
342
+ .oc-site-header__brand { display: inline-flex; align-items: center; gap: 0.6rem; color: var(--oc-foreground); font-size: 1.3rem; font-weight: 700; letter-spacing: -0.03em; text-decoration: none; }
343
+ .oc-site-header__nav { display: flex; align-items: center; gap: 2rem; font-size: 0.95rem; }
344
+ .oc-site-header__nav[popover] { position: static; inset: auto; margin: 0; padding: 0; border: 0; background: none; color: inherit; overflow: visible; }
345
+ .oc-site-header__nav a:not(.oc-btn) { color: var(--oc-muted-foreground); text-decoration: none; transition: color 0.2s; }
346
+ .oc-site-header__nav a:not(.oc-btn):hover,
347
+ .oc-site-header__nav a[aria-current="page"] { color: var(--oc-foreground); }
348
+ .oc-site-header__toggle { display: none; padding: 0.5rem 0.7rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-surface); color: var(--oc-foreground); cursor: pointer; }
349
+ @media (min-width: 861px) {
350
+ /* Desktop: the popover nav is always visible in the header bar. */
351
+ .oc-site-header__nav[popover] { display: flex; }
352
+ }
353
+ @media (max-width: 860px) {
354
+ .oc-site-header__toggle { display: block; }
355
+ .oc-site-header__nav[popover]:popover-open {
356
+ position: fixed;
357
+ inset: 4.5rem 0 auto;
358
+ display: flex;
359
+ flex-direction: column;
360
+ align-items: stretch;
361
+ gap: 0.25rem;
362
+ padding: 1rem 1.25rem 1.5rem;
363
+ border-block-end: 1px solid var(--oc-border);
364
+ background: oklch(from var(--oc-background) l c h / 0.96);
365
+ backdrop-filter: blur(16px);
366
+ }
367
+ .oc-site-header__nav[popover] a:not(.oc-btn) { padding: 0.75rem 0; font-size: 1.1rem; }
368
+ }
369
+
370
+ /* Site footer: brand column + link columns + bottom bar */
371
+ .oc-site-footer {
372
+ margin-block-start: 4rem;
373
+ padding-block-start: 4rem;
374
+ border-block-start: 1px solid var(--oc-border);
375
+ background: linear-gradient(to bottom, transparent, var(--oc-surface-strong));
376
+ color: var(--oc-muted-foreground);
377
+ font-size: 0.95rem;
378
+ }
379
+ .oc-site-footer__grid { display: grid; grid-template-columns: 1.6fr repeat(3, 1fr); gap: 2.5rem; }
380
+ .oc-site-footer__about p { margin-block: 1rem 0; max-inline-size: 22rem; }
381
+ .oc-site-footer__title { margin: 0 0 1rem; color: var(--oc-foreground); font-size: 0.95rem; font-weight: 700; }
382
+ .oc-site-footer__list { display: flex; flex-direction: column; gap: 0.5rem; margin: 0; padding: 0; list-style: none; font-style: normal; }
383
+ .oc-site-footer a { color: inherit; text-decoration: none; }
384
+ .oc-site-footer a:hover { color: var(--oc-foreground); }
385
+ .oc-site-footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; margin-block-start: 3rem; padding-block: 1.5rem; border-block-start: 1px solid var(--oc-border); font-size: 0.85rem; }
386
+ @media (max-width: 860px) {
387
+ .oc-site-footer__grid { grid-template-columns: 1fr 1fr; }
388
+ .oc-site-footer__about { grid-column: 1 / -1; }
389
+ }
390
+
391
+ /* Reveal on scroll: elements slide in when they enter the viewport (scroll-driven, no JS; visible without support) */
392
+ @supports (animation-timeline: view()) {
393
+ .oc-reveal {
394
+ animation: oc-reveal linear both;
395
+ animation-timeline: view();
396
+ animation-range: entry 0% cover 30%;
397
+ }
398
+ }
399
+ @keyframes oc-reveal { from { opacity: 0; translate: 0 24px; } }
400
+
401
+ @media (prefers-reduced-motion: reduce) {
402
+ .oc-backdrop::before, .oc-backdrop::after, .oc-backdrop > .oc-backdrop__blob,
403
+ .oc-icon-tile--float, .oc-marquee__track, .oc-status-dot, .oc-reveal, .oc-site-header { animation: none; }
404
+ }