ferst-core 0.7.2 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ferst-update.mjs +33 -2
- package/cms/config.template.yml +214 -206
- package/components/BlockRenderer.astro +10 -0
- package/components/Pagination.astro +51 -0
- package/components/SiteFooter.astro +274 -270
- package/components/SiteHeader.astro +1243 -1183
- package/components/blocks/Heading.astro +51 -46
- package/components/blocks/Prose.astro +10 -2
- package/components/blocks/Quote.astro +40 -38
- package/components/recipes/Accordion.astro +91 -84
- package/components/recipes/Contacts.astro +139 -0
- package/components/recipes/Document.astro +157 -0
- package/components/recipes/FeatureCards.astro +112 -111
- package/components/recipes/Hero.astro +52 -4
- package/components/recipes/LinkRow.astro +91 -0
- package/components/recipes/MediaCards.astro +213 -212
- package/components/recipes/MediaText.astro +92 -0
- package/components/recipes/Notice.astro +90 -89
- package/components/recipes/Stats.astro +152 -145
- package/components/recipes/Table.astro +101 -97
- package/components/recipes/Tabs.astro +134 -133
- package/components/recipes/Tiles.astro +4 -2
- package/components/recipes/Timetable.astro +189 -0
- package/components/recipes/Video.astro +177 -157
- package/content/blocks.ts +113 -1
- package/content/cookies.ts +10 -0
- package/content/schemas.ts +167 -161
- package/examples/primitives.ts +2 -0
- package/examples/recipes.ts +7 -0
- package/integration.mjs +83 -64
- package/layouts/Base.astro +483 -475
- package/lib/documents.ts +68 -62
- package/lib/embed-consent.ts +78 -73
- package/lib/inline-markdown.ts +6 -0
- package/lib/links.ts +22 -0
- package/lib/paginate.ts +23 -0
- package/lib/posts.ts +20 -2
- package/lib/siteSettings.ts +61 -60
- package/lib/slug.ts +32 -0
- package/lib/table.ts +10 -0
- package/lib/timetable.ts +27 -0
- package/lib/video.ts +101 -75
- package/package.json +1 -1
- package/routes/cookie-policy.astro +27 -0
- package/routes/posts/index.astro +36 -31
- package/routes/posts/page/[page].astro +36 -0
- package/routes/posts/tag/[...tag].astro +41 -37
|
@@ -1,46 +1,51 @@
|
|
|
1
|
-
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
---
|
|
2
|
+
import { pageSlugs, uniqueSlug } from '../../lib/slug';
|
|
3
|
+
interface Props {
|
|
4
|
+
text: string;
|
|
5
|
+
level?: number;
|
|
6
|
+
align?: 'start' | 'center' | 'end';
|
|
7
|
+
gradient?: boolean;
|
|
8
|
+
}
|
|
9
|
+
const { text, level = 2, align, gradient = false } = Astro.props;
|
|
10
|
+
const Tag = `h${level}` as unknown as keyof HTMLElementTagNameMap;
|
|
11
|
+
// A readable, page-unique anchor so any heading can be linked to (#mass-times).
|
|
12
|
+
const id = uniqueSlug(text, pageSlugs(Astro.locals));
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<Tag class="b-heading" id={id} data-align={align} data-gradient={gradient ? 'true' : 'false'}>{text}</Tag>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
/* By default a heading INHERITS the page/section content alignment (the
|
|
19
|
+
`--content-align` cascade set on `.site-main` / a `section`), so a whole
|
|
20
|
+
template stays coherent. An explicit `align` prop overrides it for this one
|
|
21
|
+
heading. (`text-align` inherits, and inheritance carries the resolved
|
|
22
|
+
alignment; the `[data-align]` rules win over it when set.) */
|
|
23
|
+
.b-heading {
|
|
24
|
+
color: var(--fg);
|
|
25
|
+
margin: 0;
|
|
26
|
+
text-wrap: balance;
|
|
27
|
+
white-space: pre-line; /* a title typed over several lines keeps its breaks */
|
|
28
|
+
scroll-margin-top: 6rem; /* a linked heading stops clear of the sticky header */
|
|
29
|
+
line-height: 1.2;
|
|
30
|
+
text-align: inherit;
|
|
31
|
+
}
|
|
32
|
+
.b-heading[data-align='start'] {
|
|
33
|
+
text-align: start;
|
|
34
|
+
}
|
|
35
|
+
.b-heading[data-align='center'] {
|
|
36
|
+
text-align: center;
|
|
37
|
+
}
|
|
38
|
+
.b-heading[data-align='end'] {
|
|
39
|
+
text-align: end;
|
|
40
|
+
}
|
|
41
|
+
/* Gradient text — only clip to text where supported, so it never renders as
|
|
42
|
+
invisible/transparent on older browsers (it stays solid ink there). */
|
|
43
|
+
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
|
|
44
|
+
.b-heading[data-gradient='true'] {
|
|
45
|
+
background-image: linear-gradient(120deg, var(--gold), var(--accent-2, var(--gold)));
|
|
46
|
+
-webkit-background-clip: text;
|
|
47
|
+
background-clip: text;
|
|
48
|
+
color: transparent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
import { proseParts } from '../../lib/prose';
|
|
3
3
|
interface Props {
|
|
4
4
|
text: string;
|
|
5
|
+
variant?: 'body' | 'lead';
|
|
5
6
|
}
|
|
6
|
-
const { text } = Astro.props;
|
|
7
|
+
const { text, variant = 'body' } = Astro.props;
|
|
7
8
|
// Blank lines separate paragraphs; `- ` / `1. ` lines make lists; links, bold and
|
|
8
9
|
// italic are rendered safely (lib/prose.ts → inlineMarkdown).
|
|
9
10
|
const parts = proseParts(text);
|
|
10
11
|
---
|
|
11
12
|
|
|
12
|
-
<div class="b-prose">
|
|
13
|
+
<div class="b-prose" data-variant={variant === 'lead' ? 'lead' : undefined}>
|
|
13
14
|
{parts.map((part) =>
|
|
14
15
|
part.kind === 'p' ? <p set:html={part.html} />
|
|
15
16
|
: part.kind === 'ul' ? <ul>{part.items.map((i) => <li set:html={i} />)}</ul>
|
|
@@ -38,6 +39,13 @@ const parts = proseParts(text);
|
|
|
38
39
|
.b-prose > :last-child {
|
|
39
40
|
margin-bottom: 0;
|
|
40
41
|
}
|
|
42
|
+
/* The page's introduction: larger, quieter, a narrower measure. */
|
|
43
|
+
.b-prose[data-variant='lead'] p {
|
|
44
|
+
font-size: clamp(1.125rem, 1.6vw, 1.3125rem);
|
|
45
|
+
line-height: 1.6;
|
|
46
|
+
color: var(--muted);
|
|
47
|
+
max-width: 40em;
|
|
48
|
+
}
|
|
41
49
|
.b-prose :global(a) {
|
|
42
50
|
color: var(--accent);
|
|
43
51
|
text-decoration: underline;
|
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
</
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
---
|
|
2
|
+
import { inlineMarkdown } from '../../lib/inline-markdown';
|
|
3
|
+
interface Props {
|
|
4
|
+
text: string;
|
|
5
|
+
cite?: string;
|
|
6
|
+
}
|
|
7
|
+
const { text, cite } = Astro.props;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<figure class="b-quote">
|
|
11
|
+
<blockquote set:html={inlineMarkdown(text)} />
|
|
12
|
+
{cite && <figcaption>— {cite}</figcaption>}
|
|
13
|
+
</figure>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
/* inline markdown already turns single newlines into <br>: a prayer or verse keeps its lines. */
|
|
17
|
+
.b-quote {
|
|
18
|
+
margin: 0;
|
|
19
|
+
border-inline-start: 3px solid var(--gold);
|
|
20
|
+
padding-inline-start: 1rem;
|
|
21
|
+
}
|
|
22
|
+
/* On a centred page/section the left rule reads as off-axis — drop it so the
|
|
23
|
+
quote centres cleanly. (Inherits text-align from the content-align cascade.) */
|
|
24
|
+
:global([data-content-align='center']) .b-quote {
|
|
25
|
+
border-inline-start: none;
|
|
26
|
+
padding-inline-start: 0;
|
|
27
|
+
}
|
|
28
|
+
.b-quote blockquote {
|
|
29
|
+
margin: 0;
|
|
30
|
+
color: var(--fg);
|
|
31
|
+
font-style: italic;
|
|
32
|
+
font-size: 1.125rem;
|
|
33
|
+
line-height: 1.6;
|
|
34
|
+
}
|
|
35
|
+
.b-quote figcaption {
|
|
36
|
+
color: var(--muted);
|
|
37
|
+
margin-top: 0.5rem;
|
|
38
|
+
font-size: 0.9375rem;
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
@@ -1,84 +1,91 @@
|
|
|
1
|
-
---
|
|
2
|
-
/** Accordion — a Level-2 recipe: an optional heading over expand/collapse rows
|
|
3
|
-
* (title + body). Generic sibling of FAQ (which is question/answer-flavoured).
|
|
4
|
-
* Native <details>/<summary> — accessible, interactive, zero JavaScript. */
|
|
5
|
-
import Heading from '../blocks/Heading.astro';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
padding: 0.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
content:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
.b-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
---
|
|
2
|
+
/** Accordion — a Level-2 recipe: an optional heading over expand/collapse rows
|
|
3
|
+
* (title + body). Generic sibling of FAQ (which is question/answer-flavoured).
|
|
4
|
+
* Native <details>/<summary> — accessible, interactive, zero JavaScript. */
|
|
5
|
+
import Heading from '../blocks/Heading.astro';
|
|
6
|
+
|
|
7
|
+
import { proseParts } from '../../lib/prose';
|
|
8
|
+
interface Props {
|
|
9
|
+
title?: string;
|
|
10
|
+
items?: { heading: string; body: string }[];
|
|
11
|
+
}
|
|
12
|
+
const { title, items = [] } = Astro.props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<section class="b-accordion">
|
|
16
|
+
{title && <Heading text={title} level={2} align="center" />}
|
|
17
|
+
<div class="b-accordion__list">
|
|
18
|
+
{items.map((item) => (
|
|
19
|
+
<details class="b-accordion__item">
|
|
20
|
+
<summary class="b-accordion__head">{item.heading}</summary>
|
|
21
|
+
<div class="b-accordion__body">
|
|
22
|
+
{proseParts(item.body).map((part) =>
|
|
23
|
+
part.kind === 'p' ? <p set:html={part.html} />
|
|
24
|
+
: part.kind === 'ul' ? <ul>{part.items.map((x) => <li set:html={x} />)}</ul>
|
|
25
|
+
: <ol>{part.items.map((x) => <li set:html={x} />)}</ol>
|
|
26
|
+
)}
|
|
27
|
+
</div>
|
|
28
|
+
</details>
|
|
29
|
+
))}
|
|
30
|
+
</div>
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
.b-accordion {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
gap: 1.5rem;
|
|
38
|
+
padding-inline: 1rem;
|
|
39
|
+
}
|
|
40
|
+
.b-accordion__list {
|
|
41
|
+
max-width: var(--max);
|
|
42
|
+
margin-inline: auto;
|
|
43
|
+
width: 100%;
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: 0.5rem;
|
|
47
|
+
}
|
|
48
|
+
.b-accordion__item {
|
|
49
|
+
border: var(--card-border);
|
|
50
|
+
border-radius: var(--radius-sm);
|
|
51
|
+
padding: 0.25rem 1rem;
|
|
52
|
+
background: var(--bg-surface);
|
|
53
|
+
}
|
|
54
|
+
.b-accordion__head {
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
color: var(--fg);
|
|
58
|
+
padding: 0.85rem 0;
|
|
59
|
+
list-style: none;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: space-between;
|
|
63
|
+
gap: 0.75rem;
|
|
64
|
+
}
|
|
65
|
+
.b-accordion__head::-webkit-details-marker {
|
|
66
|
+
display: none;
|
|
67
|
+
}
|
|
68
|
+
.b-accordion__head::after {
|
|
69
|
+
content: '+';
|
|
70
|
+
color: var(--gold);
|
|
71
|
+
font-weight: 700;
|
|
72
|
+
font-size: 1.25rem;
|
|
73
|
+
line-height: 1;
|
|
74
|
+
transition: transform 0.2s ease;
|
|
75
|
+
}
|
|
76
|
+
.b-accordion__item[open] .b-accordion__head::after {
|
|
77
|
+
content: '\2013'; /* en dash */
|
|
78
|
+
}
|
|
79
|
+
.b-accordion__head:focus-visible {
|
|
80
|
+
outline: 2px solid var(--gold);
|
|
81
|
+
outline-offset: 2px;
|
|
82
|
+
}
|
|
83
|
+
.b-accordion__body {
|
|
84
|
+
padding: 0 0 0.9rem;
|
|
85
|
+
color: var(--muted);
|
|
86
|
+
line-height: 1.6;
|
|
87
|
+
}
|
|
88
|
+
.b-accordion__body p {
|
|
89
|
+
margin: 0;
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Contacts — a compact directory of people or offices. Each card: the role as its title,
|
|
4
|
+
* the name, phone and email as tap targets (email with an optional subject), address and
|
|
5
|
+
* hours. A group can carry one shared note (e.g. office hours) instead of repeating it on
|
|
6
|
+
* every card. For one contact as a branded band use `contactCard`; for one contact's
|
|
7
|
+
* links in a row, `linkRow`.
|
|
8
|
+
*/
|
|
9
|
+
import Heading from '../blocks/Heading.astro';
|
|
10
|
+
import Icon from '../Icon.astro';
|
|
11
|
+
import { inlineMarkdown, textWithBreaks } from '../../lib/inline-markdown';
|
|
12
|
+
import { mailtoHref, telHref } from '../../lib/links';
|
|
13
|
+
|
|
14
|
+
interface Contact { role: string; name?: string; phone?: string; email?: string; emailSubject?: string; address?: string; hours?: string }
|
|
15
|
+
interface Props {
|
|
16
|
+
title?: string;
|
|
17
|
+
intro?: string;
|
|
18
|
+
groups: Array<{ heading?: string; note?: string; contacts: Contact[] }>;
|
|
19
|
+
}
|
|
20
|
+
const { title, intro, groups = [] } = Astro.props;
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<section class="b-contacts">
|
|
24
|
+
{(title || intro) && (
|
|
25
|
+
<div class="b-contacts__head">
|
|
26
|
+
{title && <Heading text={title} level={2} />}
|
|
27
|
+
{intro && <p class="b-contacts__intro" set:html={inlineMarkdown(intro)} />}
|
|
28
|
+
</div>
|
|
29
|
+
)}
|
|
30
|
+
{groups.map((g) => (
|
|
31
|
+
<div class="b-contacts__group">
|
|
32
|
+
{(g.heading || g.note) && (
|
|
33
|
+
<div class="b-contacts__group-head">
|
|
34
|
+
{g.heading && <h3 class="b-contacts__group-title">{g.heading}</h3>}
|
|
35
|
+
{g.note && <p class="b-contacts__note" set:html={inlineMarkdown(g.note)} />}
|
|
36
|
+
</div>
|
|
37
|
+
)}
|
|
38
|
+
<ul class="b-contacts__grid">
|
|
39
|
+
{g.contacts.map((c) => (
|
|
40
|
+
<li class="b-contacts__card">
|
|
41
|
+
<h3 class="b-contacts__role">{c.role}</h3>
|
|
42
|
+
{c.name && <p class="b-contacts__name">{c.name}</p>}
|
|
43
|
+
<div class="b-contacts__lines">
|
|
44
|
+
{c.phone && <a class="b-contacts__line" href={telHref(c.phone)}><Icon name="phone" size={16} /><span>{c.phone}</span></a>}
|
|
45
|
+
{c.email && <a class="b-contacts__line" href={mailtoHref(c.email, c.emailSubject)}><Icon name="email" size={16} /><span>{c.email}</span></a>}
|
|
46
|
+
{c.address && <p class="b-contacts__line"><Icon name="map-pin" size={16} /><span set:html={textWithBreaks(c.address)} /></p>}
|
|
47
|
+
{c.hours && <p class="b-contacts__line"><Icon name="calendar" size={16} /><span set:html={textWithBreaks(c.hours)} /></p>}
|
|
48
|
+
</div>
|
|
49
|
+
</li>
|
|
50
|
+
))}
|
|
51
|
+
</ul>
|
|
52
|
+
</div>
|
|
53
|
+
))}
|
|
54
|
+
</section>
|
|
55
|
+
|
|
56
|
+
<style>
|
|
57
|
+
.b-contacts {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
gap: 2rem;
|
|
61
|
+
width: 100%;
|
|
62
|
+
max-width: 72rem;
|
|
63
|
+
}
|
|
64
|
+
.b-contacts__head,
|
|
65
|
+
.b-contacts__group-head {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
gap: 0.4rem;
|
|
69
|
+
max-width: var(--max);
|
|
70
|
+
}
|
|
71
|
+
.b-contacts__intro,
|
|
72
|
+
.b-contacts__note {
|
|
73
|
+
margin: 0;
|
|
74
|
+
color: var(--muted);
|
|
75
|
+
line-height: 1.6;
|
|
76
|
+
}
|
|
77
|
+
.b-contacts__group {
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
gap: 1rem;
|
|
81
|
+
}
|
|
82
|
+
.b-contacts__group-title {
|
|
83
|
+
margin: 0;
|
|
84
|
+
font-size: 1.15rem;
|
|
85
|
+
}
|
|
86
|
+
.b-contacts__grid {
|
|
87
|
+
list-style: none;
|
|
88
|
+
margin: 0;
|
|
89
|
+
padding: 0;
|
|
90
|
+
display: grid;
|
|
91
|
+
gap: 1rem;
|
|
92
|
+
grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
|
|
93
|
+
}
|
|
94
|
+
.b-contacts__card {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
gap: 0.4rem;
|
|
98
|
+
padding: 1.1rem 1.25rem;
|
|
99
|
+
border: var(--card-border);
|
|
100
|
+
border-radius: var(--radius);
|
|
101
|
+
background: var(--bg-surface);
|
|
102
|
+
}
|
|
103
|
+
.b-contacts__role {
|
|
104
|
+
margin: 0;
|
|
105
|
+
font-size: 1.05rem;
|
|
106
|
+
}
|
|
107
|
+
.b-contacts__name {
|
|
108
|
+
margin: 0;
|
|
109
|
+
font-size: 0.9375rem;
|
|
110
|
+
color: var(--muted);
|
|
111
|
+
}
|
|
112
|
+
.b-contacts__lines {
|
|
113
|
+
display: grid;
|
|
114
|
+
gap: 0.35rem;
|
|
115
|
+
margin-top: 0.35rem;
|
|
116
|
+
}
|
|
117
|
+
.b-contacts__line {
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: flex-start;
|
|
120
|
+
gap: 0.5rem;
|
|
121
|
+
margin: 0;
|
|
122
|
+
color: var(--fg);
|
|
123
|
+
text-decoration: none;
|
|
124
|
+
font-size: 0.9375rem;
|
|
125
|
+
overflow-wrap: anywhere;
|
|
126
|
+
}
|
|
127
|
+
.b-contacts__line :global(svg) {
|
|
128
|
+
flex: none;
|
|
129
|
+
margin-top: 0.2em;
|
|
130
|
+
color: var(--gold);
|
|
131
|
+
}
|
|
132
|
+
a.b-contacts__line:hover span {
|
|
133
|
+
text-decoration: underline;
|
|
134
|
+
}
|
|
135
|
+
a.b-contacts__line:focus-visible {
|
|
136
|
+
outline: 2px solid var(--gold);
|
|
137
|
+
outline-offset: 2px;
|
|
138
|
+
}
|
|
139
|
+
</style>
|