ferst-core 0.7.0 → 0.7.4

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 (38) hide show
  1. package/components/BlockRenderer.astro +6 -0
  2. package/components/Pagination.astro +51 -0
  3. package/components/blocks/Heading.astro +47 -46
  4. package/components/blocks/Prose.astro +10 -2
  5. package/components/blocks/Quote.astro +40 -38
  6. package/components/recipes/Accordion.astro +91 -84
  7. package/components/recipes/ContactCard.astro +172 -170
  8. package/components/recipes/Document.astro +152 -0
  9. package/components/recipes/FeatureCards.astro +112 -111
  10. package/components/recipes/Hero.astro +120 -8
  11. package/components/recipes/LinkRow.astro +91 -0
  12. package/components/recipes/MediaCards.astro +213 -212
  13. package/components/recipes/Notice.astro +90 -89
  14. package/components/recipes/Stats.astro +152 -145
  15. package/components/recipes/Table.astro +98 -97
  16. package/components/recipes/Tabs.astro +134 -133
  17. package/components/recipes/Tiles.astro +287 -271
  18. package/components/recipes/Timetable.astro +189 -0
  19. package/components/recipes/Video.astro +177 -157
  20. package/content/blocks.ts +68 -2
  21. package/content/cookies.ts +10 -0
  22. package/examples/primitives.ts +2 -0
  23. package/examples/recipes.ts +42 -36
  24. package/integration.mjs +83 -64
  25. package/layouts/Base.astro +481 -475
  26. package/lib/documents.ts +68 -62
  27. package/lib/embed-consent.ts +78 -73
  28. package/lib/inline-markdown.ts +12 -0
  29. package/lib/links.ts +12 -0
  30. package/lib/paginate.ts +23 -0
  31. package/lib/posts.ts +20 -2
  32. package/lib/timetable.ts +27 -0
  33. package/lib/video.ts +101 -56
  34. package/package.json +1 -1
  35. package/routes/cookie-policy.astro +27 -0
  36. package/routes/posts/index.astro +36 -31
  37. package/routes/posts/page/[page].astro +36 -0
  38. package/routes/posts/tag/[...tag].astro +41 -37
