@invisibleloop/pulse 0.12.3 → 0.14.1

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 (166) hide show
  1. package/.claude/commands/verify.md +9 -5
  2. package/.claude/pulse-checklist.md +21 -1
  3. package/.github/workflows/publish.yml +1 -0
  4. package/.pulse-verified +1 -0
  5. package/CLAUDE.md +56 -15
  6. package/docs/.claude/commands/verify.md +4 -0
  7. package/docs/.claude/pulse-checklist.md +20 -2
  8. package/docs/public/.pulse-ui-version +1 -1
  9. package/docs/public/andypixel.css +237 -0
  10. package/docs/public/bookworms.css +422 -0
  11. package/docs/public/coutumes.css +584 -0
  12. package/docs/public/images/bookworms/hero-books.jpg +0 -0
  13. package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
  14. package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
  15. package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
  16. package/docs/public/images/journey/178.jpg +0 -0
  17. package/docs/public/images/journey/29.jpg +0 -0
  18. package/docs/public/images/retrowheels/hero.jpg +0 -0
  19. package/docs/public/images/shop/devbook-pro.jpg +0 -0
  20. package/docs/public/images/suzukibento/1015.jpg +0 -0
  21. package/docs/public/images/suzukibento/1025.jpg +0 -0
  22. package/docs/public/images/suzukibento/1043.jpg +0 -0
  23. package/docs/public/images/suzukibento/1059.jpg +0 -0
  24. package/docs/public/images/suzukibento/1062.jpg +0 -0
  25. package/docs/public/images/suzukibento/1084.jpg +0 -0
  26. package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  27. package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  28. package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  29. package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  30. package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  31. package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  32. package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  33. package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  34. package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  35. package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  36. package/docs/public/journey.css +528 -0
  37. package/docs/public/pulse-ui.css +1036 -22
  38. package/docs/public/retrowheels.css +402 -0
  39. package/docs/public/shop.css +235 -0
  40. package/docs/public/sparkys.css +349 -0
  41. package/docs/public/suzukibento.css +517 -0
  42. package/docs/public/swish.css +394 -0
  43. package/docs/server.js +19 -4
  44. package/docs/src/lib/nav.js +14 -3
  45. package/docs/src/pages/components/decorate.js +74 -0
  46. package/docs/src/pages/components/gallery.js +102 -0
  47. package/docs/src/pages/components/hero.js +1 -0
  48. package/docs/src/pages/components/marquee.js +76 -0
  49. package/docs/src/pages/components/photo-card.js +74 -0
  50. package/docs/src/pages/components/section.js +40 -1
  51. package/docs/src/pages/meta.js +26 -0
  52. package/docs/src/pages/templates/andypixel.js +260 -0
  53. package/docs/src/pages/templates/bookworms.js +227 -0
  54. package/docs/src/pages/templates/coutumes.js +226 -0
  55. package/docs/src/pages/templates/journey.js +186 -0
  56. package/docs/src/pages/templates/retrowheels.js +342 -0
  57. package/docs/src/pages/templates/shop.js +257 -0
  58. package/docs/src/pages/templates/sparkys.js +249 -0
  59. package/docs/src/pages/templates/suzukibento.js +200 -0
  60. package/docs/src/pages/templates/swish.js +313 -0
  61. package/docs/src/pages/templates.js +313 -2
  62. package/docs/src/pages/testing.js +2 -1
  63. package/examples/andypixel.css +237 -0
  64. package/examples/andypixel.js +258 -0
  65. package/examples/bookworms.css +422 -0
  66. package/examples/bookworms.js +225 -0
  67. package/examples/contact.js +1 -0
  68. package/examples/counter.js +1 -0
  69. package/examples/dev.server.js +17 -0
  70. package/examples/journey.css +528 -0
  71. package/examples/journey.js +186 -0
  72. package/examples/pricing.js +1 -0
  73. package/examples/quiz.js +1 -0
  74. package/examples/retrowheels.css +402 -0
  75. package/examples/retrowheels.js +340 -0
  76. package/examples/shared.js +6 -0
  77. package/examples/suzukibento.css +513 -0
  78. package/examples/suzukibento.js +200 -0
  79. package/examples/swish.css +394 -0
  80. package/examples/swish.js +311 -0
  81. package/examples/todos.js +1 -0
  82. package/package.json +2 -3
  83. package/public/.pulse-ui-version +1 -1
  84. package/public/images/bookworms/hero-books.jpg +0 -0
  85. package/public/images/bookworms/how-we-read-now.jpg +0 -0
  86. package/public/images/bookworms/lantern-keepers.jpg +0 -0
  87. package/public/images/bookworms/provincial-bookseller.jpg +0 -0
  88. package/public/images/journey/178.jpg +0 -0
  89. package/public/images/journey/29.jpg +0 -0
  90. package/public/images/retrowheels/hero.jpg +0 -0
  91. package/public/images/shop/devbook-pro.jpg +0 -0
  92. package/public/images/suzukibento/1015.jpg +0 -0
  93. package/public/images/suzukibento/1025.jpg +0 -0
  94. package/public/images/suzukibento/1043.jpg +0 -0
  95. package/public/images/suzukibento/1059.jpg +0 -0
  96. package/public/images/suzukibento/1062.jpg +0 -0
  97. package/public/images/suzukibento/1084.jpg +0 -0
  98. package/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  99. package/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  100. package/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  101. package/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  102. package/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  103. package/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  104. package/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  105. package/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  106. package/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  107. package/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  108. package/public/pulse-ui.css +1036 -22
  109. package/scripts/build.js +72 -18
  110. package/src/agent/checklist.md +20 -2
  111. package/src/agent/commands/verify.md +4 -0
  112. package/src/agent/guide-components.md +145 -7
  113. package/src/agent/guide-design-gallery.md +288 -0
  114. package/src/agent/guide-design-references.md +172 -0
  115. package/src/agent/guide-explore.md +313 -0
  116. package/src/agent/guide-styles.md +197 -11
  117. package/src/agent/guide-templates.md +220 -10
  118. package/src/agent/guide.md +9 -4
  119. package/src/agent/workflow.md +208 -58
  120. package/src/cli/dev.js +39 -7
  121. package/src/cli/fmt.js +113 -0
  122. package/src/cli/index.js +63 -30
  123. package/src/cli/scaffold.js +2 -2
  124. package/src/html.js +6 -1
  125. package/src/mcp/server.js +2415 -101
  126. package/src/mcp/validate-worker.js +34 -5
  127. package/src/runtime/ssr.js +4 -1
  128. package/src/server/index.js +22 -2
  129. package/src/spec/schema.js +5 -0
  130. package/src/testing/html.js +130 -4
  131. package/src/testing/testing.test.js +58 -0
  132. package/src/ui/banner.js +24 -9
  133. package/src/ui/brand-statement.js +58 -0
  134. package/src/ui/button.js +12 -1
  135. package/src/ui/card.js +20 -1
  136. package/src/ui/colophon.js +29 -0
  137. package/src/ui/contact-row.js +41 -0
  138. package/src/ui/cta.js +18 -4
  139. package/src/ui/decorate.js +55 -0
  140. package/src/ui/display-heading.js +44 -0
  141. package/src/ui/drop-cap.js +29 -0
  142. package/src/ui/editorial-hero.js +63 -0
  143. package/src/ui/entry-list.js +36 -0
  144. package/src/ui/feature.js +20 -3
  145. package/src/ui/footer.js +78 -9
  146. package/src/ui/gallery.js +56 -0
  147. package/src/ui/hero.js +68 -18
  148. package/src/ui/image-pair.js +72 -0
  149. package/src/ui/index.js +20 -0
  150. package/src/ui/input.js +1 -0
  151. package/src/ui/marquee.js +48 -0
  152. package/src/ui/nav.js +19 -9
  153. package/src/ui/photo-card.js +44 -0
  154. package/src/ui/product-card.js +65 -0
  155. package/src/ui/pullquote.js +14 -3
  156. package/src/ui/search.js +1 -0
  157. package/src/ui/section-label.js +38 -0
  158. package/src/ui/section.js +1 -1
  159. package/src/ui/service-card.js +47 -0
  160. package/src/ui/stat-grid.js +40 -0
  161. package/src/ui/stat.js +16 -2
  162. package/src/ui/testimonial.js +17 -6
  163. package/src/ui/textarea.js +1 -0
  164. package/src/ui/top-strip.js +47 -0
  165. package/src/ui/trust-list.js +46 -0
  166. package/src/ui/ui.test.js +35 -0
