ferst-core 0.7.2 → 0.7.5

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 (47) hide show
  1. package/bin/ferst-update.mjs +33 -2
  2. package/cms/config.template.yml +214 -206
  3. package/components/BlockRenderer.astro +10 -0
  4. package/components/Pagination.astro +51 -0
  5. package/components/SiteFooter.astro +274 -270
  6. package/components/SiteHeader.astro +1243 -1183
  7. package/components/blocks/Heading.astro +51 -46
  8. package/components/blocks/Prose.astro +10 -2
  9. package/components/blocks/Quote.astro +40 -38
  10. package/components/recipes/Accordion.astro +91 -84
  11. package/components/recipes/Contacts.astro +139 -0
  12. package/components/recipes/Document.astro +157 -0
  13. package/components/recipes/FeatureCards.astro +112 -111
  14. package/components/recipes/Hero.astro +52 -4
  15. package/components/recipes/LinkRow.astro +91 -0
  16. package/components/recipes/MediaCards.astro +213 -212
  17. package/components/recipes/MediaText.astro +92 -0
  18. package/components/recipes/Notice.astro +90 -89
  19. package/components/recipes/Stats.astro +152 -145
  20. package/components/recipes/Table.astro +101 -97
  21. package/components/recipes/Tabs.astro +134 -133
  22. package/components/recipes/Tiles.astro +4 -2
  23. package/components/recipes/Timetable.astro +189 -0
  24. package/components/recipes/Video.astro +177 -157
  25. package/content/blocks.ts +113 -1
  26. package/content/cookies.ts +10 -0
  27. package/content/schemas.ts +167 -161
  28. package/examples/primitives.ts +2 -0
  29. package/examples/recipes.ts +7 -0
  30. package/integration.mjs +83 -64
  31. package/layouts/Base.astro +483 -475
  32. package/lib/documents.ts +68 -62
  33. package/lib/embed-consent.ts +78 -73
  34. package/lib/inline-markdown.ts +6 -0
  35. package/lib/links.ts +22 -0
  36. package/lib/paginate.ts +23 -0
  37. package/lib/posts.ts +20 -2
  38. package/lib/siteSettings.ts +61 -60
  39. package/lib/slug.ts +32 -0
  40. package/lib/table.ts +10 -0
  41. package/lib/timetable.ts +27 -0
  42. package/lib/video.ts +101 -75
  43. package/package.json +1 -1
  44. package/routes/cookie-policy.astro +27 -0
  45. package/routes/posts/index.astro +36 -31
  46. package/routes/posts/page/[page].astro +36 -0
  47. package/routes/posts/tag/[...tag].astro +41 -37
