@rathnasgala/theme 2.0.1 → 2.0.3
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.
- package/package.json +1 -1
- package/payload/.gala/managed-files.json +22 -20
- package/payload/lib/accent.js +105 -0
- package/payload/lib/render-markdown.js +1 -1
- package/payload/lib/seo.js +17 -3
- package/payload/lib/site-config.js +62 -0
- package/payload/src/_data/site.js +16 -0
- package/payload/src/_includes/components/ui.njk +36 -24
- package/payload/src/_includes/layouts/base.njk +23 -15
- package/payload/src/_includes/layouts/post.njk +10 -5
- package/payload/src/assets/engagement-comments.js +19 -9
- package/payload/src/assets/interactions.js +113 -41
- package/payload/src/assets/reader.js +2 -2
- package/payload/src/assets/theme.css +1 -1
- package/payload/src/posts.11ty.js +6 -0
- package/payload/src/styles/theme.css +295 -48
- package/payload/static/favicon.ico +0 -0
|
@@ -22,8 +22,9 @@ function threads(items) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function commentsController(root, endpoint) {
|
|
25
|
-
const state = { items: [], cursor: null, total: 0, busy: false, status: '',
|
|
25
|
+
const state = { items: [], cursor: null, total: 0, busy: false, status: '', phase: 'loading' };
|
|
26
26
|
const articleId = /\/v1\/articles\/([^/]+)\/engagement/.exec(endpoint)?.[1] ?? '';
|
|
27
|
+
const statusNode = root.closest('.gala-conversation')?.querySelector('[data-engagement-status]');
|
|
27
28
|
|
|
28
29
|
async function load(nextCursor = '', { append = false, fresh = false } = {}) {
|
|
29
30
|
const url = new URL(endpoint);
|
|
@@ -110,23 +111,31 @@ function commentsController(root, endpoint) {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
function render() {
|
|
114
|
+
if (statusNode) {
|
|
115
|
+
statusNode.textContent = state.phase === 'loading'
|
|
116
|
+
? 'Loading comments…'
|
|
117
|
+
: state.phase === 'unavailable' ? 'Comments are temporarily unavailable.' : state.status;
|
|
118
|
+
}
|
|
119
|
+
if (state.phase !== 'ready') {
|
|
120
|
+
root.replaceChildren();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
113
123
|
const section = element('section', 'gala-comments-island'); section.setAttribute('aria-label', 'Comments');
|
|
114
|
-
section.append(element('
|
|
124
|
+
section.append(element('p', 'gala-comments__heading', state.total === 1 ? '1 comment' : `${state.total} comments`));
|
|
115
125
|
if (sessionUser.value) section.append(form('Add a comment', '', (body) => write('comment.create', { articleId, body })));
|
|
116
126
|
else {
|
|
117
127
|
const prompt = element('p', 'gala-comments__prompt');
|
|
118
128
|
const signIn = element('button', '', 'Sign in to join the conversation'); signIn.type = 'button';
|
|
119
129
|
signIn.addEventListener('click', () => requestSignIn({ kind: 'comment' })); prompt.append(signIn); section.append(prompt);
|
|
120
130
|
}
|
|
121
|
-
if (state.
|
|
122
|
-
if (state.loading) section.append(element('p', 'gala-comments__status', 'Loading comments…'));
|
|
123
|
-
else if (!state.items.length) section.append(element('p', 'gala-comments__empty', 'No comments yet.'));
|
|
131
|
+
if (!state.items.length) section.append(element('p', 'gala-comments__empty', 'No comments yet.'));
|
|
124
132
|
else { const list = element('ol', 'gala-comments'); list.append(...threads(state.items).map(commentNode)); section.append(list); }
|
|
125
133
|
if (state.cursor) {
|
|
126
134
|
const more = element('button', 'gala-comments__more', 'Show more comments'); more.type = 'button'; more.disabled = state.busy;
|
|
127
135
|
more.addEventListener('click', async () => {
|
|
128
|
-
state.busy = true; render();
|
|
129
|
-
try { await load(state.cursor, { append: true });
|
|
136
|
+
state.busy = true; state.status = 'Loading more comments…'; render();
|
|
137
|
+
try { await load(state.cursor, { append: true }); state.status = ''; }
|
|
138
|
+
catch { state.status = 'More comments couldn’t be loaded. Try again.'; }
|
|
130
139
|
state.busy = false; render();
|
|
131
140
|
}); section.append(more);
|
|
132
141
|
}
|
|
@@ -135,8 +144,9 @@ function commentsController(root, endpoint) {
|
|
|
135
144
|
|
|
136
145
|
window.addEventListener('gala-session-change', render);
|
|
137
146
|
render();
|
|
138
|
-
load().
|
|
139
|
-
.
|
|
147
|
+
load().then(() => { state.phase = 'ready'; })
|
|
148
|
+
.catch(() => { state.phase = 'unavailable'; })
|
|
149
|
+
.finally(render);
|
|
140
150
|
}
|
|
141
151
|
|
|
142
152
|
document.querySelectorAll('[data-gala-comments]').forEach((root) => {
|
|
@@ -8,7 +8,99 @@ function selectableFallback(control) {
|
|
|
8
8
|
if (status) status.textContent = 'Select and copy the URL shown.';
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
const readingProgress = document.querySelector('[data-reading-progress]');
|
|
12
|
+
const readingContent = document.querySelector('.gala-markdown');
|
|
13
|
+
if (readingProgress && readingContent) {
|
|
14
|
+
let progressFrame = null;
|
|
15
|
+
const updateReadingProgress = () => {
|
|
16
|
+
progressFrame = null;
|
|
17
|
+
const start = readingContent.offsetTop;
|
|
18
|
+
const distance = Math.max(1, readingContent.offsetHeight - window.innerHeight);
|
|
19
|
+
const progress = Math.min(1, Math.max(0, (window.scrollY - start) / distance));
|
|
20
|
+
readingProgress.value = progress;
|
|
21
|
+
};
|
|
22
|
+
const scheduleReadingProgress = () => {
|
|
23
|
+
if (progressFrame == null) progressFrame = requestAnimationFrame(updateReadingProgress);
|
|
24
|
+
};
|
|
25
|
+
addEventListener('scroll', scheduleReadingProgress, { passive: true });
|
|
26
|
+
addEventListener('resize', scheduleReadingProgress, { passive: true });
|
|
27
|
+
scheduleReadingProgress();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const articleToc = document.querySelector('.gala-toc');
|
|
31
|
+
if (articleToc?.tagName === 'DETAILS') {
|
|
32
|
+
const tocNavigation = articleToc.querySelector('nav');
|
|
33
|
+
const tocLinks = [...articleToc.querySelectorAll('a[href^="#"]')];
|
|
34
|
+
const headings = tocLinks.map((link) => document.getElementById(link.hash.slice(1))).filter(Boolean);
|
|
35
|
+
let tocFrame = null;
|
|
36
|
+
const synchronizeToc = () => {
|
|
37
|
+
tocFrame = null;
|
|
38
|
+
articleToc.classList.toggle('gala-toc--floating', articleToc.getBoundingClientRect().top <= 96);
|
|
39
|
+
if (!headings.length) return;
|
|
40
|
+
const readingLine = window.innerHeight * 0.42;
|
|
41
|
+
let active = headings[0];
|
|
42
|
+
for (const heading of headings) {
|
|
43
|
+
if (heading.getBoundingClientRect().top <= readingLine) active = heading;
|
|
44
|
+
else break;
|
|
45
|
+
}
|
|
46
|
+
const activeLink = tocLinks.find((link) => link.hash === `#${active.id}`);
|
|
47
|
+
for (const link of tocLinks) {
|
|
48
|
+
if (link === activeLink) link.setAttribute('aria-current', 'location');
|
|
49
|
+
else link.removeAttribute('aria-current');
|
|
50
|
+
}
|
|
51
|
+
if (tocNavigation && tocLinks.length) {
|
|
52
|
+
const firstRect = tocLinks[0].getBoundingClientRect();
|
|
53
|
+
const lastRect = tocLinks[tocLinks.length - 1].getBoundingClientRect();
|
|
54
|
+
articleToc.classList.toggle('gala-toc--overflowing',
|
|
55
|
+
lastRect.bottom - firstRect.top > tocNavigation.clientHeight);
|
|
56
|
+
}
|
|
57
|
+
if (activeLink && tocNavigation && tocNavigation.scrollHeight > tocNavigation.clientHeight) {
|
|
58
|
+
const navigationRect = tocNavigation.getBoundingClientRect();
|
|
59
|
+
const activeRect = activeLink.getBoundingClientRect();
|
|
60
|
+
const target = tocNavigation.scrollTop + activeRect.top - navigationRect.top
|
|
61
|
+
- (tocNavigation.clientHeight - activeRect.height) / 2;
|
|
62
|
+
tocNavigation.scrollTo({ top: Math.max(0, target), behavior: 'auto' });
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const scheduleToc = () => {
|
|
66
|
+
if (tocFrame == null) tocFrame = requestAnimationFrame(synchronizeToc);
|
|
67
|
+
};
|
|
68
|
+
addEventListener('scroll', scheduleToc, { passive: true });
|
|
69
|
+
addEventListener('resize', scheduleToc, { passive: true });
|
|
70
|
+
scheduleToc();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const actionRail = document.querySelector('.gala-action-rail');
|
|
74
|
+
const actionDock = document.querySelector('[data-action-dock]');
|
|
75
|
+
if (actionRail && actionDock) {
|
|
76
|
+
const synchronizeActionRail = (entries) => {
|
|
77
|
+
actionRail.classList.toggle('gala-action-rail--integrated', entries[0].isIntersecting);
|
|
78
|
+
};
|
|
79
|
+
new IntersectionObserver(synchronizeActionRail, {
|
|
80
|
+
rootMargin: '0px 0px 18% 0px', threshold: 0
|
|
81
|
+
}).observe(actionDock);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function presentFollowState(control, following) {
|
|
85
|
+
const label = following ? 'Unfollow article' : 'Follow article';
|
|
86
|
+
control.setAttribute('aria-pressed', String(following));
|
|
87
|
+
control.setAttribute('aria-label', label);
|
|
88
|
+
control.title = label;
|
|
89
|
+
const visibleLabel = control.querySelector('[data-follow-label]');
|
|
90
|
+
if (visibleLabel) visibleLabel.textContent = label;
|
|
91
|
+
}
|
|
92
|
+
|
|
11
93
|
document.addEventListener('click', async (event) => {
|
|
94
|
+
if (event.target instanceof HTMLDialogElement && event.target.open) {
|
|
95
|
+
const bounds = event.target.getBoundingClientRect();
|
|
96
|
+
const outside = event.clientX < bounds.left || event.clientX > bounds.right
|
|
97
|
+
|| event.clientY < bounds.top || event.clientY > bounds.bottom;
|
|
98
|
+
if (outside) {
|
|
99
|
+
event.target.close();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
12
104
|
const embed = event.target.closest('[data-gala-embed-load]');
|
|
13
105
|
if (embed) {
|
|
14
106
|
const container = embed.closest('[data-gala-embed]');
|
|
@@ -66,8 +158,7 @@ document.addEventListener('click', async (event) => {
|
|
|
66
158
|
targetType: 'articles', targetId: region.dataset.articleId
|
|
67
159
|
});
|
|
68
160
|
if (!saved) return;
|
|
69
|
-
follow
|
|
70
|
-
follow.textContent = active ? 'Unfollow article' : 'Follow article';
|
|
161
|
+
presentFollowState(follow, active);
|
|
71
162
|
return;
|
|
72
163
|
}
|
|
73
164
|
|
|
@@ -130,13 +221,6 @@ codeBlocks.forEach((pre) => {
|
|
|
130
221
|
wrapper.prepend(control);
|
|
131
222
|
});
|
|
132
223
|
|
|
133
|
-
function textElement(name, text, className) {
|
|
134
|
-
const element = document.createElement(name);
|
|
135
|
-
element.textContent = text;
|
|
136
|
-
if (className) element.className = className;
|
|
137
|
-
return element;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
224
|
function engagementCounts(data) {
|
|
141
225
|
const reactionTotal = Object.values(data.reactions ?? {})
|
|
142
226
|
.reduce((total, count) => total + (Number.isSafeInteger(count) ? count : 0), 0);
|
|
@@ -147,33 +231,22 @@ function engagementCounts(data) {
|
|
|
147
231
|
];
|
|
148
232
|
}
|
|
149
233
|
|
|
150
|
-
function
|
|
234
|
+
function publicCount(value) {
|
|
235
|
+
if (value < 1000) return '<1K';
|
|
236
|
+
return new Intl.NumberFormat('en', {
|
|
237
|
+
notation: 'compact', maximumFractionDigits: 1
|
|
238
|
+
}).format(value);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function renderEngagement(region, payload) {
|
|
151
242
|
const data = payload?.data;
|
|
152
243
|
if (!data || typeof data !== 'object') throw new TypeError('Engagement data is invalid');
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
profile.className = 'gala-engagement-profile';
|
|
160
|
-
profile.setAttribute('aria-label', 'Author profile');
|
|
161
|
-
profile.append(textElement('h2', data.profile.displayName));
|
|
162
|
-
if (data.profile.username) profile.append(textElement('p', `@${data.profile.username}`));
|
|
163
|
-
if (Number.isSafeInteger(data.profile.followerCount)) {
|
|
164
|
-
profile.append(textElement('p', `${data.profile.followerCount} followers`));
|
|
165
|
-
}
|
|
166
|
-
live.append(profile);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (!appendComments) {
|
|
170
|
-
const summary = document.createElement('dl');
|
|
171
|
-
for (const [label, value] of engagementCounts(data)) {
|
|
172
|
-
const item = document.createElement('div');
|
|
173
|
-
item.append(textElement('dt', label), textElement('dd', String(value)));
|
|
174
|
-
summary.append(item);
|
|
175
|
-
}
|
|
176
|
-
live.append(summary);
|
|
244
|
+
for (const [label, value] of engagementCounts(data)) {
|
|
245
|
+
region.querySelectorAll(`[data-engagement-stat="${label.toLowerCase()}"]`)
|
|
246
|
+
.forEach((count) => {
|
|
247
|
+
count.textContent = publicCount(value);
|
|
248
|
+
count.closest('div')?.setAttribute('title', `${value} ${label.toLowerCase()}`);
|
|
249
|
+
});
|
|
177
250
|
}
|
|
178
251
|
|
|
179
252
|
region.querySelector('[data-engagement-snapshot]')?.remove();
|
|
@@ -182,6 +255,7 @@ function renderEngagement(region, payload, appendComments = false) {
|
|
|
182
255
|
|
|
183
256
|
async function refreshEngagement(region, commentsCursor = '', appendComments = false, fresh = false) {
|
|
184
257
|
const status = region.querySelector('[data-engagement-status]');
|
|
258
|
+
const commentsOwnStatus = Boolean(region.querySelector('[data-gala-comments]'));
|
|
185
259
|
try {
|
|
186
260
|
const requestUrl = new URL(region.dataset.engagementUrl);
|
|
187
261
|
if (commentsCursor) requestUrl.searchParams.set('commentsCursor', commentsCursor);
|
|
@@ -192,11 +266,11 @@ async function refreshEngagement(region, commentsCursor = '', appendComments = f
|
|
|
192
266
|
});
|
|
193
267
|
if (!response.ok) throw new Error(`Engagement returned HTTP ${response.status}`);
|
|
194
268
|
const payload = await response.json();
|
|
195
|
-
renderEngagement(region, payload
|
|
196
|
-
if (status) status.textContent = payload.errors?.length
|
|
269
|
+
renderEngagement(region, payload);
|
|
270
|
+
if (status && !commentsOwnStatus) status.textContent = payload.errors?.length
|
|
197
271
|
? 'Some engagement data is temporarily unavailable.' : '';
|
|
198
272
|
} catch {
|
|
199
|
-
if (status) status.textContent = '
|
|
273
|
+
if (status && !commentsOwnStatus) status.textContent = 'Engagement is temporarily unavailable.';
|
|
200
274
|
}
|
|
201
275
|
}
|
|
202
276
|
|
|
@@ -424,9 +498,7 @@ if (sessionFrame) {
|
|
|
424
498
|
});
|
|
425
499
|
document.querySelectorAll('[data-follow-article]').forEach((control) => {
|
|
426
500
|
const following = event.data.state?.following === true;
|
|
427
|
-
control
|
|
428
|
-
control.setAttribute('aria-label', following ? 'Unfollow article' : 'Follow article');
|
|
429
|
-
control.title = following ? 'Unfollow article' : 'Follow article';
|
|
501
|
+
presentFollowState(control, following);
|
|
430
502
|
});
|
|
431
503
|
return;
|
|
432
504
|
}
|
|
@@ -436,7 +508,7 @@ if (sessionFrame) {
|
|
|
436
508
|
if (event.data?.type === 'gala-session-height') {
|
|
437
509
|
const height = Number(event.data.height);
|
|
438
510
|
if (Number.isFinite(height) && height > 0 && height < 2000) {
|
|
439
|
-
sessionFrame.
|
|
511
|
+
sessionFrame.height = String(Math.ceil(height));
|
|
440
512
|
}
|
|
441
513
|
return;
|
|
442
514
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(()=>{var w=["system","light","dark"],O="gala-color-mode";function D(){let e=document.documentElement.dataset.mode;return w.includes(e)?e:"system"}function Y(){try{let e=localStorage.getItem(O);if(w.includes(e))return e}catch{return D()}return D()}function k(e){document.documentElement.dataset.mode=e;let t=w[(w.indexOf(e)+1)%w.length];document.querySelectorAll("[data-theme-mode-toggle]").forEach(a=>{let n=a.querySelector?.("[data-theme-mode-label]");n?n.textContent=`Theme: ${e}`:a.textContent=`Theme: ${e}`,a.setAttribute("aria-label",`Color mode: ${e}. Activate for ${t}.`)})}document.addEventListener("DOMContentLoaded",()=>{k(Y()),document.querySelectorAll("[data-theme-mode-toggle]").forEach(e=>{e.addEventListener("click",()=>{let t=w[(w.indexOf(document.documentElement.dataset.mode)+1)%w.length];try{localStorage.setItem(O,t)}catch{}k(t)})})});var P="gala-language-preference";function B(){try{return localStorage.getItem(P)}catch{return null}}function M(e,t){return[...e.options].find(a=>a.value===t)}document.addEventListener("DOMContentLoaded",()=>{let e=B();document.querySelectorAll("[data-language-preference]").forEach(t=>{e!=null&&M(t,e)!=null&&(t.value=e),t.addEventListener("change",()=>{try{localStorage.setItem(P,t.value)}catch{}if(!t.hasAttribute("data-navigate-on-selection"))return;let a=M(t,t.value);a?.dataset.url&&window.location.assign(a.dataset.url)})})});function _(e){let t=e.closest(".gala-share"),a=t?.querySelector(".gala-share__fallback");a?.classList.add("gala-share__fallback--visible"),a?.focus(),a?.select();let n=t?.querySelector(".gala-share__status");n&&(n.textContent="Select and copy the URL shown.")}document.addEventListener("click",async e=>{let t=e.target.closest("[data-gala-embed-load]");if(t){let r=t.closest("[data-gala-embed]"),i=t.dataset.galaEmbedSrc,u=t.dataset.galaEmbedLoad;if(!r||!i||!["youtube","codepen"].includes(u))return;let c=document.createElement("iframe");c.src=i,c.title=u==="youtube"?"YouTube video":"CodePen example",c.loading="eager",c.referrerPolicy="strict-origin-when-cross-origin",c.setAttribute("sandbox","allow-forms allow-popups allow-presentation allow-same-origin allow-scripts"),c.setAttribute("allow",u==="youtube"?"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share":"fullscreen"),c.setAttribute("allowfullscreen",""),r.replaceChildren(c);return}let a=e.target.closest("[data-open-dialog]");if(a){let r=document.getElementById(a.dataset.openDialog);r instanceof HTMLDialogElement&&r.showModal();return}let n=e.target.closest("[data-reaction]");if(n){let r=n.closest("[data-engagement-url]");if(!r)return;if(!y)return q({kind:"reaction",region:r,selector:`[data-reaction="${n.dataset.reaction}"]`});let i=n.getAttribute("aria-pressed")!=="true";if(!await F(r,i?"reaction.add":"reaction.remove",{articleId:r.dataset.articleId,reaction:n.dataset.reaction}))return;n.setAttribute("aria-pressed",String(i));return}let s=e.target.closest("[data-follow-article]");if(s){let r=s.closest("[data-engagement-url]");if(!r)return;if(!y)return q({kind:"follow",region:r,selector:"[data-follow-article]"});let i=s.getAttribute("aria-pressed")!=="true";if(!await F(r,i?"follow.add":"follow.remove",{targetType:"articles",targetId:r.dataset.articleId}))return;s.setAttribute("aria-pressed",String(i)),s.textContent=i?"Unfollow article":"Follow article";return}let o=e.target.closest("[data-copy-url]");if(o){let r=o.dataset.copyUrl;if(!window.isSecureContext||!navigator.clipboard?.writeText){_(o);return}try{await navigator.clipboard.writeText(r);let i=o.closest(".gala-share")?.querySelector(".gala-share__status");i&&(i.textContent="Link copied.")}catch{_(o)}return}let l=e.target.closest("[data-native-share]");if(l){if(navigator.share)try{await navigator.share({title:document.title,url:l.dataset.nativeShare})}catch(r){r.name!=="AbortError"&&_(l)}else _(l);return}let p=e.target.closest("[data-copy-code]");if(!p)return;let E=p.closest(".gala-code-block")?.querySelector("code");if(!(!E||!navigator.clipboard?.writeText||!window.isSecureContext))try{await navigator.clipboard.writeText(E.textContent),p.textContent="Copied"}catch{p.textContent="Select code to copy"}});var K=new Set([...document.querySelectorAll("pre code")].map(e=>e.closest("pre")).filter(Boolean));K.forEach(e=>{let t=document.createElement("div");t.className="gala-code-block",e.before(t),t.append(e);let a=document.createElement("button");a.type="button",a.dataset.copyCode="",a.textContent="Copy code",a.setAttribute("aria-label","Copy code block"),t.prepend(a)});function C(e,t,a){let n=document.createElement(e);return n.textContent=t,a&&(n.className=a),n}function z(e){return[["Reactions",Object.values(e.reactions??{}).reduce((a,n)=>a+(Number.isSafeInteger(n)?n:0),0)],["Comments",Number.isSafeInteger(e.comments?.totalCount)?e.comments.totalCount:0],["Views",Number.isSafeInteger(e.views?.count)?e.views.count:0]]}function J(e,t,a=!1){let n=t?.data;if(!n||typeof n!="object")throw new TypeError("Engagement data is invalid");let s=e.querySelector("[data-engagement-live]");if(s){if(a||s.replaceChildren(),!a&&n.profile){let o=document.createElement("section");o.className="gala-engagement-profile",o.setAttribute("aria-label","Author profile"),o.append(C("h2",n.profile.displayName)),n.profile.username&&o.append(C("p",`@${n.profile.username}`)),Number.isSafeInteger(n.profile.followerCount)&&o.append(C("p",`${n.profile.followerCount} followers`)),s.append(o)}if(!a){let o=document.createElement("dl");for(let[l,p]of z(n)){let E=document.createElement("div");E.append(C("dt",l),C("dd",String(p))),o.append(E)}s.append(o)}e.querySelector("[data-engagement-snapshot]")?.remove(),e.querySelector(".gala-engagement__placeholder")?.remove()}}async function U(e,t="",a=!1,n=!1){let s=e.querySelector("[data-engagement-status]");try{let o=new URL(e.dataset.engagementUrl);t&&o.searchParams.set("commentsCursor",t);let l=await fetch(o,{headers:{Accept:"application/json"},credentials:"omit",cache:n?"no-store":"default"});if(!l.ok)throw new Error(`Engagement returned HTTP ${l.status}`);let p=await l.json();J(e,p,a),s&&(s.textContent=p.errors?.length?"Some engagement data is temporarily unavailable.":"")}catch{s&&(s.textContent="Live engagement data is temporarily unavailable.")}}function X(){let e=new URL(window.location.href).searchParams,t={};for(let a of["utm_source","utm_medium","utm_campaign","utm_content","utm_term"]){let n=e.get(a);n&&[...n].length<=128&&(t[a]=n)}return t}function Z(e){let t=new URL(e.dataset.engagementUrl);t.pathname=t.pathname.replace(/\/engagement$/,"/views"),fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({language:document.documentElement.lang||void 0,referrer:document.referrer||void 0,campaign:X()}),credentials:"omit",keepalive:!0}).catch(()=>{})}document.addEventListener("gala-request-sign-in",e=>{y||b||q({kind:e.detail?.kind??"comment"})});var y=null,G=null,N=null,v=new Map,b=null;function q(e){let t=e.field;b={kind:e.kind,region:e.region,selector:e.selector,draft:t?t.value:null,caret:t?t.selectionStart:null};let a=document.querySelector("[data-gala-session-frame]"),n=document.querySelector("[data-engagement-status]");if(!a)return;let s=new URL(a.getAttribute("src"),window.location.href),o=new URL("/v1/widget/session/sign-in",s.origin);o.searchParams.set("siteId",s.searchParams.get("siteId")??""),t&&t.blur(),window.open(o,"gala-sign-in","popup,width=520,height=680")||(b=null,n&&(n.textContent="Allow pop-ups for this site to sign in, then try again."))}function $(){let e=b;if(!e||!document.hasFocus())return;let t=e.region?.querySelector(e.selector);if(t){if(b=null,e.kind==="comment"){e.draft!=null&&t.value===""&&(t.value=e.draft),t.focus(),e.caret!=null&&typeof t.setSelectionRange=="function"&&t.setSelectionRange(e.caret,e.caret);return}t.click()}}window.addEventListener("focus",()=>{y&&b&&$()});window.addEventListener("message",e=>{let t=document.querySelector("[data-gala-session-frame]");if(!t||e.data?.type!=="gala-session-established")return;let a=new URL(t.src).origin;e.origin===a&&t.contentWindow?.postMessage({type:"gala-session-recheck"},a)});document.querySelectorAll("[data-engagement-url]").forEach(e=>{U(e),Z(e)});var I=document.querySelector("[data-gala-session-frame]");function j(e){return e==="AUTHENTICATION_REQUIRED"||e==="INVALID_BEARER_TOKEN"||e==="REAUTHENTICATION_REQUIRED"?"Sign in again to continue.":e==="ENGAGEMENT_RATE_LIMITED"?"You are posting too quickly; try again shortly.":e==="CONTACT_RATE_LIMITED"?"You are sending too quickly; try again later.":e==="RESOURCE_NOT_FOUND"?"That item is no longer available.":e==="INVALID_ENGAGEMENT_WRITE"||e==="INVALID_CONTACT_SUBMISSION"||e==="INVALID_REQUEST"?"Check your entry and try again.":e==="ACCESS_DENIED"?"That action is not available for this account.":e==="IDEMPOTENCY_CONFLICT"||e==="ENGAGEMENT_STATE_CONFLICT"?"The item changed; reload and try again.":"The action could not be completed. Try again."}function W(e,t){if(!I||!y)return Promise.reject(new Error("AUTHENTICATION_REQUIRED"));let a=crypto.randomUUID();return new Promise((n,s)=>{let o=setTimeout(()=>{v.delete(a),s(new Error("REQUEST_TIMEOUT"))},1e4);v.set(a,{resolve:l=>{clearTimeout(o),n(l)},reject:l=>{clearTimeout(o),s(l)}}),I.contentWindow.postMessage({type:"gala-engagement-write",requestId:a,operation:e,payload:t},new URL(I.src).origin)})}document.addEventListener("submit",async e=>{let t=e.target.closest("[data-contact-form] form");if(!t)return;e.preventDefault();let a=t.closest("[data-contact-form]"),n=a.querySelector("[data-contact-status]");if(!y){n.textContent="Sign in with the account button before sending.";return}let s=new FormData(t);try{n.textContent="Sending\u2026",await W("contact.submit",{siteId:a.dataset.siteId,subject:s.get("subject"),message:s.get("message"),website:s.get("website")||null,phone:s.get("phone")||null}),t.reset(),n.textContent="Message sent."}catch(o){n.textContent=j(o.message)}});async function F(e,t,a){let n=e.querySelector("[data-engagement-status]");try{return n&&(n.textContent="Saving\u2026"),await W(t,a),n&&(n.textContent="Saved."),await U(e,"",!1,!0),!0}catch(s){return n&&(n.textContent=j(s.message)),!1}}if(I){let e=new URL(I.src).origin;window.addEventListener("message",t=>{if(t.origin!==e||t.source!==I.contentWindow)return;if(t.data?.type==="gala-engagement-result"){let o=v.get(t.data.requestId);if(!o)return;v.delete(t.data.requestId),t.data.ok===!0?o.resolve(t.data.result):o.reject(new Error(t.data.error?.code||"ENGAGEMENT_WRITE_FAILED"));return}if(t.data?.type==="gala-reader-state-result"){if(t.data.requestId!==N||t.data.ok!==!0)return;N=null;let o=new Set(Array.isArray(t.data.state?.reactions)?t.data.state.reactions:[]);document.querySelectorAll("[data-reaction]").forEach(l=>{l.setAttribute("aria-pressed",String(o.has(l.dataset.reaction)))}),document.querySelectorAll("[data-follow-article]").forEach(l=>{let p=t.data.state?.following===!0;l.setAttribute("aria-pressed",String(p)),l.setAttribute("aria-label",p?"Unfollow article":"Follow article"),l.title=p?"Unfollow article":"Follow article"});return}if(t.data?.type==="gala-session-height"){let o=Number(t.data.height);Number.isFinite(o)&&o>0&&o<2e3&&(I.style.height=`${Math.ceil(o)}px`);return}if(t.data?.type!=="gala-session")return;y=t.data.user&&typeof t.data.user.id=="string"?t.data.user:null;let a=document.querySelector("[data-user-control]"),n=y?.displayName;if(a){a.setAttribute("aria-label",n?`Account: ${n}`:"Sign in or view account"),a.title=n?`Account: ${n}`:"Account";let o=a.querySelector("[data-user-label]");o&&(o.textContent=n||"Sign in")}let s=G!==(y?.id??null);if(G=y?.id??null,y){let o=document.querySelector("[data-article-id]");o&&(N=crypto.randomUUID(),I.contentWindow.postMessage({type:"gala-reader-state-request",requestId:N,articleId:o.dataset.articleId},e))}document.querySelectorAll("[data-engagement-url]").forEach(o=>{s&&U(o)}),y&&b&&$()})}document.querySelectorAll("[data-gala-search]").forEach(e=>{let t=e.querySelector("form"),a=t?.elements.namedItem("q"),n=e.querySelector("[data-search-status]"),s=e.querySelector("[data-search-results]"),o=e.dataset.indexUrl,l,p=u=>String(u??"").normalize("NFKC").toLocaleLowerCase();function E(u,c){s.replaceChildren();for(let m of u){let d=document.createElement("li"),g=document.createElement("article"),h=document.createElement("h2"),A=document.createElement("a");if(A.href=m.url,A.lang=m.language,A.textContent=m.title,h.append(A),g.append(h),m.description){let x=document.createElement("p");x.textContent=m.description,g.append(x)}d.append(g),s.append(d)}n.textContent=`${u.length} result${u.length===1?"":"s"} for \u201C${c}\u201D.`}async function r(u){let c=u.trim();if(c===""){s.replaceChildren(),n.textContent="Enter a search term.";return}n.textContent="Searching\u2026";try{l??=fetch(o,{headers:{Accept:"application/json"}}).then(async g=>{if(!g.ok)throw new Error(`Search index returned HTTP ${g.status}`);let h=await g.json();if(h?.schemaVersion!==1||!Array.isArray(h.entries))throw new TypeError("Search index schema is unsupported");return h.entries});let m=p(c),d=(await l).filter(g=>p([g.title,g.description,g.language,...Array.isArray(g.tags)?g.tags:[],g.body].join(`
|
|
2
|
-
`)).includes(m));E(
|
|
1
|
+
(()=>{var b=["system","light","dark"],O="gala-color-mode";function D(){let e=document.documentElement.dataset.mode;return b.includes(e)?e:"system"}function J(){try{let e=localStorage.getItem(O);if(b.includes(e))return e}catch{return D()}return D()}function M(e){document.documentElement.dataset.mode=e;let t=b[(b.indexOf(e)+1)%b.length];document.querySelectorAll("[data-theme-mode-toggle]").forEach(n=>{let a=n.querySelector?.("[data-theme-mode-label]");a?a.textContent=`Theme: ${e}`:n.textContent=`Theme: ${e}`,n.setAttribute("aria-label",`Color mode: ${e}. Activate for ${t}.`)})}document.addEventListener("DOMContentLoaded",()=>{M(J()),document.querySelectorAll("[data-theme-mode-toggle]").forEach(e=>{e.addEventListener("click",()=>{let t=b[(b.indexOf(document.documentElement.dataset.mode)+1)%b.length];try{localStorage.setItem(O,t)}catch{}M(t)})})});var F="gala-language-preference";function Z(){try{return localStorage.getItem(F)}catch{return null}}function P(e,t){return[...e.options].find(n=>n.value===t)}document.addEventListener("DOMContentLoaded",()=>{let e=Z();document.querySelectorAll("[data-language-preference]").forEach(t=>{e!=null&&P(t,e)!=null&&(t.value=e),t.addEventListener("change",()=>{try{localStorage.setItem(F,t.value)}catch{}if(!t.hasAttribute("data-navigate-on-selection"))return;let n=P(t,t.value);n?.dataset.url&&window.location.assign(n.dataset.url)})})});function v(e){let t=e.closest(".gala-share"),n=t?.querySelector(".gala-share__fallback");n?.classList.add("gala-share__fallback--visible"),n?.focus(),n?.select();let a=t?.querySelector(".gala-share__status");a&&(a.textContent="Select and copy the URL shown.")}var G=document.querySelector("[data-reading-progress]"),q=document.querySelector(".gala-markdown");if(G&&q){let e=null,t=()=>{e=null;let a=q.offsetTop,s=Math.max(1,q.offsetHeight-window.innerHeight),o=Math.min(1,Math.max(0,(window.scrollY-a)/s));G.value=o},n=()=>{e==null&&(e=requestAnimationFrame(t))};addEventListener("scroll",n,{passive:!0}),addEventListener("resize",n,{passive:!0}),n()}var C=document.querySelector(".gala-toc");if(C?.tagName==="DETAILS"){let e=C.querySelector("nav"),t=[...C.querySelectorAll('a[href^="#"]')],n=t.map(d=>document.getElementById(d.hash.slice(1))).filter(Boolean),a=null,s=()=>{if(a=null,C.classList.toggle("gala-toc--floating",C.getBoundingClientRect().top<=96),!n.length)return;let d=window.innerHeight*.42,f=n[0];for(let i of n)if(i.getBoundingClientRect().top<=d)f=i;else break;let E=t.find(i=>i.hash===`#${f.id}`);for(let i of t)i===E?i.setAttribute("aria-current","location"):i.removeAttribute("aria-current");if(e&&t.length){let i=t[0].getBoundingClientRect(),r=t[t.length-1].getBoundingClientRect();C.classList.toggle("gala-toc--overflowing",r.bottom-i.top>e.clientHeight)}if(E&&e&&e.scrollHeight>e.clientHeight){let i=e.getBoundingClientRect(),r=E.getBoundingClientRect(),c=e.scrollTop+r.top-i.top-(e.clientHeight-r.height)/2;e.scrollTo({top:Math.max(0,c),behavior:"auto"})}},o=()=>{a==null&&(a=requestAnimationFrame(s))};addEventListener("scroll",o,{passive:!0}),addEventListener("resize",o,{passive:!0}),o()}var H=document.querySelector(".gala-action-rail"),$=document.querySelector("[data-action-dock]");if(H&&$){let e=t=>{H.classList.toggle("gala-action-rail--integrated",t[0].isIntersecting)};new IntersectionObserver(e,{rootMargin:"0px 0px 18% 0px",threshold:0}).observe($)}function W(e,t){let n=t?"Unfollow article":"Follow article";e.setAttribute("aria-pressed",String(t)),e.setAttribute("aria-label",n),e.title=n;let a=e.querySelector("[data-follow-label]");a&&(a.textContent=n)}document.addEventListener("click",async e=>{if(e.target instanceof HTMLDialogElement&&e.target.open){let i=e.target.getBoundingClientRect();if(e.clientX<i.left||e.clientX>i.right||e.clientY<i.top||e.clientY>i.bottom){e.target.close();return}}let t=e.target.closest("[data-gala-embed-load]");if(t){let i=t.closest("[data-gala-embed]"),r=t.dataset.galaEmbedSrc,c=t.dataset.galaEmbedLoad;if(!i||!r||!["youtube","codepen"].includes(c))return;let u=document.createElement("iframe");u.src=r,u.title=c==="youtube"?"YouTube video":"CodePen example",u.loading="eager",u.referrerPolicy="strict-origin-when-cross-origin",u.setAttribute("sandbox","allow-forms allow-popups allow-presentation allow-same-origin allow-scripts"),u.setAttribute("allow",c==="youtube"?"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share":"fullscreen"),u.setAttribute("allowfullscreen",""),i.replaceChildren(u);return}let n=e.target.closest("[data-open-dialog]");if(n){let i=document.getElementById(n.dataset.openDialog);i instanceof HTMLDialogElement&&i.showModal();return}let a=e.target.closest("[data-reaction]");if(a){let i=a.closest("[data-engagement-url]");if(!i)return;if(!y)return R({kind:"reaction",region:i,selector:`[data-reaction="${a.dataset.reaction}"]`});let r=a.getAttribute("aria-pressed")!=="true";if(!await B(i,r?"reaction.add":"reaction.remove",{articleId:i.dataset.articleId,reaction:a.dataset.reaction}))return;a.setAttribute("aria-pressed",String(r));return}let s=e.target.closest("[data-follow-article]");if(s){let i=s.closest("[data-engagement-url]");if(!i)return;if(!y)return R({kind:"follow",region:i,selector:"[data-follow-article]"});let r=s.getAttribute("aria-pressed")!=="true";if(!await B(i,r?"follow.add":"follow.remove",{targetType:"articles",targetId:i.dataset.articleId}))return;W(s,r);return}let o=e.target.closest("[data-copy-url]");if(o){let i=o.dataset.copyUrl;if(!window.isSecureContext||!navigator.clipboard?.writeText){v(o);return}try{await navigator.clipboard.writeText(i);let r=o.closest(".gala-share")?.querySelector(".gala-share__status");r&&(r.textContent="Link copied.")}catch{v(o)}return}let d=e.target.closest("[data-native-share]");if(d){if(navigator.share)try{await navigator.share({title:document.title,url:d.dataset.nativeShare})}catch(i){i.name!=="AbortError"&&v(d)}else v(d);return}let f=e.target.closest("[data-copy-code]");if(!f)return;let E=f.closest(".gala-code-block")?.querySelector("code");if(!(!E||!navigator.clipboard?.writeText||!window.isSecureContext))try{await navigator.clipboard.writeText(E.textContent),f.textContent="Copied"}catch{f.textContent="Select code to copy"}});var ee=new Set([...document.querySelectorAll("pre code")].map(e=>e.closest("pre")).filter(Boolean));ee.forEach(e=>{let t=document.createElement("div");t.className="gala-code-block",e.before(t),t.append(e);let n=document.createElement("button");n.type="button",n.dataset.copyCode="",n.textContent="Copy code",n.setAttribute("aria-label","Copy code block"),t.prepend(n)});function te(e){return[["Reactions",Object.values(e.reactions??{}).reduce((n,a)=>n+(Number.isSafeInteger(a)?a:0),0)],["Comments",Number.isSafeInteger(e.comments?.totalCount)?e.comments.totalCount:0],["Views",Number.isSafeInteger(e.views?.count)?e.views.count:0]]}function ne(e){return e<1e3?"<1K":new Intl.NumberFormat("en",{notation:"compact",maximumFractionDigits:1}).format(e)}function ae(e,t){let n=t?.data;if(!n||typeof n!="object")throw new TypeError("Engagement data is invalid");for(let[a,s]of te(n))e.querySelectorAll(`[data-engagement-stat="${a.toLowerCase()}"]`).forEach(o=>{o.textContent=ne(s),o.closest("div")?.setAttribute("title",`${s} ${a.toLowerCase()}`)});e.querySelector("[data-engagement-snapshot]")?.remove(),e.querySelector(".gala-engagement__placeholder")?.remove()}async function x(e,t="",n=!1,a=!1){let s=e.querySelector("[data-engagement-status]"),o=!!e.querySelector("[data-gala-comments]");try{let d=new URL(e.dataset.engagementUrl);t&&d.searchParams.set("commentsCursor",t);let f=await fetch(d,{headers:{Accept:"application/json"},credentials:"omit",cache:a?"no-store":"default"});if(!f.ok)throw new Error(`Engagement returned HTTP ${f.status}`);let E=await f.json();ae(e,E),s&&!o&&(s.textContent=E.errors?.length?"Some engagement data is temporarily unavailable.":"")}catch{s&&!o&&(s.textContent="Engagement is temporarily unavailable.")}}function re(){let e=new URL(window.location.href).searchParams,t={};for(let n of["utm_source","utm_medium","utm_campaign","utm_content","utm_term"]){let a=e.get(n);a&&[...a].length<=128&&(t[n]=a)}return t}function oe(e){let t=new URL(e.dataset.engagementUrl);t.pathname=t.pathname.replace(/\/engagement$/,"/views"),fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({language:document.documentElement.lang||void 0,referrer:document.referrer||void 0,campaign:re()}),credentials:"omit",keepalive:!0}).catch(()=>{})}document.addEventListener("gala-request-sign-in",e=>{y||T||R({kind:e.detail?.kind??"comment"})});var y=null,j=null,_=null,L=new Map,T=null;function R(e){let t=e.field;T={kind:e.kind,region:e.region,selector:e.selector,draft:t?t.value:null,caret:t?t.selectionStart:null};let n=document.querySelector("[data-gala-session-frame]"),a=document.querySelector("[data-engagement-status]");if(!n)return;let s=new URL(n.getAttribute("src"),window.location.href),o=new URL("/v1/widget/session/sign-in",s.origin);o.searchParams.set("siteId",s.searchParams.get("siteId")??""),t&&t.blur(),window.open(o,"gala-sign-in","popup,width=520,height=680")||(T=null,a&&(a.textContent="Allow pop-ups for this site to sign in, then try again."))}function Y(){let e=T;if(!e||!document.hasFocus())return;let t=e.region?.querySelector(e.selector);if(t){if(T=null,e.kind==="comment"){e.draft!=null&&t.value===""&&(t.value=e.draft),t.focus(),e.caret!=null&&typeof t.setSelectionRange=="function"&&t.setSelectionRange(e.caret,e.caret);return}t.click()}}window.addEventListener("focus",()=>{y&&T&&Y()});window.addEventListener("message",e=>{let t=document.querySelector("[data-gala-session-frame]");if(!t||e.data?.type!=="gala-session-established")return;let n=new URL(t.src).origin;e.origin===n&&t.contentWindow?.postMessage({type:"gala-session-recheck"},n)});document.querySelectorAll("[data-engagement-url]").forEach(e=>{x(e),oe(e)});var I=document.querySelector("[data-gala-session-frame]");function V(e){return e==="AUTHENTICATION_REQUIRED"||e==="INVALID_BEARER_TOKEN"||e==="REAUTHENTICATION_REQUIRED"?"Sign in again to continue.":e==="ENGAGEMENT_RATE_LIMITED"?"You are posting too quickly; try again shortly.":e==="CONTACT_RATE_LIMITED"?"You are sending too quickly; try again later.":e==="RESOURCE_NOT_FOUND"?"That item is no longer available.":e==="INVALID_ENGAGEMENT_WRITE"||e==="INVALID_CONTACT_SUBMISSION"||e==="INVALID_REQUEST"?"Check your entry and try again.":e==="ACCESS_DENIED"?"That action is not available for this account.":e==="IDEMPOTENCY_CONFLICT"||e==="ENGAGEMENT_STATE_CONFLICT"?"The item changed; reload and try again.":"The action could not be completed. Try again."}function Q(e,t){if(!I||!y)return Promise.reject(new Error("AUTHENTICATION_REQUIRED"));let n=crypto.randomUUID();return new Promise((a,s)=>{let o=setTimeout(()=>{L.delete(n),s(new Error("REQUEST_TIMEOUT"))},1e4);L.set(n,{resolve:d=>{clearTimeout(o),a(d)},reject:d=>{clearTimeout(o),s(d)}}),I.contentWindow.postMessage({type:"gala-engagement-write",requestId:n,operation:e,payload:t},new URL(I.src).origin)})}document.addEventListener("submit",async e=>{let t=e.target.closest("[data-contact-form] form");if(!t)return;e.preventDefault();let n=t.closest("[data-contact-form]"),a=n.querySelector("[data-contact-status]");if(!y){a.textContent="Sign in with the account button before sending.";return}let s=new FormData(t);try{a.textContent="Sending\u2026",await Q("contact.submit",{siteId:n.dataset.siteId,subject:s.get("subject"),message:s.get("message"),website:s.get("website")||null,phone:s.get("phone")||null}),t.reset(),a.textContent="Message sent."}catch(o){a.textContent=V(o.message)}});async function B(e,t,n){let a=e.querySelector("[data-engagement-status]");try{return a&&(a.textContent="Saving\u2026"),await Q(t,n),a&&(a.textContent="Saved."),await x(e,"",!1,!0),!0}catch(s){return a&&(a.textContent=V(s.message)),!1}}if(I){let e=new URL(I.src).origin;window.addEventListener("message",t=>{if(t.origin!==e||t.source!==I.contentWindow)return;if(t.data?.type==="gala-engagement-result"){let o=L.get(t.data.requestId);if(!o)return;L.delete(t.data.requestId),t.data.ok===!0?o.resolve(t.data.result):o.reject(new Error(t.data.error?.code||"ENGAGEMENT_WRITE_FAILED"));return}if(t.data?.type==="gala-reader-state-result"){if(t.data.requestId!==_||t.data.ok!==!0)return;_=null;let o=new Set(Array.isArray(t.data.state?.reactions)?t.data.state.reactions:[]);document.querySelectorAll("[data-reaction]").forEach(d=>{d.setAttribute("aria-pressed",String(o.has(d.dataset.reaction)))}),document.querySelectorAll("[data-follow-article]").forEach(d=>{let f=t.data.state?.following===!0;W(d,f)});return}if(t.data?.type==="gala-session-height"){let o=Number(t.data.height);Number.isFinite(o)&&o>0&&o<2e3&&(I.height=String(Math.ceil(o)));return}if(t.data?.type!=="gala-session")return;y=t.data.user&&typeof t.data.user.id=="string"?t.data.user:null;let n=document.querySelector("[data-user-control]"),a=y?.displayName;if(n){n.setAttribute("aria-label",a?`Account: ${a}`:"Sign in or view account"),n.title=a?`Account: ${a}`:"Account";let o=n.querySelector("[data-user-label]");o&&(o.textContent=a||"Sign in")}let s=j!==(y?.id??null);if(j=y?.id??null,y){let o=document.querySelector("[data-article-id]");o&&(_=crypto.randomUUID(),I.contentWindow.postMessage({type:"gala-reader-state-request",requestId:_,articleId:o.dataset.articleId},e))}document.querySelectorAll("[data-engagement-url]").forEach(o=>{s&&x(o)}),y&&T&&Y()})}document.querySelectorAll("[data-gala-search]").forEach(e=>{let t=e.querySelector("form"),n=t?.elements.namedItem("q"),a=e.querySelector("[data-search-status]"),s=e.querySelector("[data-search-results]"),o=e.dataset.indexUrl,d,f=c=>String(c??"").normalize("NFKC").toLocaleLowerCase();function E(c,u){s.replaceChildren();for(let m of c){let p=document.createElement("li"),l=document.createElement("article"),h=document.createElement("h2"),w=document.createElement("a");if(w.href=m.url,w.lang=m.language,w.textContent=m.title,h.append(w),l.append(h),m.description){let k=document.createElement("p");k.textContent=m.description,l.append(k)}p.append(l),s.append(p)}a.textContent=`${c.length} result${c.length===1?"":"s"} for \u201C${u}\u201D.`}async function i(c){let u=c.trim();if(u===""){s.replaceChildren(),a.textContent="Enter a search term.";return}a.textContent="Searching\u2026";try{d??=fetch(o,{headers:{Accept:"application/json"}}).then(async l=>{if(!l.ok)throw new Error(`Search index returned HTTP ${l.status}`);let h=await l.json();if(h?.schemaVersion!==1||!Array.isArray(h.entries))throw new TypeError("Search index schema is unsupported");return h.entries});let m=f(u),p=(await d).filter(l=>f([l.title,l.description,l.language,...Array.isArray(l.tags)?l.tags:[],l.body].join(`
|
|
2
|
+
`)).includes(m));E(p,u)}catch{s.replaceChildren(),a.textContent="Search is temporarily unavailable."}}t?.addEventListener("submit",c=>{c.preventDefault();let u=n.value,m=new URL(window.location.href);u.trim()===""?m.searchParams.delete("q"):m.searchParams.set("q",u),window.history.replaceState(null,"",m),i(u)});let r=new URLSearchParams(window.location.search).get("q")??"";n&&(n.value=r),i(r)});var S={value:null},A=document.querySelector("[data-gala-session-frame]"),z=A?new URL(A.src).origin:null,N=new Map;function K(e){return e==="AUTHENTICATION_REQUIRED"||e==="INVALID_BEARER_TOKEN"||e==="REAUTHENTICATION_REQUIRED"?"Sign in again to continue.":e==="ENGAGEMENT_RATE_LIMITED"?"You are posting too quickly; try again shortly.":e==="CONTACT_RATE_LIMITED"?"You are sending too quickly; try again later.":e==="RESOURCE_NOT_FOUND"?"That item is no longer available.":e==="INVALID_ENGAGEMENT_WRITE"||e==="INVALID_CONTACT_SUBMISSION"||e==="INVALID_REQUEST"?"Check your entry and try again.":e==="ACCESS_DENIED"?"That action is not available for this account.":e==="IDEMPOTENCY_CONFLICT"||e==="ENGAGEMENT_STATE_CONFLICT"?"The item changed; reload and try again.":"The action could not be completed. Try again."}function X(e,t){if(!A||!S.value)return Promise.reject(new Error("AUTHENTICATION_REQUIRED"));let n=crypto.randomUUID();return new Promise((a,s)=>{let o=setTimeout(()=>{N.delete(n),s(new Error("REQUEST_TIMEOUT"))},1e4);N.set(n,{resolve:d=>{clearTimeout(o),a(d)},reject:d=>{clearTimeout(o),s(d)}}),A.contentWindow.postMessage({type:"gala-engagement-write",requestId:n,operation:e,payload:t},z)})}function U(e){document.dispatchEvent(new CustomEvent("gala-request-sign-in",{detail:e}))}A&&window.addEventListener("message",e=>{if(e.origin!==z||e.source!==A.contentWindow)return;if(e.data?.type==="gala-engagement-result"){let n=N.get(e.data.requestId);if(!n)return;N.delete(e.data.requestId),e.data.ok===!0?n.resolve(e.data.result):n.reject(new Error(e.data.error?.code||"ENGAGEMENT_WRITE_FAILED"));return}if(e.data?.type!=="gala-session")return;let t=e.data.user;S.value=t&&typeof t.id=="string"?t:null,window.dispatchEvent(new CustomEvent("gala-session-change"))});var se=5,g=(e,t,n)=>{let a=document.createElement(e);return t&&(a.className=t),n!==void 0&&(a.textContent=n),a};function ie(e){let t=new Map;for(let a of e){let s=a.parentCommentId??"";t.has(s)||t.set(s,[]),t.get(s).push(a)}let n=(a,s)=>(t.get(a)??[]).map(o=>({...o,depth:s,replies:n(o.commentId,s+1)}));return n("",0)}function ce(e,t){let n={items:[],cursor:null,total:0,busy:!1,status:"",phase:"loading"},a=/\/v1\/articles\/([^/]+)\/engagement/.exec(t)?.[1]??"",s=e.closest(".gala-conversation")?.querySelector("[data-engagement-status]");async function o(r="",{append:c=!1,fresh:u=!1}={}){let m=new URL(t);r&&m.searchParams.set("commentsCursor",r);let p=await fetch(m,{headers:{Accept:"application/json"},credentials:"omit",cache:u?"no-store":"default"});if(!p.ok)throw new Error(`Comments returned HTTP ${p.status}`);let l=(await p.json())?.data?.comments;if(!l||!Array.isArray(l.items))throw new TypeError("Comment page is invalid");n.items=c?[...n.items,...l.items]:l.items,n.cursor=l.nextCursor??null,Number.isSafeInteger(l.totalCount)&&(n.total=l.totalCount)}async function d(r,c){if(!S.value)return U({kind:"comment"}),!1;n.busy=!0,n.status="",i();try{return await X(r,c),await o("",{fresh:!0}),!0}catch(u){return n.status=K(u.message),!1}finally{n.busy=!1,i()}}function f(r,c,u){let m=g("form","gala-comment__form"),p=g("textarea");p.rows=3,p.value=c,p.placeholder=r,p.setAttribute("aria-label",r);let l=g("button","","Post");return l.type="submit",l.disabled=n.busy,m.append(p,l),m.addEventListener("submit",async h=>{h.preventDefault();let w=p.value.trim();w&&await u(w)&&(p.value="")}),m}function E(r){let c=g("li","gala-comment");c.dataset.commentId=r.commentId,c.dataset.depth=r.depth;let u=g("p","gala-comment__meta");if(u.append(g("strong","",r.author?.displayName??"[deleted]")),r.createdAt){let m=g("time","",new Date(r.createdAt).toLocaleDateString());m.dateTime=r.createdAt,u.append(m)}if(c.append(u,g("p","gala-comment__body",r.deleted?"[deleted]":r.body)),!r.deleted){let m=g("div","gala-comment-actions");if(r.depth<se){let l=g("button","","Reply");l.type="button",l.dataset.replyComment=r.commentId,l.addEventListener("click",()=>{let h=c.querySelector(":scope > .gala-comment__form");if(h){h.remove();return}m.after(f(`Reply to ${r.author?.displayName??"this comment"}`,"",w=>d("comment.create",{articleId:a,parentCommentId:r.commentId,body:w})))}),m.append(l)}if(S.value&&r.author?.userId===S.value.id){let l=g("button","","Edit");l.type="button",l.dataset.editComment=r.commentId,l.addEventListener("click",()=>m.after(f("Edit your comment",r.body??"",w=>d("comment.edit",{articleId:a,commentId:r.commentId,body:w}))));let h=g("button","","Delete");h.type="button",h.dataset.deleteComment=r.commentId,h.addEventListener("click",()=>d("comment.delete",{articleId:a,commentId:r.commentId})),m.append(l,h)}else{let l=g("button","","Report");l.type="button",l.dataset.reportComment=r.commentId,l.addEventListener("click",()=>d("comment.report",{articleId:a,commentId:r.commentId,reason:"OTHER"})),m.append(l)}c.append(m)}if(r.replies.length){let m=g("ol","gala-comment-replies");m.append(...r.replies.map(E)),c.append(m)}return c}function i(){if(s&&(s.textContent=n.phase==="loading"?"Loading comments\u2026":n.phase==="unavailable"?"Comments are temporarily unavailable.":n.status),n.phase!=="ready"){e.replaceChildren();return}let r=g("section","gala-comments-island");if(r.setAttribute("aria-label","Comments"),r.append(g("p","gala-comments__heading",n.total===1?"1 comment":`${n.total} comments`)),S.value)r.append(f("Add a comment","",c=>d("comment.create",{articleId:a,body:c})));else{let c=g("p","gala-comments__prompt"),u=g("button","","Sign in to join the conversation");u.type="button",u.addEventListener("click",()=>U({kind:"comment"})),c.append(u),r.append(c)}if(!n.items.length)r.append(g("p","gala-comments__empty","No comments yet."));else{let c=g("ol","gala-comments");c.append(...ie(n.items).map(E)),r.append(c)}if(n.cursor){let c=g("button","gala-comments__more","Show more comments");c.type="button",c.disabled=n.busy,c.addEventListener("click",async()=>{n.busy=!0,n.status="Loading more comments\u2026",i();try{await o(n.cursor,{append:!0}),n.status=""}catch{n.status="More comments couldn\u2019t be loaded. Try again."}n.busy=!1,i()}),r.append(c)}e.replaceChildren(r)}window.addEventListener("gala-session-change",i),i(),o().then(()=>{n.phase="ready"}).catch(()=>{n.phase="unavailable"}).finally(i)}document.querySelectorAll("[data-gala-comments]").forEach(e=>{let t=e.closest("[data-engagement-url]")?.dataset.engagementUrl;t&&ce(e,t)});})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{color-scheme:light dark;--gala-color-background: light-dark(#fdfcfb, #131211);--gala-color-surface: light-dark(#f6f4f1, #1c1a18);--gala-color-raised: light-dark(#fffffe, #24211f);--gala-color-text: light-dark(#1a1817, #f2efec);--gala-color-muted: light-dark(#6b645e, #a8a099);--gala-color-border: light-dark(#968e86, #6b645e);--gala-color-hairline: light-dark(#e8e3dd, #2a2724);--gala-color-border-strong: light-dark(#c9c2ba, #46413d);--gala-color-accent: light-dark( var(--gala-accent-light, #8a4b2a), var(--gala-accent-dark, #e8a87c));--gala-font-body: ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;--gala-font-display: var(--gala-font-body);--gala-font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;--gala-text-xs: .78rem;--gala-text-sm: .875rem;--gala-text-base: clamp(1rem, .97rem + .15vw, 1.0625rem);--gala-text-lg: clamp(1.15rem, 1.09rem + .3vw, 1.3rem);--gala-text-xl: clamp(1.4rem, 1.28rem + .6vw, 1.75rem);--gala-text-2xl: clamp(1.75rem, 1.5rem + 1.2vw, 2.4rem);--gala-text-3xl: clamp(2.2rem, 1.7rem + 2.4vw, 3.4rem);--gala-leading-tight: 1.15;--gala-leading-body: 1.65;--gala-tracking-tight: -.018em;--gala-tracking-wide: .08em;--gala-weight-display: 640;--gala-step: 1rem;--gala-space-2xs: calc(var(--gala-step) * .25);--gala-space-xs: calc(var(--gala-step) * .5);--gala-space-sm: calc(var(--gala-step) * .75);--gala-space-md: var(--gala-step);--gala-space-lg: calc(var(--gala-step) * 1.75);--gala-space-xl: calc(var(--gala-step) * 3);--gala-space-page: clamp(1.25rem, 5vw, 4.5rem);--gala-radius-control: .625rem;--gala-radius-surface: 1rem;--gala-shadow-high: 0 1px 2px light-dark(rgb(26 24 23 / 5%), rgb(0 0 0 / 35%)), 0 12px 28px -12px light-dark(rgb(26 24 23 / 18%), rgb(0 0 0 / 60%));--gala-motion-fast: .12s;--gala-motion-base: .22s;--gala-ease: cubic-bezier(.2, 0, 0, 1);--gala-content-width: 72rem;--gala-measure: 68ch;--gala-widget-min-block-size: 8rem}:root[data-mode=light]{color-scheme:light;--gala-color-background: #fdfcfb;--gala-color-surface: #f6f4f1;--gala-color-raised: #fffffe;--gala-color-text: #1a1817;--gala-color-muted: #6b645e;--gala-color-border: #968e86;--gala-color-hairline: #e8e3dd;--gala-color-border-strong: #c9c2ba;--gala-color-accent: var(--gala-accent-light, #8a4b2a)}:root[data-mode=dark]{color-scheme:dark;--gala-color-background: #131211;--gala-color-surface: #1c1a18;--gala-color-raised: #24211f;--gala-color-text: #f2efec;--gala-color-muted: #a8a099;--gala-color-border: #6b645e;--gala-color-hairline: #2a2724;--gala-color-border-strong: #46413d;--gala-color-accent: var(--gala-accent-dark, #e8a87c)}:root[data-palette=ocean]{--gala-color-background: light-dark(#f8fbfd, #0b1a24);--gala-color-surface: light-dark(#eef4f9, #12252f);--gala-color-raised: light-dark(#ffffff, #172d39);--gala-color-text: light-dark(#12242f, #eaf3f8);--gala-color-muted: light-dark(#5a6f7d, #98aebb);--gala-color-border: light-dark(#7d8d99, #5f7d8c);--gala-color-hairline: light-dark(#dde7ee, #1d3644);--gala-color-border-strong: light-dark(#bdccd7, #33505f);--gala-color-accent: light-dark(#0f5f7a, #7fd0e8)}:root[data-palette=ocean][data-mode=light]{color-scheme:light;--gala-color-background: #f8fbfd;--gala-color-surface: #eef4f9;--gala-color-raised: #ffffff;--gala-color-text: #12242f;--gala-color-muted: #5a6f7d;--gala-color-border: #7d8d99;--gala-color-hairline: #dde7ee;--gala-color-border-strong: #bdccd7;--gala-color-accent: var(--gala-accent-light, #0f5f7a)}:root[data-palette=ocean][data-mode=dark]{color-scheme:dark;--gala-color-background: #0b1a24;--gala-color-surface: #12252f;--gala-color-raised: #172d39;--gala-color-text: #eaf3f8;--gala-color-muted: #98aebb;--gala-color-border: #5f7d8c;--gala-color-hairline: #1d3644;--gala-color-border-strong: #33505f;--gala-color-accent: var(--gala-accent-dark, #7fd0e8)}:root[data-palette=forest]{--gala-color-background: light-dark(#fbfcfa, #101410);--gala-color-surface: light-dark(#f0f4ee, #182018);--gala-color-raised: light-dark(#ffffff, #1f271f);--gala-color-text: light-dark(#18201a, #eef3ec);--gala-color-muted: light-dark(#5b665d, #9aa89b);--gala-color-border: light-dark(#849084, #5e685f);--gala-color-hairline: light-dark(#e2e9df, #232b23);--gala-color-border-strong: light-dark(#5b665d, #7d897e);--gala-color-accent: light-dark(#2f6b3f, #8fd3a1)}:root[data-palette=forest][data-mode=light]{color-scheme:light;--gala-color-background: #fbfcfa;--gala-color-surface: #f0f4ee;--gala-color-raised: #ffffff;--gala-color-text: #18201a;--gala-color-muted: #5b665d;--gala-color-border: #849084;--gala-color-hairline: #e2e9df;--gala-color-border-strong: #5b665d;--gala-color-accent: var(--gala-accent-light, #2f6b3f)}:root[data-palette=forest][data-mode=dark]{color-scheme:dark;--gala-color-background: #101410;--gala-color-surface: #182018;--gala-color-raised: #1f271f;--gala-color-text: #eef3ec;--gala-color-muted: #9aa89b;--gala-color-border: #5e685f;--gala-color-hairline: #232b23;--gala-color-border-strong: #7d897e;--gala-color-accent: var(--gala-accent-dark, #8fd3a1)}:root[data-palette=plum]{--gala-color-background: light-dark(#fdfbfd, #141016);--gala-color-surface: light-dark(#f5eff5, #1d1720);--gala-color-raised: light-dark(#ffffff, #241d28);--gala-color-text: light-dark(#1f1820, #f2ecf3);--gala-color-muted: light-dark(#665c69, #a99dac);--gala-color-border: light-dark(#948a95, #6b606d);--gala-color-hairline: light-dark(#ebe2ec, #281f2b);--gala-color-border-strong: light-dark(#665c69, #8b7f8d);--gala-color-accent: light-dark(#7a3d76, #dda6d8)}:root[data-palette=plum][data-mode=light]{color-scheme:light;--gala-color-background: #fdfbfd;--gala-color-surface: #f5eff5;--gala-color-raised: #ffffff;--gala-color-text: #1f1820;--gala-color-muted: #665c69;--gala-color-border: #948a95;--gala-color-hairline: #ebe2ec;--gala-color-border-strong: #665c69;--gala-color-accent: var(--gala-accent-light, #7a3d76)}:root[data-palette=plum][data-mode=dark]{color-scheme:dark;--gala-color-background: #141016;--gala-color-surface: #1d1720;--gala-color-raised: #241d28;--gala-color-text: #f2ecf3;--gala-color-muted: #a99dac;--gala-color-border: #6b606d;--gala-color-hairline: #281f2b;--gala-color-border-strong: #8b7f8d;--gala-color-accent: var(--gala-accent-dark, #dda6d8)}:root[data-theme=editorial]{--gala-font-body: ui-serif, "Iowan Old Style", Charter, "Source Serif 4", Georgia, serif;--gala-font-display: var(--gala-font-body);--gala-weight-display: 600;--gala-step: 1.1rem;--gala-leading-body: 1.7;--gala-radius-control: .5rem;--gala-radius-surface: .75rem;--gala-measure: 66ch;--gala-surface-border: var(--gala-color-hairline);--gala-surface-shadow: none}:root[data-theme=modern]{--gala-font-body: ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;--gala-font-display: var(--gala-font-body);--gala-weight-display: 640;--gala-step: 1rem;--gala-leading-body: 1.65;--gala-radius-control: .625rem;--gala-radius-surface: 1rem;--gala-measure: 70ch;--gala-surface-border: transparent;--gala-surface-shadow: var(--gala-shadow-high)}:root[data-theme=technical]{--gala-font-body: ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;--gala-font-display: var(--gala-font-body);--gala-weight-display: 660;--gala-tracking-tight: -.022em;--gala-step: .9rem;--gala-leading-body: 1.6;--gala-radius-control: .375rem;--gala-radius-surface: .5rem;--gala-measure: 74ch;--gala-surface-border: var(--gala-color-border-strong);--gala-surface-shadow: none}:root[data-theme=technical] .gala-eyebrow,:root[data-theme=technical] .gala-engagement dt,:root[data-theme=technical] .gala-comment__meta time{font-family:var(--gala-font-mono)}@media(prefers-reduced-motion:reduce){:root{--gala-motion-fast: 0ms;--gala-motion-base: 0ms}*,*:before,*:after{animation-duration:1ms!important;transition-duration:1ms!important}}:root[data-layout=portfolio]{--gala-content-width: 82rem}body{margin:0;padding:var(--gala-space-page);color:var(--gala-color-text);background:var(--gala-color-background);font-family:var(--gala-font-body);font-size:var(--gala-text-base);line-height:var(--gala-leading-body);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}.gala-skip-link{position:fixed;inset-block-start:1rem;inset-inline-start:1rem;z-index:100;padding:.75rem 1rem;color:#fff;background:var(--gala-color-accent);border-radius:999px;transform:translateY(-200%)}.gala-skip-link:focus{transform:translateY(0)}.gala-site-name{color:var(--gala-color-text);font-family:var(--gala-font-display);font-size:var(--gala-text-lg);font-weight:var(--gala-weight-display);letter-spacing:var(--gala-tracking-tight);text-decoration:none}.gala-header-link{color:var(--gala-color-text);font-size:var(--gala-text-sm);text-decoration:none}.gala-account-control{display:inline-flex;align-items:center;gap:.5rem;min-block-size:2.75rem;padding-inline:.9rem;border-radius:999px}.gala-account-control svg{inline-size:1rem;block-size:1rem}.gala-article{max-inline-size:76rem;margin-inline:auto}.gala-article-header{padding-block:clamp(3rem,9vw,8rem)}.gala-article-header h1{max-inline-size:12ch;font-size:clamp(3.25rem,9vw,7.5rem);line-height:.92;letter-spacing:-.055em}.gala-byline{color:var(--gala-color-muted);font-size:var(--gala-text-sm)}.gala-markdown{max-inline-size:42rem;margin-inline:auto;font-size:clamp(1.1rem,1rem + .35vw,1.3rem)}.gala-markdown :where(h2){margin-block-start:clamp(3rem,7vw,5rem);font-size:clamp(2.2rem,5vw,3.8rem)}.gala-markdown :where(img,video){max-inline-size:100%;block-size:auto}@media(max-width:47.99rem){.gala-header-link{display:none}.gala-site-header__actions{gap:.25rem}}:where(h1,h2,h3,h4){font-family:var(--gala-font-display);font-weight:var(--gala-weight-display);line-height:var(--gala-leading-tight);letter-spacing:var(--gala-tracking-tight);text-wrap:balance;margin-block:0 var(--gala-space-sm)}:where(h1){font-size:var(--gala-text-3xl)}:where(h2){font-size:var(--gala-text-2xl)}:where(h3){font-size:var(--gala-text-xl)}:where(h4){font-size:var(--gala-text-lg)}:where(p,ul,ol,blockquote){margin-block:0 var(--gala-space-md)}:where(p){text-wrap:pretty}blockquote{margin-inline:0;padding-inline-start:var(--gala-space-md);border-inline-start:2px solid var(--gala-color-accent);color:var(--gala-color-muted)}:where(code,kbd,samp){font-family:var(--gala-font-mono);font-size:.9em}:where(pre){padding:var(--gala-space-md);overflow-x:auto;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}:where(hr){border:0;border-block-start:1px solid var(--gala-color-hairline);margin-block:var(--gala-space-xl)}:where(table){border-collapse:collapse;inline-size:100%}:where(th,td){padding:var(--gala-space-xs) var(--gala-space-sm);border-block-end:1px solid var(--gala-color-hairline);text-align:start}.gala-eyebrow,.gala-comment__meta time,.gala-comment__edited{font-size:var(--gala-text-xs);letter-spacing:var(--gala-tracking-wide);text-transform:uppercase;color:var(--gala-color-muted)}.gala-site-header{position:sticky;inset-block-start:0;z-index:10;display:flex;align-items:center;justify-content:space-between;gap:var(--gala-space-md);max-inline-size:var(--gala-content-width);margin-inline:auto;margin-block-end:var(--gala-space-xl);padding-block:var(--gala-space-xs);background:color-mix(in srgb,var(--gala-color-background) 88%,transparent);backdrop-filter:blur(8px);border-block-end:1px solid var(--gala-color-hairline)}@supports not (backdrop-filter: blur(8px)){.gala-site-header{background:var(--gala-color-background)}}.gala-site-header__actions{display:flex;gap:.5rem}.gala-icon-control{display:inline-grid;inline-size:2.75rem;block-size:2.75rem;box-sizing:border-box;padding:.6rem;place-items:center;color:inherit;border:.125rem solid transparent;border-radius:50%}.gala-icon-control{transition:background var(--gala-motion-fast) var(--gala-ease),color var(--gala-motion-fast) var(--gala-ease);color:var(--gala-color-muted)}.gala-icon-control:hover{background:var(--gala-color-surface);color:var(--gala-color-text)}.gala-icon-control:focus-visible{outline:.1875rem solid currentcolor;outline-offset:.1875rem}.gala-icon-control svg{inline-size:100%;block-size:100%;fill:none;stroke:currentcolor}.gala-dialog{inline-size:min(42rem,calc(100% - 2rem));max-block-size:calc(100% - 2rem);box-sizing:border-box;padding:1.5rem;color:var(--gala-color-text);background:var(--gala-color-background);border:1px solid var(--gala-surface-border);border-radius:var(--gala-radius-surface);box-shadow:var(--gala-shadow-high)}.gala-dialog::backdrop{background:#000000a6}.gala-account-dialog{inline-size:min(26rem,calc(100% - 2rem))}.gala-account-dialog iframe{display:block;inline-size:100%;block-size:7rem;border:0;overflow:hidden}.gala-dialog{display:grid;gap:var(--gala-space-sm)}.gala-dialog__close{justify-self:end;margin:0}.gala-dialog>h2{margin-block:0}main,.gala-page-footer{max-inline-size:var(--gala-content-width);margin-inline:auto}a{color:var(--gala-color-accent)}button{min-block-size:2.75rem;padding-inline:var(--gala-space-md);color:inherit;background:var(--gala-color-surface);border:1px solid var(--gala-color-border);border-radius:var(--gala-radius-control);font:inherit;font-size:var(--gala-text-sm);cursor:pointer;transition:background var(--gala-motion-fast) var(--gala-ease),border-color var(--gala-motion-fast) var(--gala-ease)}button:hover{border-color:var(--gala-color-border-strong);background:var(--gala-color-raised)}button[disabled]{opacity:.55;cursor:not-allowed}button:focus-visible{outline:.1875rem solid currentcolor;outline-offset:.1875rem}.gala-button,.gala-badge,.gala-tag-chip{display:inline-flex;align-items:center;min-block-size:2.75rem;border:1px solid var(--gala-color-border);border-radius:var(--gala-radius-control);font-size:var(--gala-text-sm)}.gala-badge,.gala-tag-chip{padding-inline:.75rem}.gala-tag-chip{text-decoration:none}.gala-section-heading{margin-block:3rem 1rem}.gala-section-heading>:where(h2,p){margin-block:0 .5rem}.gala-hero{display:grid;gap:1.5rem;align-items:center}.gala-hero__image{inline-size:100%;block-size:auto;border-radius:var(--gala-radius-control)}.gala-page-content{max-inline-size:var(--gala-measure)}.gala-page-content :where(img,video){max-inline-size:100%;block-size:auto}.gala-author-profile{display:flex;gap:1rem;padding:1rem;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}.gala-author-profile img{border-radius:50%;object-fit:cover}.gala-social-links,.gala-page-footer ul,.gala-share ul{display:flex;flex-wrap:wrap;gap:1rem;padding:0;list-style:none}.gala-card-index{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:1rem;padding:0;list-style:none}[data-layout=portfolio] .gala-card-index{grid-template-columns:repeat(auto-fit,minmax(min(100%,22rem),1fr));gap:1.5rem}[data-layout=portfolio] .gala-card{padding:clamp(1.25rem,3vw,2rem)}.gala-card{block-size:100%;box-sizing:border-box;padding:var(--gala-space-lg);background:var(--gala-color-surface);border:1px solid var(--gala-surface-border);border-radius:var(--gala-radius-surface);box-shadow:var(--gala-surface-shadow)}.gala-card{transition:transform var(--gala-motion-base) var(--gala-ease),box-shadow var(--gala-motion-base) var(--gala-ease)}.gala-card-index li:hover .gala-card{transform:translateY(-2px);box-shadow:var(--gala-shadow-high)}.gala-stats-graph{min-block-size:var(--gala-widget-min-block-size);margin-inline:0}.gala-stats-graph svg{inline-size:100%;block-size:8rem}.gala-stats-graph polyline{fill:none;stroke:var(--gala-color-accent);stroke-width:2}.gala-loading{display:grid;min-block-size:var(--gala-widget-min-block-size);place-items:center}.gala-loading__indicator{inline-size:2rem;block-size:2rem;border:.25rem solid var(--gala-color-border);border-block-start-color:var(--gala-color-accent);border-radius:50%;animation:gala-spin .8s linear infinite}.gala-pagination{display:flex;justify-content:space-between;gap:1rem}.gala-search{display:grid;grid-template-columns:1fr auto;gap:.5rem}.gala-search label{grid-column:1 / -1}.gala-search input{min-block-size:2.75rem;font:inherit}.gala-search-results{display:grid;gap:1rem;padding:0;list-style:none}.gala-search-results article{padding:1rem;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}.gala-search-results h2{margin-block-start:0}.gala-toc{padding:1rem;background:var(--gala-color-surface)}.gala-share__fallback{inline-size:min(100%,36rem)}.gala-engagement__placeholder{display:grid;min-block-size:var(--gala-widget-min-block-size);align-content:center;margin:0}.gala-engagement-profile>*{margin-block:0 .5rem}.gala-engagement-actions{display:grid;gap:1rem;margin-block-start:1.5rem}.gala-engagement-actions[hidden]{display:none}.gala-account-dialog__invite{margin-block:0 1rem;max-inline-size:34rem}.gala-account-dialog__invite[hidden]{display:none}.gala-engagement-actions form,[data-comment-editor]{display:grid;gap:.5rem;max-inline-size:42rem}.gala-engagement-actions label{display:grid;gap:.5rem}.gala-engagement-actions textarea,[data-comment-editor] textarea{min-block-size:7rem;padding:.75rem;font:inherit}.gala-page-footer{margin-block-start:4rem;padding-block-start:1rem;border-block-start:.125rem solid var(--gala-color-border)}.gala-error-page{min-block-size:60vh;display:grid;align-content:center;justify-items:start}.gala-code-block{position:relative}.gala-code-block>button{position:absolute;inset-block-start:.5rem;inset-inline-end:.5rem;background:var(--gala-color-background)}.gala-code-block pre{overflow:auto;padding:3.5rem 1rem 1rem;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}.gala-embed{display:grid;place-items:center;gap:.75rem;box-sizing:border-box;inline-size:100%;margin-inline:0;padding:1rem;overflow:hidden;background:var(--gala-color-surface);border:.125rem solid var(--gala-color-border);border-radius:var(--gala-radius-control)}.gala-embed--youtube{aspect-ratio:16 / 9}.gala-embed--codepen{min-block-size:25rem}.gala-embed--gist,.gala-embed--x{min-block-size:8rem}.gala-embed iframe{inline-size:100%;block-size:100%;border:0}.gala-embed__icon{inline-size:2rem;block-size:2rem;background:center / contain no-repeat}.gala-embed--youtube .gala-embed__icon{background-image:url(../assets/embed-youtube.svg)}.gala-embed--codepen .gala-embed__icon{background-image:url(../assets/embed-codepen.svg)}.gala-embed--gist .gala-embed__icon{background-image:url(../assets/embed-gist.svg)}.gala-embed--x .gala-embed__icon{background-image:url(../assets/embed-x.svg)}.shiki,.shiki span{color:var(--shiki-light);background-color:var(--shiki-light-bg)}:root[data-mode=dark] .shiki,:root[data-mode=dark] .shiki span{color:var(--shiki-dark);background-color:var(--shiki-dark-bg)}@media(prefers-color-scheme:dark){:root[data-mode=system] .shiki,:root[data-mode=system] .shiki span{color:var(--shiki-dark);background-color:var(--shiki-dark-bg)}}.gala-visually-hidden{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.gala-contact form{display:grid;gap:1rem;max-inline-size:42rem}.gala-contact label{display:grid;gap:.35rem}.gala-contact input,.gala-contact textarea{font:inherit;padding:.65rem}.gala-contact textarea{min-block-size:12rem;resize:vertical}@keyframes gala-spin{to{transform:rotate(1turn)}}@media(min-width:48rem){.gala-hero{grid-template-columns:minmax(0,1fr) minmax(18rem,1fr)}}@media(prefers-reduced-motion:no-preference){@view-transition{navigation:auto}}.gala-comments-island{margin-block-start:var(--gala-space-xl)}.gala-comments__heading{font-size:var(--gala-text-lg);margin-block-end:var(--gala-space-md)}.gala-comments,.gala-comment-replies{list-style:none;margin:0;padding:0}.gala-comment-replies{margin-block-start:var(--gala-space-sm);margin-inline-start:var(--gala-space-xs);padding-inline-start:var(--gala-space-md);border-inline-start:1px solid var(--gala-color-hairline)}.gala-comment{padding-block:var(--gala-space-sm)}.gala-comment+.gala-comment{border-block-start:1px solid var(--gala-color-hairline)}.gala-comment-replies .gala-comment+.gala-comment{border-block-start:0}.gala-comment__meta{display:flex;align-items:baseline;gap:var(--gala-space-xs);margin-block-end:var(--gala-space-2xs);font-size:var(--gala-text-sm)}.gala-comment__meta strong{font-weight:600}.gala-comment__pending{font-size:var(--gala-text-xs);color:var(--gala-color-muted)}.gala-comment__body{margin-block:0;overflow-wrap:anywhere}.gala-comment-actions{display:flex;gap:var(--gala-space-2xs);margin-block-start:var(--gala-space-2xs);opacity:.72;transition:opacity var(--gala-motion-fast) var(--gala-ease)}.gala-comment:hover>.gala-comment-actions,.gala-comment:focus-within>.gala-comment-actions{opacity:1}.gala-comment-actions button{min-block-size:2rem;padding-inline:var(--gala-space-xs);font-size:var(--gala-text-xs);background:transparent;border-color:transparent;color:var(--gala-color-muted)}.gala-comment-actions button:hover{color:var(--gala-color-text);background:var(--gala-color-surface)}.gala-comment__form{display:grid;gap:var(--gala-space-xs);margin-block:var(--gala-space-sm)}.gala-comment__form textarea{inline-size:100%;box-sizing:border-box;padding:var(--gala-space-sm);color:inherit;background:var(--gala-color-raised);border:1px solid var(--gala-color-border);border-radius:var(--gala-radius-control);font:inherit;font-size:var(--gala-text-sm);line-height:var(--gala-leading-body);resize:vertical}.gala-comment__form textarea:focus-visible{outline:2px solid var(--gala-color-accent);outline-offset:1px}.gala-comment__controls{display:flex;gap:var(--gala-space-xs)}.gala-comments__status,.gala-comments__empty,.gala-comments__prompt{color:var(--gala-color-muted);font-size:var(--gala-text-sm)}.gala-comments__more{margin-block-start:var(--gala-space-md)}.gala-engagement{min-block-size:var(--gala-widget-min-block-size);max-inline-size:var(--gala-content-width);margin-block-start:var(--gala-space-xl);padding-block-start:var(--gala-space-lg);border-block-start:1px solid var(--gala-color-border)}.gala-engagement-profile{display:flex;align-items:baseline;gap:var(--gala-space-sm);padding:0;background:none}.gala-engagement-profile h2{font-size:var(--gala-text-lg);margin:0}.gala-engagement-profile p{margin:0;color:var(--gala-color-muted);font-size:var(--gala-text-sm)}.gala-engagement dl{display:flex;flex-wrap:wrap;gap:var(--gala-space-lg);margin-block:var(--gala-space-md)}.gala-engagement dl>div{display:flex;flex-direction:column-reverse;gap:.1rem}.gala-engagement dt{font-size:var(--gala-text-xs);letter-spacing:var(--gala-tracking-wide);text-transform:uppercase;color:var(--gala-color-muted)}.gala-engagement dd{margin:0;font-size:var(--gala-text-lg);font-variant-numeric:tabular-nums}.gala-reaction-actions{display:flex;flex-wrap:wrap;gap:var(--gala-space-2xs)}.gala-engagement-actions{display:grid;gap:var(--gala-space-sm);justify-items:start}.gala-engagement-actions>[data-follow-article]{inline-size:auto}.gala-share{margin-block-start:var(--gala-space-lg)}.gala-engagement{position:relative;margin-block-start:clamp(4rem,9vw,8rem)}.gala-conversation{max-width:var(--gala-measure);margin-inline:auto;padding-block:clamp(3rem,7vw,6rem)}.gala-conversation__header{margin-block-end:var(--gala-space-xl)}.gala-conversation__header h2{margin:0;font:var(--gala-weight-display) var(--gala-text-2xl)/var(--gala-leading-tight) var(--gala-font-display);letter-spacing:var(--gala-tracking-tight)}.gala-action-rail{position:sticky;z-index:4;inset-block-start:6rem;float:left;width:3.25rem;margin-inline-start:max(-5rem,calc((var(--gala-measure) - 100%) / 2 - 4rem));display:grid;gap:.5rem;padding:.5rem;border:1px solid var(--gala-color-hairline);border-radius:999px;background:color-mix(in srgb,var(--gala-color-background) 88%,transparent);box-shadow:var(--gala-shadow-high);backdrop-filter:blur(16px)}.gala-action-rail button,.gala-action-rail a{inline-size:2.25rem;min-block-size:2.25rem;display:grid;place-items:center;padding:0;border:0;border-radius:50%;color:var(--gala-color-muted);background:transparent;font:600 var(--gala-text-xs)/1 var(--gala-font-body);text-decoration:none;cursor:pointer}.gala-action-rail button:hover,.gala-action-rail a:hover,.gala-action-rail button[aria-pressed=true]{color:var(--gala-color-background);background:var(--gala-color-accent)}.gala-action-rail .gala-reaction-actions,.gala-action-rail .gala-share{display:contents}.gala-action-rail .gala-share__fallback{position:fixed;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%)}.gala-action-rail .gala-share__fallback--visible{z-index:10;inset:auto 1rem 1rem;inline-size:min(32rem,calc(100vw - 2rem));block-size:auto;padding:.75rem;overflow:visible;clip-path:none}@media(max-width:54rem){.gala-action-rail{inset-block-start:auto;inset-block-end:1rem;float:none;box-sizing:border-box;width:calc(100% - 2rem);max-width:calc(100% - 2rem);margin:0 auto;grid-auto-flow:column;overflow-x:auto}.gala-conversation{padding-block-start:var(--gala-space-xl)}}
|
|
1
|
+
:root{color-scheme:light dark;--gala-color-background: light-dark(#fdfcfb, #131211);--gala-color-surface: light-dark(#f6f4f1, #1c1a18);--gala-color-raised: light-dark(#fffffe, #24211f);--gala-color-text: light-dark(#1a1817, #f2efec);--gala-color-muted: light-dark(#6b645e, #a8a099);--gala-color-border: light-dark(#968e86, #6b645e);--gala-color-hairline: light-dark(#e8e3dd, #2a2724);--gala-color-border-strong: light-dark(#c9c2ba, #46413d);--gala-color-accent: light-dark( var(--gala-accent-light, #8a4b2a), var(--gala-accent-dark, #e8a87c));--gala-font-body: ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;--gala-font-display: var(--gala-font-body);--gala-font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;--gala-text-xs: .78rem;--gala-text-sm: .875rem;--gala-text-base: clamp(1rem, .97rem + .15vw, 1.0625rem);--gala-text-lg: clamp(1.15rem, 1.09rem + .3vw, 1.3rem);--gala-text-xl: clamp(1.4rem, 1.28rem + .6vw, 1.75rem);--gala-text-2xl: clamp(1.75rem, 1.5rem + 1.2vw, 2.4rem);--gala-text-3xl: clamp(2.2rem, 1.7rem + 2.4vw, 3.4rem);--gala-leading-tight: 1.15;--gala-leading-body: 1.65;--gala-tracking-tight: -.018em;--gala-tracking-wide: .08em;--gala-weight-display: 640;--gala-step: 1rem;--gala-space-2xs: calc(var(--gala-step) * .25);--gala-space-xs: calc(var(--gala-step) * .5);--gala-space-sm: calc(var(--gala-step) * .75);--gala-space-md: var(--gala-step);--gala-space-lg: calc(var(--gala-step) * 1.75);--gala-space-xl: calc(var(--gala-step) * 3);--gala-space-page: clamp(1.25rem, 5vw, 4.5rem);--gala-radius-control: .625rem;--gala-radius-surface: 1rem;--gala-shadow-high: 0 1px 2px light-dark(rgb(26 24 23 / 5%), rgb(0 0 0 / 35%)), 0 12px 28px -12px light-dark(rgb(26 24 23 / 18%), rgb(0 0 0 / 60%));--gala-motion-fast: .12s;--gala-motion-base: .22s;--gala-ease: cubic-bezier(.2, 0, 0, 1);--gala-content-width: 72rem;--gala-measure: 68ch;--gala-widget-min-block-size: 8rem}:root[data-mode=light]{color-scheme:light;--gala-color-background: #fdfcfb;--gala-color-surface: #f6f4f1;--gala-color-raised: #fffffe;--gala-color-text: #1a1817;--gala-color-muted: #6b645e;--gala-color-border: #968e86;--gala-color-hairline: #e8e3dd;--gala-color-border-strong: #c9c2ba;--gala-color-accent: var(--gala-accent-light, #8a4b2a)}:root[data-mode=dark]{color-scheme:dark;--gala-color-background: #131211;--gala-color-surface: #1c1a18;--gala-color-raised: #24211f;--gala-color-text: #f2efec;--gala-color-muted: #a8a099;--gala-color-border: #6b645e;--gala-color-hairline: #2a2724;--gala-color-border-strong: #46413d;--gala-color-accent: var(--gala-accent-dark, #e8a87c)}:root[data-palette=ocean]{--gala-color-background: light-dark(#f8fbfd, #0b1a24);--gala-color-surface: light-dark(#eef4f9, #12252f);--gala-color-raised: light-dark(#ffffff, #172d39);--gala-color-text: light-dark(#12242f, #eaf3f8);--gala-color-muted: light-dark(#5a6f7d, #98aebb);--gala-color-border: light-dark(#7d8d99, #5f7d8c);--gala-color-hairline: light-dark(#dde7ee, #1d3644);--gala-color-border-strong: light-dark(#bdccd7, #33505f);--gala-color-accent: light-dark(#0f5f7a, #7fd0e8)}:root[data-palette=ocean][data-mode=light]{color-scheme:light;--gala-color-background: #f8fbfd;--gala-color-surface: #eef4f9;--gala-color-raised: #ffffff;--gala-color-text: #12242f;--gala-color-muted: #5a6f7d;--gala-color-border: #7d8d99;--gala-color-hairline: #dde7ee;--gala-color-border-strong: #bdccd7;--gala-color-accent: var(--gala-accent-light, #0f5f7a)}:root[data-palette=ocean][data-mode=dark]{color-scheme:dark;--gala-color-background: #0b1a24;--gala-color-surface: #12252f;--gala-color-raised: #172d39;--gala-color-text: #eaf3f8;--gala-color-muted: #98aebb;--gala-color-border: #5f7d8c;--gala-color-hairline: #1d3644;--gala-color-border-strong: #33505f;--gala-color-accent: var(--gala-accent-dark, #7fd0e8)}:root[data-palette=forest]{--gala-color-background: light-dark(#fbfcfa, #101410);--gala-color-surface: light-dark(#f0f4ee, #182018);--gala-color-raised: light-dark(#ffffff, #1f271f);--gala-color-text: light-dark(#18201a, #eef3ec);--gala-color-muted: light-dark(#5b665d, #9aa89b);--gala-color-border: light-dark(#849084, #5e685f);--gala-color-hairline: light-dark(#e2e9df, #232b23);--gala-color-border-strong: light-dark(#5b665d, #7d897e);--gala-color-accent: light-dark(#2f6b3f, #8fd3a1)}:root[data-palette=forest][data-mode=light]{color-scheme:light;--gala-color-background: #fbfcfa;--gala-color-surface: #f0f4ee;--gala-color-raised: #ffffff;--gala-color-text: #18201a;--gala-color-muted: #5b665d;--gala-color-border: #849084;--gala-color-hairline: #e2e9df;--gala-color-border-strong: #5b665d;--gala-color-accent: var(--gala-accent-light, #2f6b3f)}:root[data-palette=forest][data-mode=dark]{color-scheme:dark;--gala-color-background: #101410;--gala-color-surface: #182018;--gala-color-raised: #1f271f;--gala-color-text: #eef3ec;--gala-color-muted: #9aa89b;--gala-color-border: #5e685f;--gala-color-hairline: #232b23;--gala-color-border-strong: #7d897e;--gala-color-accent: var(--gala-accent-dark, #8fd3a1)}:root[data-palette=plum]{--gala-color-background: light-dark(#fdfbfd, #141016);--gala-color-surface: light-dark(#f5eff5, #1d1720);--gala-color-raised: light-dark(#ffffff, #241d28);--gala-color-text: light-dark(#1f1820, #f2ecf3);--gala-color-muted: light-dark(#665c69, #a99dac);--gala-color-border: light-dark(#948a95, #6b606d);--gala-color-hairline: light-dark(#ebe2ec, #281f2b);--gala-color-border-strong: light-dark(#665c69, #8b7f8d);--gala-color-accent: light-dark(#7a3d76, #dda6d8)}:root[data-palette=plum][data-mode=light]{color-scheme:light;--gala-color-background: #fdfbfd;--gala-color-surface: #f5eff5;--gala-color-raised: #ffffff;--gala-color-text: #1f1820;--gala-color-muted: #665c69;--gala-color-border: #948a95;--gala-color-hairline: #ebe2ec;--gala-color-border-strong: #665c69;--gala-color-accent: var(--gala-accent-light, #7a3d76)}:root[data-palette=plum][data-mode=dark]{color-scheme:dark;--gala-color-background: #141016;--gala-color-surface: #1d1720;--gala-color-raised: #241d28;--gala-color-text: #f2ecf3;--gala-color-muted: #a99dac;--gala-color-border: #6b606d;--gala-color-hairline: #281f2b;--gala-color-border-strong: #8b7f8d;--gala-color-accent: var(--gala-accent-dark, #dda6d8)}:root[data-theme=editorial]{--gala-font-body: ui-serif, "Iowan Old Style", Charter, "Source Serif 4", Georgia, serif;--gala-font-display: var(--gala-font-body);--gala-weight-display: 600;--gala-step: 1.1rem;--gala-leading-body: 1.7;--gala-radius-control: .5rem;--gala-radius-surface: .75rem;--gala-measure: 66ch;--gala-surface-border: var(--gala-color-hairline);--gala-surface-shadow: none}:root[data-theme=modern]{--gala-font-body: ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;--gala-font-display: var(--gala-font-body);--gala-weight-display: 640;--gala-step: 1rem;--gala-leading-body: 1.65;--gala-radius-control: .625rem;--gala-radius-surface: 1rem;--gala-measure: 70ch;--gala-surface-border: transparent;--gala-surface-shadow: var(--gala-shadow-high)}:root[data-theme=technical]{--gala-font-body: ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;--gala-font-display: var(--gala-font-body);--gala-weight-display: 660;--gala-tracking-tight: -.022em;--gala-step: .9rem;--gala-leading-body: 1.6;--gala-radius-control: .375rem;--gala-radius-surface: .5rem;--gala-measure: 74ch;--gala-surface-border: var(--gala-color-border-strong);--gala-surface-shadow: none}:root[data-theme=technical] .gala-eyebrow,:root[data-theme=technical] .gala-engagement dt,:root[data-theme=technical] .gala-comment__meta time{font-family:var(--gala-font-mono)}@media(prefers-reduced-motion:reduce){:root{--gala-motion-fast: 0ms;--gala-motion-base: 0ms;scroll-behavior:auto}*,*:before,*:after{animation-duration:1ms!important;transition-duration:1ms!important}}:root[data-layout=portfolio]{--gala-content-width: 82rem}html{scroll-behavior:smooth}body{margin:0;padding:var(--gala-space-page);color:var(--gala-color-text);background:var(--gala-color-background);font-family:var(--gala-font-body);font-size:var(--gala-text-base);line-height:var(--gala-leading-body);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}.gala-skip-link{position:fixed;inset-block-start:1rem;inset-inline-start:1rem;z-index:100;padding:.75rem 1rem;color:#fff;background:var(--gala-color-accent);border-radius:999px;transform:translateY(-200%)}.gala-skip-link:focus{transform:translateY(0)}.gala-site-name{color:var(--gala-color-text);font-family:var(--gala-font-display);font-size:var(--gala-text-lg);font-weight:var(--gala-weight-display);letter-spacing:var(--gala-tracking-tight);text-decoration:none;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gala-header-link{color:var(--gala-color-text);font-size:var(--gala-text-sm);text-decoration:none}.gala-account-control{display:inline-flex;align-items:center;gap:.5rem;min-block-size:2.75rem;padding-inline:.9rem;border-radius:999px}.gala-account-control svg{inline-size:1rem;block-size:1rem}.gala-article{max-inline-size:76rem;margin-inline:auto}.gala-page--article .gala-site-header,.gala-page--article main,.gala-page--article .gala-page-footer{max-inline-size:112rem}.gala-page--article .gala-article,.gala-page--article .gala-engagement{max-inline-size:none}.gala-article-header{padding-block:clamp(2.5rem,5vw,5rem)}.gala-article-header h1{max-inline-size:12ch;font-size:clamp(2.5rem,7vw,5.25rem);line-height:.96;letter-spacing:-.055em}.gala-byline{color:var(--gala-color-muted);font-size:var(--gala-text-sm)}.gala-markdown{max-inline-size:42rem;margin-inline:auto;font-size:clamp(1.1rem,1rem + .35vw,1.3rem)}.gala-markdown :where(h2){margin-block-start:clamp(3rem,7vw,5rem);font-size:clamp(2.2rem,5vw,3.8rem)}.gala-markdown :where(h2,h3,h4,h5,h6){scroll-margin-block-start:7rem}.gala-markdown :where(img,video){max-inline-size:100%;block-size:auto}.gala-markdown :where(pre){box-sizing:border-box;max-inline-size:100%;overflow-x:auto;scrollbar-color:var(--gala-color-border-strong) var(--gala-color-surface)}:where(h1,h2,h3,h4){font-family:var(--gala-font-display);font-weight:var(--gala-weight-display);line-height:var(--gala-leading-tight);letter-spacing:var(--gala-tracking-tight);text-wrap:balance;margin-block:0 var(--gala-space-sm)}:where(h1){font-size:var(--gala-text-3xl)}:where(h2){font-size:var(--gala-text-2xl)}:where(h3){font-size:var(--gala-text-xl)}:where(h4){font-size:var(--gala-text-lg)}:where(p,ul,ol,blockquote){margin-block:0 var(--gala-space-md)}:where(p){text-wrap:pretty}blockquote{margin-inline:0;padding-inline-start:var(--gala-space-md);border-inline-start:2px solid var(--gala-color-accent);color:var(--gala-color-muted)}:where(code,kbd,samp){font-family:var(--gala-font-mono);font-size:.9em}:where(pre){padding:var(--gala-space-md);overflow-x:auto;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}:where(hr){border:0;border-block-start:1px solid var(--gala-color-hairline);margin-block:var(--gala-space-xl)}:where(table){border-collapse:collapse;inline-size:100%}:where(th,td){padding:var(--gala-space-xs) var(--gala-space-sm);border-block-end:1px solid var(--gala-color-hairline);text-align:start}.gala-eyebrow,.gala-comment__meta time,.gala-comment__edited{font-size:var(--gala-text-xs);letter-spacing:var(--gala-tracking-wide);text-transform:uppercase;color:var(--gala-color-muted)}.gala-site-header{position:sticky;inset-block-start:0;z-index:10;display:flex;align-items:center;justify-content:space-between;gap:var(--gala-space-md);max-inline-size:var(--gala-content-width);margin-inline:auto;margin-block-end:var(--gala-space-xl);padding-block:var(--gala-space-xs);background:color-mix(in srgb,var(--gala-color-background) 88%,transparent);backdrop-filter:blur(8px);border-block-end:1px solid var(--gala-color-hairline)}@supports not (backdrop-filter: blur(8px)){.gala-site-header{background:var(--gala-color-background)}}.gala-site-header__actions{display:flex;align-items:center;gap:.5rem}.gala-header-link{display:inline-flex;align-items:center;min-block-size:2.75rem}.gala-icon-control{display:inline-grid;inline-size:2.75rem;block-size:2.75rem;box-sizing:border-box;padding:.6rem;place-items:center;color:inherit;border:.125rem solid transparent;border-radius:50%}.gala-icon-control{transition:background var(--gala-motion-fast) var(--gala-ease),color var(--gala-motion-fast) var(--gala-ease);color:var(--gala-color-muted)}.gala-icon-control:hover{background:var(--gala-color-surface);color:var(--gala-color-text)}.gala-icon-control:focus-visible{outline:.1875rem solid currentcolor;outline-offset:.1875rem}.gala-icon-control svg{inline-size:100%;block-size:100%;fill:none;stroke:currentcolor}.gala-dialog{inline-size:min(42rem,calc(100% - 2rem));max-block-size:calc(100% - 2rem);box-sizing:border-box;padding:1.5rem;color:var(--gala-color-text);background:var(--gala-color-background);border:1px solid var(--gala-surface-border);border-radius:var(--gala-radius-surface);box-shadow:var(--gala-shadow-high)}.gala-dialog::backdrop{background:#000000a6}.gala-account-dialog{inline-size:min(26rem,calc(100% - 2rem))}.gala-account-dialog iframe{display:block;inline-size:100%;block-size:7rem;border:0;overflow:hidden}.gala-dialog{display:grid;gap:var(--gala-space-sm)}.gala-dialog:not([open]){display:none}.gala-dialog__close{justify-self:end;margin:0}.gala-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:var(--gala-space-md)}.gala-dialog__header h2{margin:0}main,.gala-page-footer{max-inline-size:var(--gala-content-width);margin-inline:auto}a{color:var(--gala-color-accent)}button{min-block-size:2.75rem;padding-inline:var(--gala-space-md);color:inherit;background:var(--gala-color-surface);border:1px solid var(--gala-color-border);border-radius:var(--gala-radius-control);font:inherit;font-size:var(--gala-text-sm);cursor:pointer;transition:background var(--gala-motion-fast) var(--gala-ease),border-color var(--gala-motion-fast) var(--gala-ease)}button:hover{border-color:var(--gala-color-border-strong);background:var(--gala-color-raised)}button[disabled]{opacity:.55;cursor:not-allowed}button:focus-visible{outline:.1875rem solid currentcolor;outline-offset:.1875rem}.gala-button,.gala-badge,.gala-tag-chip{display:inline-flex;align-items:center;min-block-size:2.75rem;border:1px solid var(--gala-color-border);border-radius:var(--gala-radius-control);font-size:var(--gala-text-sm)}.gala-badge,.gala-tag-chip{padding-inline:.75rem}.gala-tag-chip{text-decoration:none}.gala-section-heading{margin-block:3rem 1rem}.gala-section-heading>:where(h2,p){margin-block:0 .5rem}.gala-hero{display:grid;gap:1.5rem;align-items:center}.gala-hero__image{inline-size:100%;block-size:auto;border-radius:var(--gala-radius-control)}.gala-page-content{max-inline-size:var(--gala-measure)}.gala-page-content :where(img,video){max-inline-size:100%;block-size:auto}.gala-author-profile{display:flex;gap:1rem;padding:1rem;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}.gala-author-profile img{border-radius:50%;object-fit:cover}.gala-social-links,.gala-page-footer ul,.gala-share ul{display:flex;flex-wrap:wrap;gap:1rem;padding:0;list-style:none}.gala-card-index{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:1rem;padding:0;list-style:none}[data-layout=portfolio] .gala-card-index{grid-template-columns:repeat(auto-fit,minmax(min(100%,22rem),1fr));gap:1.5rem}[data-layout=portfolio] .gala-card{padding:clamp(1.25rem,3vw,2rem)}.gala-card{block-size:100%;box-sizing:border-box;padding:var(--gala-space-lg);background:var(--gala-color-surface);border:1px solid var(--gala-surface-border);border-radius:var(--gala-radius-surface);box-shadow:var(--gala-surface-shadow)}.gala-card{transition:transform var(--gala-motion-base) var(--gala-ease),box-shadow var(--gala-motion-base) var(--gala-ease)}.gala-card-index li:hover .gala-card{transform:translateY(-2px);box-shadow:var(--gala-shadow-high)}.gala-stats-graph{min-block-size:var(--gala-widget-min-block-size);margin-inline:0}.gala-stats-graph svg{inline-size:100%;block-size:8rem}.gala-stats-graph polyline{fill:none;stroke:var(--gala-color-accent);stroke-width:2}.gala-loading{display:grid;min-block-size:var(--gala-widget-min-block-size);place-items:center}.gala-loading__indicator{inline-size:2rem;block-size:2rem;border:.25rem solid var(--gala-color-border);border-block-start-color:var(--gala-color-accent);border-radius:50%;animation:gala-spin .8s linear infinite}.gala-pagination{display:flex;justify-content:space-between;gap:1rem}.gala-search{display:grid;grid-template-columns:1fr auto;gap:.5rem}.gala-search label{grid-column:1 / -1}.gala-search input{min-block-size:2.75rem;font:inherit}.gala-search-results{display:grid;gap:1rem;padding:0;list-style:none}.gala-search-results article{padding:1rem;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}.gala-search-results h2{margin-block-start:0}.gala-toc{box-sizing:border-box;min-inline-size:0;max-inline-size:42rem;margin-inline:auto;padding:1rem;background:var(--gala-color-surface)}.gala-toc summary{color:var(--gala-color-text);font-weight:var(--gala-weight-display);cursor:pointer}.gala-toc nav{margin-block-start:var(--gala-space-sm);overscroll-behavior:contain}.gala-toc ol{margin:0;padding-inline-start:1.25rem}.gala-toc a[aria-current=location]{color:var(--gala-color-text);font-weight:700}.gala-share__fallback{inline-size:min(100%,36rem)}.gala-engagement__placeholder{display:grid;min-block-size:var(--gala-widget-min-block-size);align-content:center;margin:0}.gala-engagement-profile>*{margin-block:0 .5rem}.gala-engagement-actions{display:grid;gap:1rem;margin-block-start:1.5rem}.gala-engagement-actions[hidden]{display:none}.gala-account-dialog__invite{margin-block:0 1rem;max-inline-size:34rem}.gala-account-dialog__invite[hidden]{display:none}.gala-engagement-actions form,[data-comment-editor]{display:grid;gap:.5rem;max-inline-size:42rem}.gala-engagement-actions label{display:grid;gap:.5rem}.gala-engagement-actions textarea,[data-comment-editor] textarea{min-block-size:7rem;padding:.75rem;font:inherit}.gala-page-footer{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--gala-space-lg);margin-block-start:var(--gala-space-xl);padding-block-start:var(--gala-space-md);border-block-start:.125rem solid var(--gala-color-border)}.gala-page-footer__identity{display:flex;gap:var(--gala-space-sm);max-inline-size:56rem}.gala-page-footer__identity img{flex:0 0 auto;border-radius:50%;object-fit:cover}.gala-page-footer__identity p{margin:0;color:var(--gala-color-muted);font-size:var(--gala-text-sm)}.gala-page-footer__details{display:grid;gap:var(--gala-space-2xs);align-content:center}.gala-page-footer__line{display:grid;grid-template-columns:repeat(3,max-content);align-items:baseline;gap:0 var(--gala-space-sm)}.gala-page-footer__line>*+*:before{content:"\b7";margin-inline-end:var(--gala-space-sm);color:var(--gala-color-border)}.gala-page-footer__bio{line-height:1.4}.gala-page-footer__author{color:var(--gala-color-text);font-weight:var(--gala-weight-display)}.gala-page-footer nav{margin-inline-start:auto}.gala-page-footer ul{justify-content:flex-end;margin:0}.gala-error-page{min-block-size:60vh;display:grid;align-content:center;justify-items:start}.gala-code-block{position:relative;box-sizing:border-box;min-inline-size:0;max-inline-size:100%;inline-size:100%;overflow:hidden}.gala-code-block>button{position:absolute;inset-block-start:.5rem;inset-inline-end:.5rem;background:var(--gala-color-background)}.gala-code-block pre{box-sizing:border-box;min-inline-size:0;max-inline-size:100%;inline-size:100%;overflow-x:auto;overscroll-behavior-inline:contain;padding:3.5rem 1rem 1rem;background:var(--gala-color-surface);border-radius:var(--gala-radius-control)}.gala-code-block code{display:block;inline-size:max-content;min-inline-size:100%}.gala-embed{display:grid;place-items:center;gap:.75rem;box-sizing:border-box;inline-size:100%;margin-inline:0;padding:1rem;overflow:hidden;background:var(--gala-color-surface);border:.125rem solid var(--gala-color-border);border-radius:var(--gala-radius-control)}.gala-embed--youtube{aspect-ratio:16 / 9}.gala-embed--codepen{min-block-size:25rem}.gala-embed--gist,.gala-embed--x{min-block-size:8rem}.gala-embed iframe{inline-size:100%;block-size:100%;border:0}.gala-embed__icon{inline-size:2rem;block-size:2rem;background:center / contain no-repeat}.gala-embed--youtube .gala-embed__icon{background-image:url(../assets/embed-youtube.svg)}.gala-embed--codepen .gala-embed__icon{background-image:url(../assets/embed-codepen.svg)}.gala-embed--gist .gala-embed__icon{background-image:url(../assets/embed-gist.svg)}.gala-embed--x .gala-embed__icon{background-image:url(../assets/embed-x.svg)}.shiki,.shiki span{color:inherit;background-color:var(--gala-color-surface)}.gala-visually-hidden{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.gala-contact form{display:grid;gap:1rem;max-inline-size:42rem}.gala-contact label{display:grid;gap:.35rem}.gala-contact input,.gala-contact textarea{font:inherit;padding:.65rem}.gala-contact textarea{min-block-size:12rem;resize:vertical}@keyframes gala-spin{to{transform:rotate(1turn)}}@media(min-width:48rem){.gala-hero{grid-template-columns:minmax(0,1fr) minmax(18rem,1fr)}}@media(prefers-reduced-motion:no-preference){@view-transition{navigation:auto}}.gala-comments-island{margin-block-start:var(--gala-space-xl)}.gala-comments__heading{font-size:var(--gala-text-lg);margin-block-end:var(--gala-space-md)}.gala-comments,.gala-comment-replies{list-style:none;margin:0;padding:0}.gala-comment-replies{margin-block-start:var(--gala-space-sm);margin-inline-start:var(--gala-space-xs);padding-inline-start:var(--gala-space-md);border-inline-start:1px solid var(--gala-color-hairline)}.gala-comment{padding-block:var(--gala-space-sm)}.gala-comment+.gala-comment{border-block-start:1px solid var(--gala-color-hairline)}.gala-comment-replies .gala-comment+.gala-comment{border-block-start:0}.gala-comment__meta{display:flex;align-items:baseline;gap:var(--gala-space-xs);margin-block-end:var(--gala-space-2xs);font-size:var(--gala-text-sm)}.gala-comment__meta strong{font-weight:600}.gala-comment__pending{font-size:var(--gala-text-xs);color:var(--gala-color-muted)}.gala-comment__body{margin-block:0;overflow-wrap:anywhere}.gala-comment-actions{display:flex;gap:var(--gala-space-2xs);margin-block-start:var(--gala-space-2xs);opacity:.72;transition:opacity var(--gala-motion-fast) var(--gala-ease)}.gala-comment:hover>.gala-comment-actions,.gala-comment:focus-within>.gala-comment-actions{opacity:1}.gala-comment-actions button{min-block-size:2rem;padding-inline:var(--gala-space-xs);font-size:var(--gala-text-xs);background:transparent;border-color:transparent;color:var(--gala-color-muted)}.gala-comment-actions button:hover{color:var(--gala-color-text);background:var(--gala-color-surface)}.gala-comment__form{display:grid;gap:var(--gala-space-xs);margin-block:var(--gala-space-sm)}.gala-comment__form textarea{inline-size:100%;box-sizing:border-box;padding:var(--gala-space-sm);color:inherit;background:var(--gala-color-raised);border:1px solid var(--gala-color-border);border-radius:var(--gala-radius-control);font:inherit;font-size:var(--gala-text-sm);line-height:var(--gala-leading-body);resize:vertical}.gala-comment__form textarea:focus-visible{outline:2px solid var(--gala-color-accent);outline-offset:1px}.gala-comment__controls{display:flex;gap:var(--gala-space-xs)}.gala-comments__status,.gala-comments__empty,.gala-comments__prompt{color:var(--gala-color-muted);font-size:var(--gala-text-sm)}.gala-comments__more{margin-block-start:var(--gala-space-md)}.gala-engagement{min-block-size:var(--gala-widget-min-block-size);max-inline-size:var(--gala-content-width);margin-block-start:var(--gala-space-xl);padding-block-start:var(--gala-space-lg);border-block-start:1px solid var(--gala-color-border)}.gala-engagement-profile{display:flex;align-items:baseline;gap:var(--gala-space-sm);padding:0;background:none}.gala-engagement-profile h2{font-size:var(--gala-text-lg);margin:0}.gala-engagement-profile p{margin:0;color:var(--gala-color-muted);font-size:var(--gala-text-sm)}.gala-engagement>dl{display:flex;flex-wrap:wrap;gap:var(--gala-space-lg);margin-block:var(--gala-space-md)}.gala-engagement>dl>div{display:flex;flex-direction:column-reverse;gap:.1rem}.gala-engagement>dl dt{font-size:var(--gala-text-xs);letter-spacing:var(--gala-tracking-wide);text-transform:uppercase;color:var(--gala-color-muted)}.gala-engagement>dl dd{margin:0;font-size:var(--gala-text-lg);font-variant-numeric:tabular-nums}.gala-reaction-actions{display:flex;flex-wrap:wrap;gap:var(--gala-space-2xs)}.gala-engagement-actions{display:grid;gap:var(--gala-space-sm);justify-items:start}.gala-engagement-actions>[data-follow-article]{inline-size:auto}.gala-share{margin-block-start:var(--gala-space-lg)}.gala-engagement{position:relative}.gala-reading-progress{position:fixed;z-index:20;inset:0 0 auto;inline-size:100%;block-size:.2rem;appearance:none;border:0;background:transparent;pointer-events:none}.gala-reading-progress::-webkit-progress-bar{background:transparent}.gala-reading-progress::-webkit-progress-value{background:var(--gala-color-accent)}.gala-reading-progress::-moz-progress-bar{background:var(--gala-color-accent)}.gala-reading-layout{display:block}.gala-conversation{max-width:var(--gala-measure);margin-inline:auto;padding-block:clamp(3rem,7vw,6rem)}.gala-conversation__header{margin-block-end:var(--gala-space-xl)}.gala-conversation__header h2{margin:0;font:var(--gala-weight-display) var(--gala-text-2xl)/var(--gala-leading-tight) var(--gala-font-display);letter-spacing:var(--gala-tracking-tight)}.gala-conversation__status{display:block;min-block-size:1.5em;color:var(--gala-color-muted)}.gala-conversation__status:empty{display:none}.gala-action-rail{position:static;z-index:4;inset-block-start:6rem;box-sizing:border-box;width:min(100%,42rem);margin:var(--gala-space-xl) auto 0;display:grid;grid-template-columns:1fr auto 1fr;gap:var(--gala-space-md);align-items:center;padding:var(--gala-space-md);border:1px solid var(--gala-color-hairline);border-radius:var(--gala-radius-surface);background:color-mix(in srgb,var(--gala-color-background) 88%,transparent);box-shadow:var(--gala-shadow-high);backdrop-filter:blur(16px)}.gala-action-rail button,.gala-action-rail a{inline-size:2.5rem;min-block-size:2.5rem;display:grid;place-items:center;padding:0;border:0;border-radius:50%;color:var(--gala-color-muted);background:transparent;font:600 var(--gala-text-xs)/1 var(--gala-font-body);text-decoration:none;cursor:pointer}.gala-action-rail svg{inline-size:1.2rem;block-size:1.2rem}.gala-action-rail button:hover,.gala-action-rail a:hover,.gala-action-rail button[aria-pressed=true]{color:var(--gala-color-background);background:var(--gala-color-accent)}.gala-engagement .gala-action-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gala-space-xs);margin:0}.gala-action-stats>div{display:flex;min-block-size:2.5rem;align-items:center;justify-content:space-between;gap:.4rem;padding-inline:.25rem}.gala-action-stats dt{display:grid;place-items:center;color:var(--gala-color-muted)}.gala-action-stats dt svg{inline-size:1.2rem;block-size:1.2rem}.gala-action-stats dd{margin:0;color:var(--gala-color-text);font:400 1.2rem/1 var(--gala-font-mono)}.gala-action-group{display:grid}.gala-reaction-actions,.gala-utility-actions,.gala-share{display:grid;grid-template-columns:repeat(6,2.5rem);gap:var(--gala-space-2xs);justify-content:center}.gala-utility-actions>.gala-share{display:contents}.gala-action-rail__separator{inline-size:1px;align-self:stretch;margin-block:.25rem;background:var(--gala-color-hairline)}.gala-action-rail .gala-share__fallback{position:fixed;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%)}.gala-action-rail .gala-share__fallback--visible{z-index:10;inset:auto 1rem 1rem;inline-size:min(32rem,calc(100vw - 2rem));block-size:auto;padding:.75rem;overflow:visible;clip-path:none}.gala-action-dock{display:block;min-block-size:4.25rem}.gala-article-boundary{width:100%;margin:clamp(4rem,8vw,7rem) auto var(--gala-space-lg);padding-block-start:var(--gala-space-md);display:flex;align-items:baseline;justify-content:space-between;gap:var(--gala-space-md);border-block-start:.25rem solid var(--gala-color-text);font-size:var(--gala-text-sm)}@media(min-width:75rem){.gala-reading-layout{display:grid;grid-template-columns:minmax(10rem,15rem) minmax(0,1fr) 8.5rem;align-items:start;justify-content:space-between;gap:clamp(2rem,4vw,5rem)}.gala-reading-layout .gala-toc{grid-column:1;grid-row:1;position:sticky;inset-block-start:6rem;max-inline-size:none;margin-inline:0;max-block-size:calc(100vh - 8rem);transition:box-shadow var(--gala-motion-fast) var(--gala-ease)}.gala-reading-layout .gala-toc--floating{box-shadow:var(--gala-shadow-high)}.gala-reading-layout .gala-toc>summary{display:none}.gala-reading-layout .gala-toc>nav{display:block;max-block-size:calc(100vh - 10rem);margin-block-start:0;overflow-y:auto;scrollbar-width:thin}.gala-reading-layout .gala-toc--overflowing ol{padding-block:max(1rem,calc((100vh - 11.5rem)/2))}.gala-reading-layout .gala-markdown{grid-column:2;grid-row:1;max-inline-size:56rem;inline-size:100%;margin:0}.gala-article-boundary{grid-column:1 / -1;grid-row:2;margin-inline:0}.gala-action-dock{grid-column:2;grid-row:3}.gala-action-rail{grid-column:3;grid-row:1;position:sticky;inset-block-start:6rem;width:8.5rem;margin:0;display:grid;grid-template-columns:1fr;gap:var(--gala-space-sm);padding:var(--gala-space-sm);border-radius:var(--gala-radius-surface)}.gala-action-rail .gala-action-stats{gap:var(--gala-space-2xs)}.gala-action-rail:not(.gala-action-rail--integrated) .gala-action-stats{grid-template-columns:1fr}.gala-action-rail .gala-action-stats>div{min-block-size:2.5rem;display:grid;grid-template-columns:repeat(2,2.5rem);justify-content:center;gap:.25rem;padding-inline:0}.gala-action-rail .gala-action-stats dd{justify-self:center}.gala-action-rail .gala-reaction-actions,.gala-action-rail .gala-utility-actions,.gala-action-rail .gala-share{grid-template-columns:repeat(2,2.5rem)}.gala-action-rail__separator{inline-size:auto;block-size:1px;margin:.25rem}.gala-action-rail--integrated{grid-column:2;grid-row:3;position:static;align-self:start;width:100%;margin:0;display:grid;grid-template-columns:minmax(0,1fr) 1px minmax(0,1fr);grid-template-rows:auto 1px auto;grid-template-areas:"reactions utility-separator utilities" "stats-separator stats-separator stats-separator" "stats stats stats";justify-content:stretch;gap:clamp(var(--gala-space-2xs),1vw,var(--gala-space-md));align-items:center;padding:var(--gala-space-md);border-radius:var(--gala-radius-surface)}.gala-action-rail--integrated .gala-action-rail__separator{grid-area:utility-separator;inline-size:1px;block-size:auto;margin:.25rem 0}.gala-action-rail--integrated .gala-action-rail__separator--stats{grid-area:stats-separator;inline-size:auto;block-size:1px;margin:0}.gala-action-rail--integrated .gala-action-group--reactions{grid-area:reactions}.gala-action-rail--integrated .gala-action-group--utilities{grid-area:utilities}.gala-action-rail--integrated .gala-reaction-actions{grid-template-columns:repeat(6,2.5rem)}.gala-action-rail--integrated .gala-utility-actions{grid-template-columns:repeat(4,2.5rem)}.gala-action-rail--integrated .gala-action-stats>div{min-block-size:2.5rem;display:flex;flex-direction:row;gap:.4rem;padding-inline:.25rem}.gala-action-rail--integrated .gala-action-stats{grid-area:stats;grid-template-columns:repeat(3,minmax(0,1fr))}}@media(max-width:74.99rem){.gala-action-rail{grid-template-columns:1fr;width:min(100%,42rem)}.gala-action-rail__separator{inline-size:auto;block-size:1px;margin:0}}@media(max-width:47.99rem){.gala-page-footer{flex-direction:column}.gala-page-footer__line{grid-template-columns:minmax(0,1fr);gap:var(--gala-space-2xs)}.gala-page-footer__line>*+*:before{content:none}.gala-page-footer nav{margin-inline-start:0}.gala-page-footer ul{justify-content:flex-start}.gala-site-name{flex:1 1 auto;min-inline-size:4rem;font-size:var(--gala-text-base)}.gala-header-link{display:none}.gala-site-header__actions{flex:0 0 auto;gap:.125rem}.gala-account-control{inline-size:2.75rem;padding:0;justify-content:center}.gala-account-control [data-user-label]{display:none}}
|
|
@@ -31,6 +31,7 @@ export default class ValidatedPostPages {
|
|
|
31
31
|
postTableOfContents: ({ post }) => post?.publicationState === 'published'
|
|
32
32
|
? renderedDocument(post).tableOfContents
|
|
33
33
|
: [],
|
|
34
|
+
readingMinutes: ({ post }) => readingMinutes(post?.body),
|
|
34
35
|
seo: ({ post, site }) => post?.publicationState === 'published'
|
|
35
36
|
? postSeo({ post, site, renderedHtml: renderedDocument(post).html })
|
|
36
37
|
: null
|
|
@@ -46,6 +47,11 @@ export default class ValidatedPostPages {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
function readingMinutes(markdown) {
|
|
51
|
+
const words = String(markdown ?? '').trim().match(/[\p{L}\p{N}]+(?:['’_-][\p{L}\p{N}]+)*/gu)?.length ?? 0;
|
|
52
|
+
return Math.max(1, Math.ceil(words / 225));
|
|
53
|
+
}
|
|
54
|
+
|
|
49
55
|
const renderedDocuments = new WeakMap();
|
|
50
56
|
|
|
51
57
|
function renderedDocument(post) {
|