@@ -0,0 +1,517 @@
1
+ /*
2
+ * suzukibento.css — Susuki Bento artist portfolio example
3
+ *
4
+ * Brutalist gallery palette: near-black ground, bone-white text, vermilion accent.
5
+ * Dark by default — palette set as bare token overrides on :root so they
6
+ * cascade into --ui-bg, --ui-text, --ui-accent etc. via pulse-ui.css.
7
+ * Zero border-radius throughout (--radius: 0px).
8
+ * Typography: Inter → system-ui (no external font requests).
9
+ *
10
+ * Token responsibility:
11
+ * :root — dark palette + zero-radius + font stack
12
+ * Component styles — layout only, var() references throughout
13
+ */
14
+
15
+ /* ==========================================================================
16
+ 1. Token overrides (hex values live here)
17
+ ========================================================================== */
18
+
19
+ /*
20
+ * Override the bare cascade tokens that --ui-* variables reference.
21
+ * See pulse-ui.css :root: --ui-bg: var(--bg, ...) etc.
22
+ * Setting --bg, --surface, --text, --accent etc. on :root is enough to
23
+ * retheme the entire UI without touching --ui-* directly.
24
+ */
25
+ :root {
26
+ --bg: #0a0a0a;
27
+ --surface: #131313;
28
+ --surface-2: #1c1c1c;
29
+ --border: #2a2a2a;
30
+ --text: #f5f3ef;
31
+ --muted: #8a8780;
32
+ --accent: #ff4a2a; /* vermilion — 4.5:1 on #0a0a0a */
33
+ --accent-hover: #ff6b4f;
34
+ --accent-dim: rgba(255, 74, 42, 0.14);
35
+ --accent-text: #0a0a0a;
36
+
37
+ /* Zero-radius brutalist design language */
38
+ --radius: 0px;
39
+ --radius-sm: 0px;
40
+
41
+ /* Font stack — system Inter, no HTTP request */
42
+ --font: 'Inter', system-ui, sans-serif;
43
+ --font-display: 'Inter', system-ui, sans-serif;
44
+ }
45
+
46
+ /* ==========================================================================
47
+ 2. Component styles (var() only — no hex values below this line)
48
+ ========================================================================== */
49
+
50
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
51
+
52
+ body {
53
+ font-family: var(--ui-font, system-ui, sans-serif);
54
+ line-height: 1.5;
55
+ -webkit-font-smoothing: antialiased;
56
+ }
57
+
58
+ /* --------------------------------------------------------------------------
59
+ Skip link
60
+ -------------------------------------------------------------------------- */
61
+ .skip-link {
62
+ position: absolute;
63
+ left: -9999px;
64
+ top: 0;
65
+ background: var(--ui-accent);
66
+ color: var(--ui-accent-text);
67
+ padding: var(--ui-space-2) var(--ui-space-4);
68
+ z-index: 100;
69
+ }
70
+
71
+ .skip-link:focus { left: var(--ui-space-2); }
72
+
73
+ /* --------------------------------------------------------------------------
74
+ Sticky top nav (hand-rolled — no nav() component)
75
+ -------------------------------------------------------------------------- */
76
+ .sb-nav {
77
+ position: sticky;
78
+ top: 0;
79
+ z-index: 50;
80
+ display: flex;
81
+ justify-content: space-between;
82
+ align-items: center;
83
+ padding: var(--ui-space-5) var(--ui-space-8);
84
+ background: var(--ui-bg);
85
+ border-bottom: 1px solid var(--ui-border);
86
+ font-size: var(--ui-text-sm);
87
+ text-transform: uppercase;
88
+ letter-spacing: 0.08em;
89
+ }
90
+
91
+ .sb-nav__brand {
92
+ font-weight: 700;
93
+ color: var(--ui-text);
94
+ text-decoration: none;
95
+ display: inline-flex;
96
+ align-items: center;
97
+ gap: 0.3em;
98
+ }
99
+
100
+ .sb-nav__dot {
101
+ display: inline-block;
102
+ width: 0.45em;
103
+ height: 0.45em;
104
+ border-radius: 50%;
105
+ background: var(--ui-accent);
106
+ flex-shrink: 0;
107
+ }
108
+
109
+ .sb-nav__links {
110
+ display: flex;
111
+ gap: var(--ui-space-6);
112
+ list-style: none;
113
+ }
114
+
115
+ .sb-nav__links a {
116
+ color: var(--ui-muted);
117
+ text-decoration: none;
118
+ }
119
+
120
+ .sb-nav__links a:hover,
121
+ .sb-nav__links a:focus { color: var(--ui-accent); }
122
+
123
+ /* Burger — hidden on desktop */
124
+ .sb-nav__toggle-input { display: none; }
125
+ .sb-nav__burger { display: none; }
126
+
127
+ @media (max-width: 600px) {
128
+ .sb-nav {
129
+ flex-wrap: wrap;
130
+ padding: var(--ui-space-4) var(--ui-space-5);
131
+ }
132
+
133
+ /* Show burger button */
134
+ .sb-nav__burger {
135
+ display: flex;
136
+ flex-direction: column;
137
+ justify-content: space-between;
138
+ width: 22px;
139
+ height: 16px;
140
+ cursor: pointer;
141
+ }
142
+
143
+ .sb-nav__burger span {
144
+ display: block;
145
+ height: 2px;
146
+ background: var(--ui-text);
147
+ transition: transform 0.2s, opacity 0.2s;
148
+ }
149
+
150
+ /* Animate to × when checked */
151
+ .sb-nav__toggle-input:checked ~ .sb-nav__burger span:nth-child(1) {
152
+ transform: translateY(7px) rotate(45deg);
153
+ }
154
+ .sb-nav__toggle-input:checked ~ .sb-nav__burger span:nth-child(2) {
155
+ opacity: 0;
156
+ }
157
+ .sb-nav__toggle-input:checked ~ .sb-nav__burger span:nth-child(3) {
158
+ transform: translateY(-7px) rotate(-45deg);
159
+ }
160
+
161
+ /* Nav hidden by default on mobile */
162
+ .sb-nav nav {
163
+ display: none;
164
+ position: absolute;
165
+ top: 100%;
166
+ left: 0;
167
+ right: 0;
168
+ background: var(--ui-bg);
169
+ border-bottom: 1px solid var(--ui-border);
170
+ z-index: 49;
171
+ }
172
+
173
+ /* Show nav when checked */
174
+ .sb-nav__toggle-input:checked ~ nav {
175
+ display: block;
176
+ }
177
+
178
+ .sb-nav__links {
179
+ flex-direction: column;
180
+ gap: 0;
181
+ padding: var(--ui-space-4) var(--ui-space-5);
182
+ }
183
+
184
+ .sb-nav__links li { border-bottom: 1px solid var(--ui-border); }
185
+
186
+ .sb-nav__links a {
187
+ display: block;
188
+ padding: var(--ui-space-3) 0;
189
+ color: var(--ui-text);
190
+ }
191
+ }
192
+
193
+ /* --------------------------------------------------------------------------
194
+ Hero
195
+ -------------------------------------------------------------------------- */
196
+ .sb-hero {
197
+ padding: var(--ui-space-24) var(--ui-space-8) var(--ui-space-16);
198
+ border-bottom: 1px solid var(--ui-border);
199
+ }
200
+
201
+ .sb-hero__eyebrow {
202
+ display: inline-block;
203
+ font-size: var(--ui-text-sm);
204
+ text-transform: uppercase;
205
+ letter-spacing: 0.18em;
206
+ color: var(--ui-accent);
207
+ margin-bottom: var(--ui-space-6);
208
+ }
209
+
210
+ .sb-hero__title {
211
+ font-size: clamp(3.5rem, 12vw, 11rem);
212
+ font-weight: 800;
213
+ line-height: 0.9;
214
+ letter-spacing: -0.05em;
215
+ color: var(--ui-text);
216
+ }
217
+
218
+ .sb-hero__title em {
219
+ font-style: normal;
220
+ color: var(--ui-accent);
221
+ }
222
+
223
+ .sb-hero__meta {
224
+ margin-top: var(--ui-space-10);
225
+ display: flex;
226
+ flex-wrap: wrap;
227
+ gap: var(--ui-space-8);
228
+ }
229
+
230
+ .sb-hero__meta-label {
231
+ display: block;
232
+ font-size: var(--ui-text-sm);
233
+ text-transform: uppercase;
234
+ letter-spacing: 0.12em;
235
+ color: var(--ui-muted);
236
+ }
237
+
238
+ .sb-hero__meta strong {
239
+ display: block;
240
+ color: var(--ui-text);
241
+ font-weight: 600;
242
+ margin-top: var(--ui-space-1);
243
+ letter-spacing: 0.04em;
244
+ font-size: var(--ui-text-sm);
245
+ text-transform: uppercase;
246
+ }
247
+
248
+ /* --------------------------------------------------------------------------
249
+ Section scaffolding
250
+ -------------------------------------------------------------------------- */
251
+ .sb-section {
252
+ padding: var(--ui-space-20) var(--ui-space-8);
253
+ border-bottom: 1px solid var(--ui-border);
254
+ }
255
+
256
+ .sb-section__head {
257
+ display: flex;
258
+ justify-content: space-between;
259
+ align-items: baseline;
260
+ margin-bottom: var(--ui-space-12);
261
+ gap: var(--ui-space-6);
262
+ flex-wrap: wrap;
263
+ }
264
+
265
+ .sb-section__label {
266
+ font-size: var(--ui-text-sm);
267
+ text-transform: uppercase;
268
+ letter-spacing: 0.18em;
269
+ color: var(--ui-accent);
270
+ }
271
+
272
+ .sb-section__title {
273
+ font-size: clamp(1.75rem, 4vw, 2.5rem);
274
+ font-weight: 700;
275
+ letter-spacing: -0.02em;
276
+ color: var(--ui-text);
277
+ }
278
+
279
+ /* --------------------------------------------------------------------------
280
+ Works grid (12-col asymmetric layout)
281
+ -------------------------------------------------------------------------- */
282
+ .sb-works {
283
+ display: grid;
284
+ grid-template-columns: repeat(12, 1fr);
285
+ grid-auto-rows: 120px;
286
+ gap: var(--ui-space-4);
287
+ }
288
+
289
+ .sb-work {
290
+ position: relative;
291
+ overflow: hidden;
292
+ background: var(--ui-surface);
293
+ }
294
+
295
+ .sb-work img {
296
+ width: 100%;
297
+ height: 100%;
298
+ object-fit: cover;
299
+ display: block;
300
+ filter: grayscale(15%) contrast(1.05);
301
+ transition: filter 0.4s ease, transform 0.6s ease;
302
+ }
303
+
304
+ .sb-work:hover img {
305
+ filter: none;
306
+ transform: scale(1.02);
307
+ }
308
+
309
+ .sb-work__caption {
310
+ position: absolute;
311
+ left: var(--ui-space-3);
312
+ bottom: var(--ui-space-3);
313
+ background: var(--ui-bg);
314
+ color: var(--ui-text);
315
+ padding: var(--ui-space-1) var(--ui-space-3);
316
+ font-size: var(--ui-text-xs);
317
+ text-transform: uppercase;
318
+ letter-spacing: 0.1em;
319
+ }
320
+
321
+ .sb-work__caption span {
322
+ color: var(--ui-muted);
323
+ margin-left: var(--ui-space-2);
324
+ }
325
+
326
+ /* Explicit positions — gap-free brickwork layout */
327
+ .sb-work--a { grid-column: 1 / 8; grid-row: 1 / 5; }
328
+ .sb-work--b { grid-column: 8 / 13; grid-row: 1 / 4; }
329
+ .sb-work--c { grid-column: 8 / 13; grid-row: 4 / 8; }
330
+ .sb-work--d { grid-column: 1 / 5; grid-row: 5 / 8; }
331
+ .sb-work--e { grid-column: 5 / 8; grid-row: 5 / 8; }
332
+ .sb-work--f { grid-column: 1 / 13; grid-row: 8 / 12; }
333
+
334
+ @media (max-width: 720px) {
335
+ .sb-works { grid-auto-rows: 80px; }
336
+ .sb-work--a, .sb-work--b, .sb-work--c,
337
+ .sb-work--d, .sb-work--e, .sb-work--f {
338
+ grid-column: span 12;
339
+ grid-row: span 3;
340
+ }
341
+ }
342
+
343
+ /* --------------------------------------------------------------------------
344
+ Artist statement
345
+ -------------------------------------------------------------------------- */
346
+ .sb-statement {
347
+ max-width: 56rem;
348
+ display: grid;
349
+ grid-template-columns: 1fr 2fr;
350
+ gap: var(--ui-space-12);
351
+ }
352
+
353
+ .sb-statement p {
354
+ font-size: var(--ui-text-xl);
355
+ line-height: 1.6;
356
+ color: var(--ui-text);
357
+ margin-bottom: var(--ui-space-5);
358
+ }
359
+
360
+ .sb-statement p + p {
361
+ color: var(--ui-muted);
362
+ font-size: var(--ui-text-lg);
363
+ }
364
+
365
+ .sb-statement__bio {
366
+ font-size: var(--ui-text-sm);
367
+ color: var(--ui-muted);
368
+ line-height: 1.7;
369
+ }
370
+
371
+ .sb-statement__bio dt {
372
+ text-transform: uppercase;
373
+ letter-spacing: 0.12em;
374
+ color: var(--ui-accent);
375
+ font-size: var(--ui-text-xs);
376
+ margin-top: var(--ui-space-4);
377
+ }
378
+
379
+ .sb-statement__bio dd { color: var(--ui-text); }
380
+
381
+ @media (max-width: 720px) {
382
+ .sb-statement { grid-template-columns: 1fr; }
383
+ }
384
+
385
+ /* --------------------------------------------------------------------------
386
+ Exhibition table
387
+ -------------------------------------------------------------------------- */
388
+ .sb-exhibitions {
389
+ list-style: none;
390
+ border-top: 1px solid var(--ui-border);
391
+ }
392
+
393
+ .sb-exhibition {
394
+ display: grid;
395
+ grid-template-columns: 80px 1fr 1fr 120px;
396
+ gap: var(--ui-space-6);
397
+ padding: var(--ui-space-5) 0;
398
+ border-bottom: 1px solid var(--ui-border);
399
+ align-items: baseline;
400
+ }
401
+
402
+ .sb-exhibition__year {
403
+ font-weight: 700;
404
+ color: var(--ui-accent);
405
+ font-variant-numeric: tabular-nums;
406
+ }
407
+
408
+ .sb-exhibition__title { color: var(--ui-text); font-weight: 500; }
409
+ .sb-exhibition__venue { color: var(--ui-muted); }
410
+
411
+ .sb-exhibition__tag {
412
+ font-size: var(--ui-text-xs);
413
+ text-transform: uppercase;
414
+ letter-spacing: 0.1em;
415
+ color: var(--ui-muted);
416
+ text-align: right;
417
+ }
418
+
419
+ .sb-exhibition__tag--solo { color: var(--ui-accent); }
420
+
421
+ @media (max-width: 720px) {
422
+ .sb-exhibition { grid-template-columns: 60px 1fr; }
423
+ .sb-exhibition__venue,
424
+ .sb-exhibition__tag { grid-column: 2; text-align: left; }
425
+ }
426
+
427
+ /* --------------------------------------------------------------------------
428
+ Press list with dot separator
429
+ -------------------------------------------------------------------------- */
430
+ .sb-press {
431
+ display: flex;
432
+ flex-wrap: wrap;
433
+ gap: var(--ui-space-8);
434
+ font-size: var(--ui-text-lg);
435
+ font-weight: 500;
436
+ color: var(--ui-muted);
437
+ }
438
+
439
+ .sb-press span { white-space: nowrap; }
440
+
441
+ .sb-press span + span::before {
442
+ content: "\00b7";
443
+ margin-right: var(--ui-space-8);
444
+ color: var(--ui-accent);
445
+ }
446
+
447
+ @media (max-width: 600px) {
448
+ .sb-press {
449
+ flex-direction: column;
450
+ gap: var(--ui-space-3);
451
+ font-size: var(--ui-text-base);
452
+ }
453
+
454
+ .sb-press span + span::before { display: none; }
455
+ }
456
+
457
+ /* --------------------------------------------------------------------------
458
+ Contact
459
+ -------------------------------------------------------------------------- */
460
+ .sb-contact {
461
+ display: grid;
462
+ grid-template-columns: 1fr 1fr;
463
+ gap: var(--ui-space-12);
464
+ align-items: start;
465
+ }
466
+
467
+ .sb-contact h3 {
468
+ font-size: var(--ui-text-sm);
469
+ text-transform: uppercase;
470
+ letter-spacing: 0.18em;
471
+ color: var(--ui-accent);
472
+ margin-bottom: var(--ui-space-4);
473
+ }
474
+
475
+ .sb-contact p,
476
+ .sb-contact a {
477
+ font-size: var(--ui-text-lg);
478
+ color: var(--ui-text);
479
+ line-height: 1.6;
480
+ text-decoration: none;
481
+ display: block;
482
+ }
483
+
484
+ .sb-contact a:hover { color: var(--ui-accent); }
485
+
486
+ @media (max-width: 720px) {
487
+ .sb-contact { grid-template-columns: 1fr; gap: var(--ui-space-8); }
488
+ }
489
+
490
+ /* --------------------------------------------------------------------------
491
+ Footer
492
+ -------------------------------------------------------------------------- */
493
+ .sb-foot {
494
+ display: flex;
495
+ justify-content: space-between;
496
+ padding: var(--ui-space-6) var(--ui-space-8);
497
+ font-size: var(--ui-text-xs);
498
+ text-transform: uppercase;
499
+ letter-spacing: 0.12em;
500
+ color: var(--ui-muted);
501
+ border-top: 1px solid var(--ui-border);
502
+ }
503
+
504
+ @media (max-width: 600px) {
505
+ .sb-foot {
506
+ flex-direction: column;
507
+ gap: var(--ui-space-2);
508
+ padding: var(--ui-space-5);
509
+ }
510
+ }
511
+
512
+ .sb-foot__link {
513
+ color: var(--ui-muted);
514
+ text-decoration: none;
515
+ }
516
+
517
+ .sb-foot__link:hover { color: var(--ui-text); }