@@ -1,270 +1,274 @@
1
- ---
2
- /**
3
- * Site-wide footer: site name, a one-line description, a Facebook /
4
- * Contact link row, then a legal-nav + copyright strip. Extracted from
5
- * Base.astro so changing footer links is a JSON edit, not a layout edit.
6
- *
7
- * Split masthead at 48em+ (768px, matching --bp-tablet): name/description
8
- * left, Facebook/Contact right, one row — below that it's a centred stack.
9
- * Fills the page's width deliberately on desktop instead of sitting as a
10
- * small centred island inside a full-bleed band.
11
- *
12
- * Follows the page's light/dark theme like every other component. Colour
13
- * rule throughout: accent links/labels are `--accent` in light mode and
14
- * `--gold` in dark mode, never the reverse — `--gold` measures only
15
- * ~1.75:1 against the light-mode footer background (fails WCAG AA badly),
16
- * it only reads well on the dark background. Checked with the WCAG 2.1
17
- * relative-luminance formula before writing the CSS below, not after.
18
- */
19
- import type { NavLink } from '../content/schemas';
20
-
21
- interface Props {
22
- facebookUrl: string;
23
- footerLinks: NavLink[];
24
- footerClass?: string;
25
- /** Brand / site name shown in the footer masthead. */
26
- shortName: string;
27
- /** Optional one-line tagline under the brand. Client-provided; omitted if blank. */
28
- tagline?: string;
29
- }
30
-
31
- const { facebookUrl, footerLinks, footerClass, shortName, tagline } = Astro.props;
32
- const year = new Date().getFullYear();
33
- ---
34
-
35
- <footer class:list={['site-footer', footerClass]}>
36
- <div class="footer-top">
37
- <div class="footer-row">
38
- <div class="footer-copyblock">
39
- <p class="footer-brand">{shortName}</p>
40
- {tagline && <p class="footer-blurb">{tagline}</p>}
41
- </div>
42
- <div class="footer-social">
43
- {facebookUrl && (
44
- <>
45
- <a class="footer-social__link" href={facebookUrl} target="_blank" rel="noopener noreferrer">
46
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
47
- <path d="M22 12c0-5.522-4.477-10-10-10S2 6.478 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987H7.898V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z"></path>
48
- </svg>
49
- Facebook
50
- </a>
51
- <span class="footer-social__divider" aria-hidden="true"></span>
52
- </>
53
- )}
54
- <a class="footer-social__link" href="/contact/">
55
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
56
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
57
- </svg>
58
- Contact
59
- </a>
60
- </div>
61
- </div>
62
- </div>
63
-
64
- <div class="footer-bottom">
65
- <nav class="footer-nav" aria-label="Footer navigation">
66
- {footerLinks.map((link) => (
67
- <a href={link.href} set:html={link.label} />
68
- ))}
69
- <button type="button" id="footer-cookie-preferences">Cookie preferences</button>
70
- </nav>
71
- <p class="footer-copy">&copy; {year} All rights reserved.</p>
72
- </div>
73
- </footer>
74
-
75
- <script>
76
- document.getElementById('footer-cookie-preferences')?.addEventListener('click', () => {
77
- document.dispatchEvent(new CustomEvent('open-cookie-preferences'));
78
- });
79
- </script>
80
-
81
- <style is:global>
82
- /*
83
- * Every rule below is qualified under `.site-footer` on purpose, even
84
- * where a bare class would normally do. Base.astro's global stylesheet
85
- * carries `[data-theme="dark"] p { color: #C8D4C2 }` and
86
- * `[data-theme="dark"] a { color: #E8A84A }` — both specificity (0,1,1).
87
- * A bare `.footer-brand { color: ... }` is only (0,1,0) and loses that
88
- * fight in dark mode. Prefixing with `.site-footer` (0,2,0/1) wins
89
- * outright instead of depending on stylesheet order.
90
- */
91
- .site-footer {
92
- font-size: 0.85rem;
93
- background: var(--bg-tag);
94
- color: var(--fg);
95
- }
96
- [data-theme="dark"] .site-footer {
97
- background: var(--dark-bg);
98
- color: var(--text-light);
99
- }
100
- .site-footer a {
101
- text-decoration: none;
102
- }
103
- .site-footer a:focus-visible,
104
- .site-footer button:focus-visible {
105
- outline: 2px solid var(--gold);
106
- outline-offset: 2px;
107
- border-radius: 4px;
108
- }
109
-
110
- /* ── Top: title, description, social row ──
111
- Below 48em (768px): centred stack, same as before. At 768px+: a split
112
- masthead — name/description left, Facebook/Contact right — so the
113
- top section uses the page's width instead of sitting as a small
114
- centred island inside it. */
115
- .site-footer .footer-top {
116
- max-width: 72rem;
117
- margin-inline: auto;
118
- padding: clamp(1.75rem, 4vw, 2.5rem) 1.25rem clamp(1.5rem, 3vw, 2rem);
119
- }
120
- .site-footer .footer-row {
121
- display: flex;
122
- flex-direction: column;
123
- align-items: center;
124
- gap: 0.5rem;
125
- text-align: center;
126
- }
127
- .site-footer .footer-copyblock {
128
- display: flex;
129
- flex-direction: column;
130
- gap: 0.5rem;
131
- }
132
- .site-footer .footer-brand {
133
- margin: 0;
134
- font-family: var(--font-heading);
135
- font-weight: 700;
136
- font-size: 1.15rem;
137
- line-height: 1.3;
138
- color: inherit;
139
- }
140
- .site-footer .footer-blurb {
141
- margin: 0;
142
- font-size: 0.85rem;
143
- font-weight: 400;
144
- line-height: 1.5;
145
- color: var(--muted);
146
- }
147
- .site-footer .footer-social {
148
- display: flex;
149
- align-items: center;
150
- gap: 1.25rem;
151
- margin-top: 0.35rem;
152
- }
153
- @media (min-width: 48em) {
154
- .site-footer .footer-row {
155
- flex-direction: row;
156
- justify-content: space-between;
157
- text-align: left;
158
- }
159
- .site-footer .footer-social {
160
- margin-top: 0;
161
- }
162
- }
163
- .site-footer .footer-social__link {
164
- display: inline-flex;
165
- align-items: center;
166
- gap: 0.4rem;
167
- min-height: 44px;
168
- font-size: 0.92rem;
169
- font-weight: 600;
170
- color: var(--accent);
171
- }
172
- [data-theme="dark"] .site-footer .footer-social__link {
173
- color: var(--gold);
174
- }
175
- .site-footer .footer-social__divider {
176
- width: 1px;
177
- height: 1.4rem;
178
- background: var(--border);
179
- }
180
-
181
- /* ── Legal nav + copyright ── */
182
- .site-footer .footer-bottom {
183
- border-top: 1px solid var(--border);
184
- padding: 1.1rem clamp(1.25rem, 4vw, 2.25rem);
185
- max-width: 72rem;
186
- margin-inline: auto;
187
- display: flex;
188
- flex-direction: column;
189
- align-items: center;
190
- text-align: center;
191
- gap: 0.6rem 1.5rem;
192
- }
193
- /* Tight gap: each link carries its own inline padding (below), so the visible
194
- hit target is a clear region *centred on its label* — no wide dead gap between
195
- labels where a hover lands on nothing (and the browser keeps showing the
196
- previous link's URL, which reads as an off-by-one). The negative inline margin
197
- cancels the first/last item's padding so the row still edge-aligns. */
198
- .site-footer .footer-nav {
199
- display: flex;
200
- flex-wrap: wrap;
201
- justify-content: center;
202
- gap: 0.15rem 0.15rem;
203
- margin-inline: -0.55rem;
204
- }
205
- .site-footer .footer-nav a,
206
- .site-footer .footer-nav button {
207
- min-height: 44px;
208
- display: inline-flex;
209
- align-items: center;
210
- font-family: inherit;
211
- font-size: 0.68rem;
212
- font-weight: 400;
213
- letter-spacing: 0.02em;
214
- color: var(--accent);
215
- background: none;
216
- border: none;
217
- border-radius: var(--radius-sm, 6px);
218
- padding: 0 0.55rem;
219
- cursor: pointer;
220
- text-decoration: none;
221
- transition: background 0.15s ease, color 0.15s ease;
222
- }
223
- /* Visible hover/focus affordance: makes each link's boundary unmistakable, so
224
- it's always clear which one you're on. */
225
- .site-footer .footer-nav a:hover,
226
- .site-footer .footer-nav button:hover,
227
- .site-footer .footer-nav a:focus-visible,
228
- .site-footer .footer-nav button:focus-visible {
229
- background: color-mix(in srgb, var(--accent) 12%, transparent);
230
- text-decoration: underline;
231
- text-underline-offset: 3px;
232
- }
233
- [data-theme="dark"] .site-footer .footer-nav a,
234
- [data-theme="dark"] .site-footer .footer-nav button {
235
- color: var(--gold);
236
- }
237
- [data-theme="dark"] .site-footer .footer-nav a:hover,
238
- [data-theme="dark"] .site-footer .footer-nav button:hover,
239
- [data-theme="dark"] .site-footer .footer-nav a:focus-visible,
240
- [data-theme="dark"] .site-footer .footer-nav button:focus-visible {
241
- background: color-mix(in srgb, var(--gold) 16%, transparent);
242
- }
243
- .site-footer .footer-copy {
244
- margin: 0;
245
- font-family: var(--font-body);
246
- font-size: 0.68rem;
247
- font-weight: 400;
248
- color: var(--muted);
249
- }
250
-
251
- /* 48em / 768px, matching --bp-tablet in theme.css — the bottom bar's
252
- row layout (legal nav | copyright, space-between) gets cramped once
253
- the nav wraps below it, so it stacks and centres instead. */
254
- @media (min-width: 48em) {
255
- .site-footer .footer-bottom {
256
- flex-direction: row;
257
- justify-content: space-between;
258
- text-align: left;
259
- }
260
- .site-footer .footer-nav {
261
- justify-content: flex-start;
262
- }
263
- }
264
-
265
- @media (max-width: 22.5em) {
266
- .site-footer .footer-social {
267
- gap: 1rem;
268
- }
269
- }
270
- </style>
1
+ ---
2
+ /**
3
+ * Site-wide footer: site name, a one-line description, a Facebook /
4
+ * Contact link row, then a legal-nav + copyright strip. Extracted from
5
+ * Base.astro so changing footer links is a JSON edit, not a layout edit.
6
+ *
7
+ * Split masthead at 48em+ (768px, matching --bp-tablet): name/description
8
+ * left, Facebook/Contact right, one row — below that it's a centred stack.
9
+ * Fills the page's width deliberately on desktop instead of sitting as a
10
+ * small centred island inside a full-bleed band.
11
+ *
12
+ * Follows the page's light/dark theme like every other component. Colour
13
+ * rule throughout: accent links/labels are `--accent` in light mode and
14
+ * `--gold` in dark mode, never the reverse — `--gold` measures only
15
+ * ~1.75:1 against the light-mode footer background (fails WCAG AA badly),
16
+ * it only reads well on the dark background. Checked with the WCAG 2.1
17
+ * relative-luminance formula before writing the CSS below, not after.
18
+ */
19
+ import type { NavLink } from '../content/schemas';
20
+
21
+ interface Props {
22
+ facebookUrl: string;
23
+ footerLinks: NavLink[];
24
+ footerClass?: string;
25
+ /** Brand / site name shown in the footer masthead. */
26
+ shortName: string;
27
+ /** Optional one-line tagline under the brand. Client-provided; omitted if blank. */
28
+ tagline?: string;
29
+ /** The site's call-to-action (navbarSettings.cta), shown beside Facebook; null hides it. */
30
+ cta?: { label: string; href: string } | null;
31
+ }
32
+
33
+ const { facebookUrl, footerLinks, footerClass, shortName, tagline, cta = { label: 'Contact', href: '/contact/' } } = Astro.props;
34
+ const year = new Date().getFullYear();
35
+ ---
36
+
37
+ <footer class:list={['site-footer', footerClass]}>
38
+ <div class="footer-top">
39
+ <div class="footer-row">
40
+ <div class="footer-copyblock">
41
+ <p class="footer-brand">{shortName}</p>
42
+ {tagline && <p class="footer-blurb">{tagline}</p>}
43
+ </div>
44
+ <div class="footer-social">
45
+ {facebookUrl && (
46
+ <>
47
+ <a class="footer-social__link" href={facebookUrl} target="_blank" rel="noopener noreferrer">
48
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
49
+ <path d="M22 12c0-5.522-4.477-10-10-10S2 6.478 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987H7.898V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z"></path>
50
+ </svg>
51
+ Facebook
52
+ </a>
53
+ {cta && <span class="footer-social__divider" aria-hidden="true"></span>}
54
+ </>
55
+ )}
56
+ {cta && (
57
+ <a class="footer-social__link" href={cta.href}>
58
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
59
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
60
+ </svg>
61
+ {cta.label}
62
+ </a>
63
+ )}
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <div class="footer-bottom">
69
+ <nav class="footer-nav" aria-label="Footer navigation">
70
+ {footerLinks.map((link) => (
71
+ <a href={link.href} set:html={link.label} />
72
+ ))}
73
+ <button type="button" id="footer-cookie-preferences">Cookie preferences</button>
74
+ </nav>
75
+ <p class="footer-copy">&copy; {year} All rights reserved.</p>
76
+ </div>
77
+ </footer>
78
+
79
+ <script>
80
+ document.getElementById('footer-cookie-preferences')?.addEventListener('click', () => {
81
+ document.dispatchEvent(new CustomEvent('open-cookie-preferences'));
82
+ });
83
+ </script>
84
+
85
+ <style is:global>
86
+ /*
87
+ * Every rule below is qualified under `.site-footer` on purpose, even
88
+ * where a bare class would normally do. Base.astro's global stylesheet
89
+ * carries `[data-theme="dark"] p { color: #C8D4C2 }` and
90
+ * `[data-theme="dark"] a { color: #E8A84A }` — both specificity (0,1,1).
91
+ * A bare `.footer-brand { color: ... }` is only (0,1,0) and loses that
92
+ * fight in dark mode. Prefixing with `.site-footer` (0,2,0/1) wins
93
+ * outright instead of depending on stylesheet order.
94
+ */
95
+ .site-footer {
96
+ font-size: 0.85rem;
97
+ background: var(--bg-tag);
98
+ color: var(--fg);
99
+ }
100
+ [data-theme="dark"] .site-footer {
101
+ background: var(--dark-bg);
102
+ color: var(--text-light);
103
+ }
104
+ .site-footer a {
105
+ text-decoration: none;
106
+ }
107
+ .site-footer a:focus-visible,
108
+ .site-footer button:focus-visible {
109
+ outline: 2px solid var(--gold);
110
+ outline-offset: 2px;
111
+ border-radius: 4px;
112
+ }
113
+
114
+ /* ── Top: title, description, social row ──
115
+ Below 48em (768px): centred stack, same as before. At 768px+: a split
116
+ masthead — name/description left, Facebook/Contact right — so the
117
+ top section uses the page's width instead of sitting as a small
118
+ centred island inside it. */
119
+ .site-footer .footer-top {
120
+ max-width: 72rem;
121
+ margin-inline: auto;
122
+ padding: clamp(1.75rem, 4vw, 2.5rem) 1.25rem clamp(1.5rem, 3vw, 2rem);
123
+ }
124
+ .site-footer .footer-row {
125
+ display: flex;
126
+ flex-direction: column;
127
+ align-items: center;
128
+ gap: 0.5rem;
129
+ text-align: center;
130
+ }
131
+ .site-footer .footer-copyblock {
132
+ display: flex;
133
+ flex-direction: column;
134
+ gap: 0.5rem;
135
+ }
136
+ .site-footer .footer-brand {
137
+ margin: 0;
138
+ font-family: var(--font-heading);
139
+ font-weight: 700;
140
+ font-size: 1.15rem;
141
+ line-height: 1.3;
142
+ color: inherit;
143
+ }
144
+ .site-footer .footer-blurb {
145
+ margin: 0;
146
+ font-size: 0.85rem;
147
+ font-weight: 400;
148
+ line-height: 1.5;
149
+ color: var(--muted);
150
+ }
151
+ .site-footer .footer-social {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 1.25rem;
155
+ margin-top: 0.35rem;
156
+ }
157
+ @media (min-width: 48em) {
158
+ .site-footer .footer-row {
159
+ flex-direction: row;
160
+ justify-content: space-between;
161
+ text-align: left;
162
+ }
163
+ .site-footer .footer-social {
164
+ margin-top: 0;
165
+ }
166
+ }
167
+ .site-footer .footer-social__link {
168
+ display: inline-flex;
169
+ align-items: center;
170
+ gap: 0.4rem;
171
+ min-height: 44px;
172
+ font-size: 0.92rem;
173
+ font-weight: 600;
174
+ color: var(--accent);
175
+ }
176
+ [data-theme="dark"] .site-footer .footer-social__link {
177
+ color: var(--gold);
178
+ }
179
+ .site-footer .footer-social__divider {
180
+ width: 1px;
181
+ height: 1.4rem;
182
+ background: var(--border);
183
+ }
184
+
185
+ /* ── Legal nav + copyright ── */
186
+ .site-footer .footer-bottom {
187
+ border-top: 1px solid var(--border);
188
+ padding: 1.1rem clamp(1.25rem, 4vw, 2.25rem);
189
+ max-width: 72rem;
190
+ margin-inline: auto;
191
+ display: flex;
192
+ flex-direction: column;
193
+ align-items: center;
194
+ text-align: center;
195
+ gap: 0.6rem 1.5rem;
196
+ }
197
+ /* Tight gap: each link carries its own inline padding (below), so the visible
198
+ hit target is a clear region *centred on its label* — no wide dead gap between
199
+ labels where a hover lands on nothing (and the browser keeps showing the
200
+ previous link's URL, which reads as an off-by-one). The negative inline margin
201
+ cancels the first/last item's padding so the row still edge-aligns. */
202
+ .site-footer .footer-nav {
203
+ display: flex;
204
+ flex-wrap: wrap;
205
+ justify-content: center;
206
+ gap: 0.15rem 0.15rem;
207
+ margin-inline: -0.55rem;
208
+ }
209
+ .site-footer .footer-nav a,
210
+ .site-footer .footer-nav button {
211
+ min-height: 44px;
212
+ display: inline-flex;
213
+ align-items: center;
214
+ font-family: inherit;
215
+ font-size: 0.68rem;
216
+ font-weight: 400;
217
+ letter-spacing: 0.02em;
218
+ color: var(--accent);
219
+ background: none;
220
+ border: none;
221
+ border-radius: var(--radius-sm, 6px);
222
+ padding: 0 0.55rem;
223
+ cursor: pointer;
224
+ text-decoration: none;
225
+ transition: background 0.15s ease, color 0.15s ease;
226
+ }
227
+ /* Visible hover/focus affordance: makes each link's boundary unmistakable, so
228
+ it's always clear which one you're on. */
229
+ .site-footer .footer-nav a:hover,
230
+ .site-footer .footer-nav button:hover,
231
+ .site-footer .footer-nav a:focus-visible,
232
+ .site-footer .footer-nav button:focus-visible {
233
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
234
+ text-decoration: underline;
235
+ text-underline-offset: 3px;
236
+ }
237
+ [data-theme="dark"] .site-footer .footer-nav a,
238
+ [data-theme="dark"] .site-footer .footer-nav button {
239
+ color: var(--gold);
240
+ }
241
+ [data-theme="dark"] .site-footer .footer-nav a:hover,
242
+ [data-theme="dark"] .site-footer .footer-nav button:hover,
243
+ [data-theme="dark"] .site-footer .footer-nav a:focus-visible,
244
+ [data-theme="dark"] .site-footer .footer-nav button:focus-visible {
245
+ background: color-mix(in srgb, var(--gold) 16%, transparent);
246
+ }
247
+ .site-footer .footer-copy {
248
+ margin: 0;
249
+ font-family: var(--font-body);
250
+ font-size: 0.68rem;
251
+ font-weight: 400;
252
+ color: var(--muted);
253
+ }
254
+
255
+ /* 48em / 768px, matching --bp-tablet in theme.css — the bottom bar's
256
+ row layout (legal nav | copyright, space-between) gets cramped once
257
+ the nav wraps below it, so it stacks and centres instead. */
258
+ @media (min-width: 48em) {
259
+ .site-footer .footer-bottom {
260
+ flex-direction: row;
261
+ justify-content: space-between;
262
+ text-align: left;
263
+ }
264
+ .site-footer .footer-nav {
265
+ justify-content: flex-start;
266
+ }
267
+ }
268
+
269
+ @media (max-width: 22.5em) {
270
+ .site-footer .footer-social {
271
+ gap: 1rem;
272
+ }
273
+ }
274
+ </style>