@@ -1,212 +1,213 @@
1
- ---
2
- /**
3
- * MediaCards — a Level-2 recipe: a grid of rich content cards with the IMAGE as a
4
- * header on top (cover-cropped to a uniform ratio, like PostCard), then an optional
5
- * icon badge, a title, a meta line (e.g. "Sundays 10:45 · Main hall"), a description, and
6
- * an optional CTA link. The image-HEADER pattern (distinct from `tiles`, which puts
7
- * the image BEHIND the text): use it for groups, team members, services or events —
8
- * anywhere the photo and the copy both need to be fully legible. Whole card links
9
- * when `href` is set. Token-driven, equal-height.
10
- */
11
- import Icon from '../Icon.astro';
12
- import { isIconKey } from '../../lib/icons';
13
- import Heading from '../blocks/Heading.astro';
14
-
15
- interface Card {
16
- image?: { src: string; alt?: string };
17
- icon?: string;
18
- title: string;
19
- meta?: string;
20
- text?: string;
21
- href?: string;
22
- linkLabel?: string;
23
- }
24
- interface Props {
25
- title?: string;
26
- intro?: string;
27
- columns?: 2 | 3;
28
- cards?: Card[];
29
- }
30
- const { title, intro, columns = 3, cards = [] } = Astro.props;
31
- // Never reserve more columns than there are cards; `data-single` centres a lone one.
32
- const cols = Math.min(columns, Math.max(cards.length, 1)) as 1 | 2 | 3;
33
- ---
34
-
35
- <section class="b-media-cards">
36
- {(title || intro) && (
37
- <div class="b-media-cards__head">
38
- {title && <Heading text={title} level={2} />}
39
- {intro && <p class="b-media-cards__intro">{intro}</p>}
40
- </div>
41
- )}
42
- <div class="b-media-cards__grid" data-cols={cols} data-single={cards.length === 1 ? 'true' : undefined}>
43
- {cards.map((card) => {
44
- // Surface a mailto:/tel: as visible, copyable text — a plain mailto does
45
- // nothing on a device with no mail app, so the address must be readable too.
46
- const email = card.href?.startsWith('mailto:') ? card.href.slice(7).split('?')[0] : null;
47
- const phone = card.href?.startsWith('tel:') ? card.href.slice(4) : null;
48
- return (
49
- <article class:list={['b-media-cards__card', 'ferst-tile', 'ferst-tile--sm', card.href && 'is-link']}>
50
- {card.image && (
51
- <div class="b-media-cards__media">
52
- <img src={card.image.src} alt={card.image.alt ?? ''} loading="lazy" />
53
- </div>
54
- )}
55
- <div class="b-media-cards__body">
56
- {card.icon && isIconKey(card.icon) && (
57
- <span class="b-media-cards__badge" aria-hidden="true"><Icon name={card.icon} size={20} /></span>
58
- )}
59
- <h3 class="b-media-cards__title">
60
- {card.href ? <a href={card.href}>{card.title}</a> : card.title}
61
- </h3>
62
- {card.meta && <p class="b-media-cards__meta">{card.meta}</p>}
63
- {card.text && <p class="b-media-cards__text">{card.text}</p>}
64
- {(email || phone) && (
65
- <span class="b-media-cards__contact">{email ?? phone}</span>
66
- )}
67
- {card.href && !email && !phone && (
68
- <span class="b-media-cards__cta" aria-hidden="true">{card.linkLabel ?? 'Learn more'} →</span>
69
- )}
70
- </div>
71
- </article>
72
- );
73
- })}
74
- </div>
75
- </section>
76
-
77
- <style>
78
- .b-media-cards {
79
- display: flex;
80
- flex-direction: column;
81
- gap: 2rem;
82
- max-width: 72rem;
83
- margin-inline: auto;
84
- padding-inline: 1rem;
85
- }
86
- .b-media-cards__head {
87
- display: flex;
88
- flex-direction: column;
89
- gap: 0.75rem;
90
- }
91
- .b-media-cards__intro {
92
- margin: 0;
93
- color: var(--muted);
94
- font-size: 1.0625rem;
95
- line-height: 1.6;
96
- max-width: var(--max);
97
- }
98
- .b-media-cards__grid {
99
- display: grid;
100
- grid-template-columns: 1fr;
101
- gap: 1.5rem;
102
- }
103
- /* A solitary card is a centred feature, not a full-width slab. */
104
- .b-media-cards__grid[data-single] { max-width: 28rem; margin-inline: auto; }
105
- @media (min-width: 40em) {
106
- .b-media-cards__grid[data-cols='2'] { grid-template-columns: repeat(2, 1fr); }
107
- .b-media-cards__grid[data-cols='3'] { grid-template-columns: repeat(2, 1fr); }
108
- }
109
- @media (min-width: 60em) {
110
- .b-media-cards__grid[data-cols='3'] { grid-template-columns: repeat(3, 1fr); }
111
- }
112
- .b-media-cards__card {
113
- position: relative;
114
- display: flex;
115
- flex-direction: column;
116
- /* surface comes from .ferst-tile .ferst-tile--sm (bg/border/radius/shadow-sm) */
117
- overflow: hidden;
118
- transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
119
- }
120
- .b-media-cards__card.is-link:hover {
121
- transform: translateY(-2px);
122
- box-shadow: var(--shadow-md);
123
- border-color: var(--gold);
124
- }
125
- /* Image header — cover-cropped to a uniform 16:9 so any source ratio fits (the
126
- PostCard technique; also out-specifies the global img{height:auto}). */
127
- .b-media-cards__media {
128
- position: relative;
129
- aspect-ratio: 16 / 9;
130
- overflow: hidden;
131
- background: var(--bg-section);
132
- }
133
- .b-media-cards .b-media-cards__media img {
134
- position: absolute;
135
- inset: 0;
136
- width: 100%;
137
- height: 100%;
138
- object-fit: cover;
139
- display: block;
140
- }
141
- .b-media-cards__body {
142
- display: flex;
143
- flex-direction: column;
144
- gap: 0.5rem;
145
- padding: 1.1rem 1.2rem 1.3rem;
146
- flex: 1;
147
- }
148
- /* Icon badge — a small brand chip; slightly lifted over the image edge when an
149
- image is present (the negative margin only bites inside a card with a header). */
150
- .b-media-cards__badge {
151
- display: inline-flex;
152
- align-items: center;
153
- justify-content: center;
154
- width: 2.25rem;
155
- height: 2.25rem;
156
- border-radius: 999px;
157
- background: var(--gold-light);
158
- color: var(--gold);
159
- margin-top: -2.3rem;
160
- margin-bottom: 0.2rem;
161
- border: 3px solid var(--bg-surface);
162
- }
163
- /* No image → the badge sits normally at the top of the body. */
164
- .b-media-cards__card:not(:has(.b-media-cards__media)) .b-media-cards__badge {
165
- margin-top: 0;
166
- border: none;
167
- }
168
- .b-media-cards__title {
169
- margin: 0;
170
- font-size: 1.2rem;
171
- color: var(--fg);
172
- }
173
- .b-media-cards__card.is-link:hover .b-media-cards__title a { color: var(--gold); }
174
- .b-media-cards__title a { color: var(--fg); }
175
- /* Stretch the title link over the whole card so it's clickable. */
176
- .b-media-cards__title a::after { content: ''; position: absolute; inset: 0; }
177
- .b-media-cards__meta {
178
- margin: 0;
179
- align-self: flex-start;
180
- padding: 0.2rem 0.6rem;
181
- border-radius: 999px;
182
- background: var(--bg-section);
183
- color: var(--muted);
184
- font-family: var(--font-heading);
185
- font-size: 0.72rem;
186
- font-weight: 600;
187
- letter-spacing: 0.03em;
188
- }
189
- .b-media-cards__text {
190
- margin: 0;
191
- color: var(--muted);
192
- line-height: 1.6;
193
- }
194
- .b-media-cards__cta {
195
- margin-top: auto;
196
- padding-top: 0.5rem;
197
- font-family: var(--font-heading);
198
- font-size: 0.85rem;
199
- font-weight: 600;
200
- color: var(--gold);
201
- }
202
- /* A mailto/tel shown as visible, copyable text (below any description). */
203
- .b-media-cards__contact {
204
- margin-top: auto;
205
- padding-top: 0.5rem;
206
- font-family: var(--font-heading);
207
- font-size: 0.85rem;
208
- font-weight: 600;
209
- color: var(--gold);
210
- overflow-wrap: anywhere;
211
- }
212
- </style>
1
+ ---
2
+ /**
3
+ * MediaCards — a Level-2 recipe: a grid of rich content cards with the IMAGE as a
4
+ * header on top (cover-cropped to a uniform ratio, like PostCard), then an optional
5
+ * icon badge, a title, a meta line (e.g. "Sundays 10:45 · Main hall"), a description, and
6
+ * an optional CTA link. The image-HEADER pattern (distinct from `tiles`, which puts
7
+ * the image BEHIND the text): use it for groups, team members, services or events —
8
+ * anywhere the photo and the copy both need to be fully legible. Whole card links
9
+ * when `href` is set. Token-driven, equal-height.
10
+ */
11
+ import Icon from '../Icon.astro';
12
+ import { isIconKey } from '../../lib/icons';
13
+ import Heading from '../blocks/Heading.astro';
14
+
15
+ import { inlineMarkdown, inlineMarkdownNoLinks } from '../../lib/inline-markdown';
16
+ interface Card {
17
+ image?: { src: string; alt?: string };
18
+ icon?: string;
19
+ title: string;
20
+ meta?: string;
21
+ text?: string;
22
+ href?: string;
23
+ linkLabel?: string;
24
+ }
25
+ interface Props {
26
+ title?: string;
27
+ intro?: string;
28
+ columns?: 2 | 3;
29
+ cards?: Card[];
30
+ }
31
+ const { title, intro, columns = 3, cards = [] } = Astro.props;
32
+ // Never reserve more columns than there are cards; `data-single` centres a lone one.
33
+ const cols = Math.min(columns, Math.max(cards.length, 1)) as 1 | 2 | 3;
34
+ ---
35
+
36
+ <section class="b-media-cards">
37
+ {(title || intro) && (
38
+ <div class="b-media-cards__head">
39
+ {title && <Heading text={title} level={2} />}
40
+ {intro && <p class="b-media-cards__intro" set:html={inlineMarkdown(intro)} />}
41
+ </div>
42
+ )}
43
+ <div class="b-media-cards__grid" data-cols={cols} data-single={cards.length === 1 ? 'true' : undefined}>
44
+ {cards.map((card) => {
45
+ // Surface a mailto:/tel: as visible, copyable text — a plain mailto does
46
+ // nothing on a device with no mail app, so the address must be readable too.
47
+ const email = card.href?.startsWith('mailto:') ? card.href.slice(7).split('?')[0] : null;
48
+ const phone = card.href?.startsWith('tel:') ? card.href.slice(4) : null;
49
+ return (
50
+ <article class:list={['b-media-cards__card', 'ferst-tile', 'ferst-tile--sm', card.href && 'is-link']}>
51
+ {card.image && (
52
+ <div class="b-media-cards__media">
53
+ <img src={card.image.src} alt={card.image.alt ?? ''} loading="lazy" />
54
+ </div>
55
+ )}
56
+ <div class="b-media-cards__body">
57
+ {card.icon && isIconKey(card.icon) && (
58
+ <span class="b-media-cards__badge" aria-hidden="true"><Icon name={card.icon} size={20} /></span>
59
+ )}
60
+ <h3 class="b-media-cards__title">
61
+ {card.href ? <a href={card.href}>{card.title}</a> : card.title}
62
+ </h3>
63
+ {card.meta && <p class="b-media-cards__meta">{card.meta}</p>}
64
+ {card.text && <p class="b-media-cards__text" set:html={card.href ? inlineMarkdownNoLinks(card.text) : inlineMarkdown(card.text)} />}
65
+ {(email || phone) && (
66
+ <span class="b-media-cards__contact">{email ?? phone}</span>
67
+ )}
68
+ {card.href && !email && !phone && (
69
+ <span class="b-media-cards__cta" aria-hidden="true">{card.linkLabel ?? 'Learn more'} →</span>
70
+ )}
71
+ </div>
72
+ </article>
73
+ );
74
+ })}
75
+ </div>
76
+ </section>
77
+
78
+ <style>
79
+ .b-media-cards {
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: 2rem;
83
+ max-width: 72rem;
84
+ margin-inline: auto;
85
+ padding-inline: 1rem;
86
+ }
87
+ .b-media-cards__head {
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: 0.75rem;
91
+ }
92
+ .b-media-cards__intro {
93
+ margin: 0;
94
+ color: var(--muted);
95
+ font-size: 1.0625rem;
96
+ line-height: 1.6;
97
+ max-width: var(--max);
98
+ }
99
+ .b-media-cards__grid {
100
+ display: grid;
101
+ grid-template-columns: 1fr;
102
+ gap: 1.5rem;
103
+ }
104
+ /* A solitary card is a centred feature, not a full-width slab. */
105
+ .b-media-cards__grid[data-single] { max-width: 28rem; margin-inline: auto; }
106
+ @media (min-width: 40em) {
107
+ .b-media-cards__grid[data-cols='2'] { grid-template-columns: repeat(2, 1fr); }
108
+ .b-media-cards__grid[data-cols='3'] { grid-template-columns: repeat(2, 1fr); }
109
+ }
110
+ @media (min-width: 60em) {
111
+ .b-media-cards__grid[data-cols='3'] { grid-template-columns: repeat(3, 1fr); }
112
+ }
113
+ .b-media-cards__card {
114
+ position: relative;
115
+ display: flex;
116
+ flex-direction: column;
117
+ /* surface comes from .ferst-tile .ferst-tile--sm (bg/border/radius/shadow-sm) */
118
+ overflow: hidden;
119
+ transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
120
+ }
121
+ .b-media-cards__card.is-link:hover {
122
+ transform: translateY(-2px);
123
+ box-shadow: var(--shadow-md);
124
+ border-color: var(--gold);
125
+ }
126
+ /* Image header — cover-cropped to a uniform 16:9 so any source ratio fits (the
127
+ PostCard technique; also out-specifies the global img{height:auto}). */
128
+ .b-media-cards__media {
129
+ position: relative;
130
+ aspect-ratio: 16 / 9;
131
+ overflow: hidden;
132
+ background: var(--bg-section);
133
+ }
134
+ .b-media-cards .b-media-cards__media img {
135
+ position: absolute;
136
+ inset: 0;
137
+ width: 100%;
138
+ height: 100%;
139
+ object-fit: cover;
140
+ display: block;
141
+ }
142
+ .b-media-cards__body {
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: 0.5rem;
146
+ padding: 1.1rem 1.2rem 1.3rem;
147
+ flex: 1;
148
+ }
149
+ /* Icon badge — a small brand chip; slightly lifted over the image edge when an
150
+ image is present (the negative margin only bites inside a card with a header). */
151
+ .b-media-cards__badge {
152
+ display: inline-flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ width: 2.25rem;
156
+ height: 2.25rem;
157
+ border-radius: 999px;
158
+ background: var(--gold-light);
159
+ color: var(--gold);
160
+ margin-top: -2.3rem;
161
+ margin-bottom: 0.2rem;
162
+ border: 3px solid var(--bg-surface);
163
+ }
164
+ /* No image → the badge sits normally at the top of the body. */
165
+ .b-media-cards__card:not(:has(.b-media-cards__media)) .b-media-cards__badge {
166
+ margin-top: 0;
167
+ border: none;
168
+ }
169
+ .b-media-cards__title {
170
+ margin: 0;
171
+ font-size: 1.2rem;
172
+ color: var(--fg);
173
+ }
174
+ .b-media-cards__card.is-link:hover .b-media-cards__title a { color: var(--gold); }
175
+ .b-media-cards__title a { color: var(--fg); }
176
+ /* Stretch the title link over the whole card so it's clickable. */
177
+ .b-media-cards__title a::after { content: ''; position: absolute; inset: 0; }
178
+ .b-media-cards__meta {
179
+ margin: 0;
180
+ align-self: flex-start;
181
+ padding: 0.2rem 0.6rem;
182
+ border-radius: 999px;
183
+ background: var(--bg-section);
184
+ color: var(--muted);
185
+ font-family: var(--font-heading);
186
+ font-size: 0.72rem;
187
+ font-weight: 600;
188
+ letter-spacing: 0.03em;
189
+ }
190
+ .b-media-cards__text {
191
+ margin: 0;
192
+ color: var(--muted);
193
+ line-height: 1.6;
194
+ }
195
+ .b-media-cards__cta {
196
+ margin-top: auto;
197
+ padding-top: 0.5rem;
198
+ font-family: var(--font-heading);
199
+ font-size: 0.85rem;
200
+ font-weight: 600;
201
+ color: var(--gold);
202
+ }
203
+ /* A mailto/tel shown as visible, copyable text (below any description). */
204
+ .b-media-cards__contact {
205
+ margin-top: auto;
206
+ padding-top: 0.5rem;
207
+ font-family: var(--font-heading);
208
+ font-size: 0.85rem;
209
+ font-weight: 600;
210
+ color: var(--gold);
211
+ overflow-wrap: anywhere;
212
+ }
213
+ </style>
@@ -1,89 +1,90 @@
1
- ---
2
- /**
3
- * Notice — a Level-2 recipe: an inline highlighted info box (optional icon +
4
- * title + body + optional link) that sets a passage apart from the surrounding
5
- * prose — a "reporting a concern", "please note", "how to book" callout. Two
6
- * tones (accent / neutral) in keeping with badge/announcement; token-driven, so
7
- * it follows the brand and both themes. The page-level sibling of the corner
8
- * StickyCallout (this one sits in the content flow).
9
- */
10
- import Icon from '../Icon.astro';
11
- import { isIconKey } from '../../lib/icons';
12
-
13
- interface Props {
14
- tone?: 'accent' | 'neutral';
15
- icon?: string;
16
- title?: string;
17
- text: string;
18
- link?: { label: string; href: string };
19
- }
20
- const { tone = 'accent', icon, title, text, link } = Astro.props;
21
- const paras = text.split(/\n\s*\n/).map((p) => p.trim()).filter(Boolean);
22
- ---
23
-
24
- <aside class="b-notice" data-tone={tone}>
25
- {icon && isIconKey(icon) && (
26
- <span class="b-notice__icon" aria-hidden="true"><Icon name={icon} size={22} /></span>
27
- )}
28
- <div class="b-notice__body">
29
- {title && <p class="b-notice__title">{title}</p>}
30
- {paras.map((p) => <p class="b-notice__text">{p}</p>)}
31
- {link && <a class="b-notice__link" href={link.href}>{link.label}</a>}
32
- </div>
33
- </aside>
34
-
35
- <style>
36
- .b-notice {
37
- display: flex;
38
- gap: 0.85rem;
39
- align-items: flex-start;
40
- max-width: var(--max);
41
- margin-inline: auto;
42
- padding: 1.1rem 1.25rem;
43
- border-radius: var(--radius);
44
- border-inline-start: 4px solid var(--gold);
45
- }
46
- .b-notice[data-tone='accent'] {
47
- background: var(--gold-light);
48
- border-inline-start-color: var(--gold);
49
- }
50
- .b-notice[data-tone='neutral'] {
51
- background: var(--bg-section);
52
- border-inline-start-color: var(--accent);
53
- }
54
- /* On a centred page, keep the box a readable width but centre it as a block;
55
- text inside stays left (a centred info box reads badly). */
56
- :global([data-content-align='center']) .b-notice {
57
- text-align: start;
58
- }
59
- .b-notice__icon {
60
- display: inline-flex;
61
- color: var(--gold);
62
- margin-top: 0.1rem;
63
- flex-shrink: 0;
64
- }
65
- .b-notice__body {
66
- display: flex;
67
- flex-direction: column;
68
- gap: 0.4rem;
69
- min-width: 0;
70
- }
71
- .b-notice__title {
72
- margin: 0;
73
- font-family: var(--font-heading);
74
- font-weight: 600;
75
- font-size: 1.05rem;
76
- color: var(--fg);
77
- }
78
- .b-notice__text {
79
- margin: 0;
80
- color: var(--muted);
81
- line-height: 1.6;
82
- }
83
- .b-notice__link {
84
- margin-top: 0.1rem;
85
- align-self: flex-start;
86
- color: var(--gold);
87
- font-weight: 600;
88
- }
89
- </style>
1
+ ---
2
+ /**
3
+ * Notice — a Level-2 recipe: an inline highlighted info box (optional icon +
4
+ * title + body + optional link) that sets a passage apart from the surrounding
5
+ * prose — a "reporting a concern", "please note", "how to book" callout. Two
6
+ * tones (accent / neutral) in keeping with badge/announcement; token-driven, so
7
+ * it follows the brand and both themes. The page-level sibling of the corner
8
+ * StickyCallout (this one sits in the content flow).
9
+ */
10
+ import Icon from '../Icon.astro';
11
+ import { isIconKey } from '../../lib/icons';
12
+
13
+ import { inlineMarkdown } from '../../lib/inline-markdown';
14
+ interface Props {
15
+ tone?: 'accent' | 'neutral';
16
+ icon?: string;
17
+ title?: string;
18
+ text: string;
19
+ link?: { label: string; href: string };
20
+ }
21
+ const { tone = 'accent', icon, title, text, link } = Astro.props;
22
+ const paras = text.split(/\n\s*\n/).map((p) => p.trim()).filter(Boolean);
23
+ ---
24
+
25
+ <aside class="b-notice" data-tone={tone}>
26
+ {icon && isIconKey(icon) && (
27
+ <span class="b-notice__icon" aria-hidden="true"><Icon name={icon} size={22} /></span>
28
+ )}
29
+ <div class="b-notice__body">
30
+ {title && <p class="b-notice__title">{title}</p>}
31
+ {paras.map((p) => <p class="b-notice__text" set:html={inlineMarkdown(p)} />)}
32
+ {link && <a class="b-notice__link" href={link.href}>{link.label}</a>}
33
+ </div>
34
+ </aside>
35
+
36
+ <style>
37
+ .b-notice {
38
+ display: flex;
39
+ gap: 0.85rem;
40
+ align-items: flex-start;
41
+ max-width: var(--max);
42
+ margin-inline: auto;
43
+ padding: 1.1rem 1.25rem;
44
+ border-radius: var(--radius);
45
+ border-inline-start: 4px solid var(--gold);
46
+ }
47
+ .b-notice[data-tone='accent'] {
48
+ background: var(--gold-light);
49
+ border-inline-start-color: var(--gold);
50
+ }
51
+ .b-notice[data-tone='neutral'] {
52
+ background: var(--bg-section);
53
+ border-inline-start-color: var(--accent);
54
+ }
55
+ /* On a centred page, keep the box a readable width but centre it as a block;
56
+ text inside stays left (a centred info box reads badly). */
57
+ :global([data-content-align='center']) .b-notice {
58
+ text-align: start;
59
+ }
60
+ .b-notice__icon {
61
+ display: inline-flex;
62
+ color: var(--gold);
63
+ margin-top: 0.1rem;
64
+ flex-shrink: 0;
65
+ }
66
+ .b-notice__body {
67
+ display: flex;
68
+ flex-direction: column;
69
+ gap: 0.4rem;
70
+ min-width: 0;
71
+ }
72
+ .b-notice__title {
73
+ margin: 0;
74
+ font-family: var(--font-heading);
75
+ font-weight: 600;
76
+ font-size: 1.05rem;
77
+ color: var(--fg);
78
+ }
79
+ .b-notice__text {
80
+ margin: 0;
81
+ color: var(--muted);
82
+ line-height: 1.6;
83
+ }
84
+ .b-notice__link {
85
+ margin-top: 0.1rem;
86
+ align-self: flex-start;
87
+ color: var(--gold);
88
+ font-weight: 600;
89
+ }
90
+ </style>