@birdycom/omniaui 0.11.0 → 0.11.2

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