/* Shared page furniture: headers, sections, cards, forms, buttons, links, footer.
 *
 * Plain CSS in the document, not Lit `css` templates in shadow roots. It was
 * the latter — styles/page-header.js, card.js, form.js, input.js, button.js,
 * link.js — until the blog arrived. Those pages are server-rendered HTML, so
 * they cannot reach into a shadow root, and the rules had to be copied into
 * public.css to make a blog page look like the rest of the site. Two copies of a
 * layout with no build step to notice them drifting is not a stable
 * arrangement.
 *
 * So the page-level components render into light DOM (`createRenderRoot()
 * returns this`) and read these rules from the document, exactly as the
 * server-rendered pages do. One copy, one place.
 *
 * `<pg-router>` renders into light DOM for the same reason: a shadow root
 * anywhere above a component cuts it off from document styles, and the router
 * sits above every page.
 *
 * Encapsulation comes from the element name instead — component-specific rules
 * at the foot of this file are scoped `pg-account .row`, not `.row`. Generic
 * widgets (pg-pill, pg-toast, pg-icon, the nav bar) keep their shadow roots and
 * their own styles; they are widgets, and encapsulation is what they want.
 *
 * Every colour here is a token from main.css. Layout values live here only.
 */

/* --- Page shell ---------------------------------------------------------
 *
 * Was `:host` in pageHeaderStyles. A light-DOM component is styled by its own
 * tag, so page components are listed here. Add a new page's tag when you add
 * the page — nothing will tell you, the page will simply lay out wrong. */
pg-login,
pg-verify-login,
pg-activate,
pg-account,
pg-confirm-email-change,
pg-confirm-account-closure,
.home-page,
pg-write-posts,
pg-write-post-edit,
pg-docs {
	display: flex;
	flex-direction: column;
	align-items: center;
	width: 100vw;
	overflow-x: hidden;
}

/* --- Header band --------------------------------------------------------- */

.header {
	width: 100%;
	background: var(--dark-color-1);
	border-bottom: 1px solid var(--hairline);
	display: flex;
	justify-content: center;
	padding: 56px 24px 48px;
	box-sizing: border-box;
}

.header-inner {
	width: 100%;
	max-width: 860px;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.eyebrow {
	font-size: 0.78rem;
	font-weight: 600;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--signal-color);
	margin: 0;
}

.header h1 {
	font-size: clamp(2rem, 5vw, 3rem);
	font-weight: 700;
	letter-spacing: -0.025em;
	line-height: 1.05;
	margin: 0;
	color: var(--light-color-1);
}

/* --- Sections ------------------------------------------------------------ */

.section {
	width: 100%;
	padding: 56px 24px;
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 24px;
}

.inner {
	width: 100%;
	max-width: 860px;
	display: flex;
	flex-direction: column;
	gap: 20px;
}

/* Scoped to .section rather than bare, unlike the shadow-DOM original: in the
   document these would otherwise restyle every heading and paragraph on the
   page, including inside the blog's prose. */
.section h2 {
	font-size: clamp(1.3rem, 3vw, 1.8rem);
	font-weight: 700;
	letter-spacing: -0.02em;
	margin: 0;
	color: var(--light-color-1);
}

.section h3 {
	font-size: 1rem;
	font-weight: 600;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	margin: 0;
	color: var(--light-color-4);
	padding-top: 8px;
	border-top: 1px solid var(--hairline);
}

.section p {
	font-size: 0.95rem;
	line-height: 1.65;
	color: var(--light-color-4);
	margin: 0;
}

.section ul,
.section ol {
	margin: 0;
	padding-left: 20px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.section li {
	font-size: 0.95rem;
	line-height: 1.65;
	color: var(--light-color-4);
}

/* --- Card ---------------------------------------------------------------- */

/* Surface for the contents of a page section: a raised panel, one step above
   the page's near-black background and separated from it by a hairline rather
   than a shadow. */
.card {
	background: var(--dark-color-1);
	border: 1px solid var(--hairline);
	border-radius: 12px;
	padding: 20px 24px;
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	gap: 20px;
}

.card form {
	margin: 0;
}

/* --- Forms --------------------------------------------------------------- */

form {
	display: flex;
	flex-direction: column;
	gap: 20px;
}

form label {
	background-color: var(--lighten-color-1);
	border-radius: 5px 5px 0 0;
	display: flex;
	align-items: flex-start;
	justify-content: flex-start;
	flex-direction: column;
}

form label span {
	user-select: none;
	-webkit-user-select: none;
	padding: 16px;
	display: inline-flex;
}

form label.checkbox {
	flex-direction: row;
	align-items: center;
}

form label.checkbox span {
	white-space: pre-wrap;
}

form label.checkbox input {
	cursor: pointer;
}

/* Tinted, not filled: a solid block of the alarm colour on a near-black page
   is a hole punched in the layout. */
form .error {
	color: var(--alarm);
	background-color: var(--alarm-tint);
	overflow-wrap: anywhere;
}

/* --- Inputs -------------------------------------------------------------- */

input, textarea, select {
	width: 100%;
	max-width: 100%;
	min-width: 100%;
	padding: 16px;
	box-sizing: border-box;
	font-size: 1em;
	font-family: inherit;
	background-color: var(--darken-color-1);
	color: var(--light-color-1);
	transition: background-color 200ms, border-color 200ms;
	border-bottom: 1px solid var(--light-color-4);
	border-top: 0;
	border-left: 0;
	border-right: 0;
	outline: 0;
	color-scheme: dark;
}

textarea {
	min-height: 200px;
}

input::placeholder {
	color: var(--light-color-4);
}

input:hover:not(:focus):not([readonly]),
textarea:hover:not(:focus):not([readonly]),
select:hover:not(:focus):not([readonly]) {
	border-color: var(--light-color-1);
}

input[readonly], textarea[readonly], select[readonly] {
	background-color: var(--lighten-color-1);
}

/* main.css's blanket :focus-visible ring is right for links and buttons and
   wrong here. A text field matches :focus-visible on a plain mouse click — the
   browser assumes anything you can type into wants a visible focus — so every
   click into a field drew a red box around it. The field states focus with its
   own underline instead, doubled to 2px via an inset shadow so the cue is not
   colour alone and does not reflow the layout. */
input:focus:not([readonly]),
textarea:focus:not([readonly]),
select:focus:not([readonly]) {
	border-color: var(--signal-color);
	color: var(--light-color-1);
	outline: 0;
	box-shadow: inset 0 -1px 0 var(--signal-color);
}

/* Chrome paints an autofilled field itself and ignores background-color on it,
   so the well has to be restored two ways. color-scheme above is what stops it
   picking the near-white fill; the inset shadow covers the blue-grey one it
   uses instead, because the fill is a background and only a shadow lands on
   top of one. Text has to go through -webkit-text-fill-color — `color` is the
   property Chrome overrides.

   --darken-color-2, not --darken-color-1: a shadow does not composite the way
   a background does, so it needs an opaque colour, and --darken-color-2 is
   already 32% black over --dark-color-1 resolved.

   An inset shadow is clipped to the padding box, so the border-bottom still
   shows through and states hover and focus as it does everywhere else. The
   :focus rule below only restores the doubling, and lists the underline first
   because shadows paint front-to-back — behind the 1000px fill it would be
   invisible. */
input:-webkit-autofill,
textarea:-webkit-autofill,
select:-webkit-autofill {
	box-shadow: inset 0 0 0 1000px var(--darken-color-2);
	-webkit-text-fill-color: var(--light-color-1);
	caret-color: var(--light-color-1);
}

input:-webkit-autofill:focus:not([readonly]),
textarea:-webkit-autofill:focus:not([readonly]),
select:-webkit-autofill:focus:not([readonly]) {
	box-shadow:
		inset 0 -1px 0 var(--signal-color),
		inset 0 0 0 1000px var(--darken-color-2);
}

/* The preview — the value Chrome draws in the field while a suggestion is
   highlighted — is laid out at the UA's own 13.3px input default and ignores
   the field's font entirely, so it steps up in size the moment it is
   committed. crbug.com/41471071, open since 2019. ::first-line is the only
   selector that reaches that text, and a preview is always one line.

   Restating the two font declarations from the block above rather than
   inheriting them is the point of the rule: on ::first-line they resolve
   against the input, which is where the size Chrome should have used lives. */
input:-webkit-autofill::first-line {
	font-size: 1em;
	font-family: inherit;
}

input[type="checkbox"], input[type="radio"] {
	width: 20px;
	height: 20px;
	min-width: auto;
	margin-left: 16px;
	padding: 0;
}

/* These have no underline to state focus with, and the border above is the
   browser's to draw, so they keep the ring the rule above turns off. */
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
	outline: 2px solid var(--signal-color);
	outline-offset: 2px;
	box-shadow: none;
}

/* --- Buttons ------------------------------------------------------------- */

/* Chalk, not the accent. A white fill is the strongest affordance on a
   near-black page, and it leaves red to mean consequence — .danger below is the
   red button, and the two can no longer be read as shades of one thing. */
.btn {
	border: 0 solid var(--light-color-4);
	border-radius: 5px;
	background-color: var(--chalk);
	cursor: pointer;
	color: var(--void);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 10px 16px;
	min-width: 60px;
	transition-duration: 300ms;
	/* Comma-separated. This was space-separated, which is invalid and made
	   the whole declaration drop, so nothing transitioned at all. */
	transition-property: border-color, background-color;
	user-select: none;
	-webkit-user-select: none;
	gap: 5px;
	font-size: 1em;
	font-family: inherit;
}

.btn:hover:not([disabled]) {
	background-color: var(--chalk-dim);
}

/* The ring is the signal colour, not the label colour: on a chalk fill a
   near-white outline is invisible where it crosses the button's own edge. */
.btn:focus-visible {
	outline: 2px solid var(--signal-color);
	outline-offset: 2px;
}

/* The fill has to go, not just the label. This used to override only the
   colour, leaving muted grey text on the full-strength fill — 1.05:1, which is
   unreadable rather than merely inactive. */
.btn[disabled] {
	background-color: var(--lighten-color-2);
	color: var(--light-color-3);
	cursor: not-allowed;
}

/* Variants used across more than one page. */
.btn.quiet {
	background-color: transparent;
	border: 1px solid var(--light-color-4);
	color: var(--light-color-1);
}

.btn.quiet:hover:not([disabled]) {
	background-color: var(--lighten-color-1);
}

/* Dark label on the fill, as .btn has on chalk — a near-white one reaches only
   3.0:1 against this red. */
.btn.danger:not([disabled]) {
	background-color: var(--carmine);
	color: var(--void);
}

.btn.danger:hover:not([disabled]) {
	background-color: var(--redline-hi);
}

/* --- Links --------------------------------------------------------------- */

/* Links take the signal colour — the one hue on the page, and navigation is
   what it is for on a reading page.
   Scoped under .section and .header rather than bare, so the nav bar, the
   blog's prose and anything else with its own link treatment are untouched. */
.section a, .header a {
	text-decoration: underline;
	transition: color 300ms;
	color: var(--signal-color);
	display: inline-flex;
	gap: 5px;
	cursor: pointer;
}

/* Was outline: 0 with only a colour shift to mark focus, which is not
   enough on its own. :focus-visible keeps it off mouse clicks. */
.section a:focus-visible, .header a:focus-visible {
	outline: 2px solid var(--signal-color);
	outline-offset: 2px;
	color: var(--light-color-1);
}

.section a:hover, .header a:hover {
	color: var(--light-color-1);
}

/* A link styled as a button. `.section a` outranks `.btn` on specificity, so
   without these the front page's calls to action take the link treatment —
   accent-coloured, underlined — on top of the button's own fill, and the label
   fights the surface it sits on. Same reason public.css qualifies `.join-btn`. */
.section a.btn, .header a.btn,
.section a.btn:hover, .header a.btn:hover,
.section a.btn:focus-visible, .header a.btn:focus-visible {
	color: var(--void);
	text-decoration: none;
}

.section a.btn.quiet, .header a.btn.quiet,
.section a.btn.quiet:hover, .header a.btn.quiet:hover,
.section a.btn.quiet:focus-visible, .header a.btn.quiet:focus-visible {
	color: var(--light-color-1);
}

.teaser {
	border-radius: 5px;
	cursor: pointer;
	outline: 1px solid transparent;
	transition: outline-color 200ms;
	user-select: none;
	-webkit-user-select: none;
}

.teaser:hover {
	outline-color: var(--hairline-strong);
}

/* --- Shared row and metadata patterns ------------------------------------ */

/* Used by the account page and, in the same shape, by anything listing
   editable facts with actions on the right. */
.row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
}

.row + .row {
	border-top: 1px solid var(--hairline);
	padding-top: 16px;
}

.row-main {
	display: flex;
	flex-direction: column;
	gap: 4px;
	min-width: 0;
}

.row-title {
	color: var(--light-color-1);
	font-weight: 600;
	overflow-wrap: anywhere;
}

.row-sub {
	font-size: 0.85rem;
	color: var(--light-color-4);
}

.actions {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
	align-items: center;
}

/* Read-only facts sitting under a page title as header metadata rather than in
   a card — a card, on every other section, means "there is something to do in
   here". The <div> wrappers around each dt/dd pair are what let the row lay out
   as columns; a bare dl cannot, and they are valid children of a dl. */
.meta {
	margin: 8px 0 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 16px 40px;
}

.meta > div {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

/* Deliberately the eyebrow's treatment: it ties the row to the header it lives
   in instead of reading as a transplanted list. */
.meta dt {
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--light-color-4);
}

.meta dd {
	margin: 0;
	font-size: 0.95rem;
	color: var(--light-color-1);
	overflow-wrap: anywhere;
}

/* --- Per-component -------------------------------------------------------
 *
 * Scoped by element name. This is what replaces shadow-DOM encapsulation for
 * light-DOM components: `pg-account .danger-card`, never a bare `.danger-card`.
 */

pg-account .danger-card {
	border-color: var(--carmine);
}

pg-confirm-account-closure .card {
	border-color: var(--carmine);
}

pg-login .divider {
	display: flex;
	align-items: center;
	gap: 12px;
	color: var(--light-color-4);
	font-size: 0.85em;
}

pg-login .divider::before,
pg-login .divider::after {
	content: "";
	flex: 1;
	height: 1px;
	background-color: var(--light-color-4);
}

pg-login button.secondary {
	background-color: transparent;
	border: 1px solid var(--light-color-4);
	color: var(--light-color-1);
}

pg-login button.secondary:hover:not([disabled]) {
	background-color: var(--lighten-color-1);
	border-color: var(--signal-color);
}

pg-activate pg-pill.success {
	color: var(--ok);
}

pg-verify-login .resend-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	font-size: 0.9em;
	color: var(--light-color-4);
}

pg-verify-login .resend-wait {
	animation: fade-in 200ms ease-out;
}

pg-verify-login .countdown {
	font-variant-numeric: tabular-nums;
	color: var(--light-color-1);
	font-weight: 600;
}

pg-verify-login .resend {
	background: none;
	border: none;
	padding: 0;
	color: var(--signal-color);
	text-decoration: underline;
	cursor: pointer;
	font-size: inherit;
	font-family: inherit;
	animation: fade-in 200ms ease-out;
}

pg-verify-login .resend[disabled] {
	color: var(--light-color-4);
	text-decoration: none;
	cursor: default;
}

/* --- .home-page ----------------------------------------------------------
 *
 * **A class and not a tag, because the front page is not a component.** It is
 * server-rendered by app/public_render.go, like the blog, and every other block
 * in this sheet is scoped by an element name only because a Lit page component
 * is styled by its own tag. Emitting a <pg-home> that no module defines would
 * cost more than the rename saved: util/selfcheck.js reports an undefined
 * element as a silent fault, and an exemption there to accommodate one
 * deliberate case is how that output stops being worth reading.
 *
 * The front page is the only multi-band page on the site, and the only one
 * whose job is to be read rather than used. It reuses `.header`, `.section` and
 * `.inner` unchanged, so it shares a column and a measure with every other
 * page; what it adds is air, display type and the hero photograph.
 *
 * Two bands — the goal, and the latest updates. A band earns its place by
 * being one of those, not by having copy that needs somewhere to go, and what
 * the wing is for is deliberately not one of them (app/public_render.go).
 *
 * The air is the separator. There are no cards on this page and there must not
 * be: `.card` means "there is something to do in here" everywhere else, and a
 * stack of them reads as a feature list, which is the one thing this page
 * cannot be. If the bands stop reading as distinct, add space — not a surface.
 */

.home-page .header {
	padding: 88px 24px 72px;
}

/* --- the hero photograph ---
 *
 * The header band becomes the frame for a full-bleed photo. Three rules make
 * that safe rather than pretty:
 *
 * 1. `position: relative` on the band and `absolute` on the media, so the photo
 *    takes the band's height from the text rather than dictating it. The band
 *    is a paragraph tall on a phone and a headline tall on a desktop, and a
 *    16:9 photo asked to set that height would leave a letterbox on one and
 *    crop the pilot out of the other.
 * 2. `object-fit: cover` with the focus held right of centre. The frame's
 *    subject — the feet, the sun, the line down the slab — sits centre-right,
 *    and a narrow viewport cropping from the true centre keeps the empty slab
 *    and loses the flying.
 * 3. The scrim is not optional and it is not decoration. `.lead` is
 *    --light-color-3 on what would otherwise be a lit granite slab and a
 *    sunrise; it clears 4.5:1 because of the layer below and nowhere near it
 *    without. Anything added to this band has to survive the same test.
 */

.home-page .header.hero {
	position: relative;
	isolation: isolate;
	/* The scrim ends on --void, so the band dissolves into the page instead of
	   ending on a line. */
	border-bottom: none;

	/* Tall enough for the photograph to be a place rather than a texture, and
	   capped so it does not become a full-screen splash the reader has to get
	   past: the top edge of the band below should be visible on a laptop,
	   because the hero states the goal and the next band says where the project
	   actually is, and a reader who sees only the first has the half that
	   flatters. `min-height`, not `height` — long copy or a large type size
	   grows the band instead of overflowing it.

	   svh, not vh, on the phones that support it: with vh the band is sized to
	   the viewport *without* the browser's collapsing URL bar, so the first
	   paint is a hero that overshoots the screen by the height of that bar. The
	   vh line is the fallback and has to come first. */
	min-height: min(78vh, 880px);
	min-height: min(78svh, 880px);

	/* .header is a flex row that centres its single column horizontally; this
	   drops that column to the foot of the band, where the scrim is heaviest and
	   the frame is emptiest. */
	align-items: flex-end;

	padding-top: 160px;
	padding-bottom: 104px;
}

.home-page .hero-media {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 62% 50%;
	z-index: -2;
}

.home-page .hero-scrim {
	position: absolute;
	inset: 0;
	z-index: -1;
	/* Lighter at the top, where the sun and the sky are and no text sits, and
	   heaviest across the lead and the buttons. The last stop is the page
	   colour at full opacity, which is what removes the seam. */
	background: linear-gradient(
		180deg,
		rgba(10, 10, 11, 0.38) 0%,
		rgba(10, 10, 11, 0.62) 32%,
		rgba(10, 10, 11, 0.84) 66%,
		var(--void) 100%
	);
}

/* The photo is a backdrop, so it gets no motion and no parallax — but a
   reader who has asked for less movement should also not meet a 4K frame
   sharpening in as it decodes. Nothing here animates; this is a note for
   whoever is tempted to add it. */

@media (max-width: 620px) {
	/* On a phone the band is short and the scrim's mid stops land above the
	   text. Push the dark end up. */
	.home-page .hero-scrim {
		background: linear-gradient(
			180deg,
			rgba(10, 10, 11, 0.45) 0%,
			rgba(10, 10, 11, 0.78) 45%,
			var(--void) 100%
		);
	}
}

.home-page .header-inner {
	gap: 20px;
}

/* Wider than the h1's tracking suggests, and held to a measure: this is the
   only paragraph on the site that has to be read by someone deciding whether
   to keep reading. `.section p` does not reach into `.header`. */
.home-page .lead {
	font-size: 1.05rem;
	line-height: 1.6;
	color: var(--light-color-3);
	margin: 0;
	max-width: 62ch;
}

.home-page .section {
	padding: 72px 24px;
	gap: 28px;
}

/* The band statement. Larger than `.section h2` and set tighter, because on
   this page a heading is a sentence the reader is meant to finish rather than
   a label on the block below it. */
.home-page .statement {
	font-size: clamp(1.5rem, 3.4vw, 2.1rem);
	line-height: 1.15;
	letter-spacing: -0.025em;
	max-width: 24ch;
}

.home-page .inner > p {
	max-width: 68ch;
}

.home-page .actions {
	gap: 12px;
	padding-top: 8px;
}

@media (max-width: 620px) {
	.home-page .header {
		padding: 56px 24px 48px;
	}

	/* The hero needs its own line here: `.header.hero` outranks `.header`, so
	   the rule above does not reach it and the band would keep its desktop
	   264px of padding on a screen a third the height.

	   Both the padding and the floor come down. On a phone the copy alone is
	   most of the band, so a 78svh floor and desktop air together push the band
	   below it a full screen under the fold, which is exactly what the cap
	   upstairs exists to prevent. Enough height left for the photograph to still
	   read as a place.

	   vh first as the fallback, svh second, for the collapsing-URL-bar reason
	   given on the desktop rule.

	   padding-top is the one number here that is not a taste judgement. The bar
	   is floated over the photograph (main.css, `body:has(.hero) pg-nav-bar`),
	   so this padding is the only thing holding the copy clear of it, and it
	   has to clear the bar at its tallest. **The bar is one row and 60px at
	   every width**, which nav/bar.js works to keep true on a phone — it used
	   to wrap its search onto a second line and its account group onto a third,
	   standing 152px tall at 360px, and the eyebrow rendered underneath it and
	   was invisible. 84 is that 60 plus 24 of air. **If the bar ever grows a
	   row again this is the number that moves**, which is most of why the
	   phone layout over there is written the way it is. */
	.home-page .header.hero {
		min-height: min(60vh, 560px);
		min-height: min(60svh, 560px);
		padding-top: 84px;
		padding-bottom: 56px;
	}

	.home-page .section {
		padding: 48px 24px;
	}
}

/* --- Footer -------------------------------------------------------------- */

/* Site-wide chrome, on the server-rendered pages and on the SPA alike. It lived
   in public.css while only the blog had one, and moved here when it grew the
   links to /contact, /terms and /privacy: a privacy notice reachable only from
   the pages a stranger lands on is unreachable from every page where an address
   is actually typed in.

   It is written twice — in app/public_render.go's shell and in site/index.html
   — for the same reason <pg-nav-bar> is: two entry points, no build step, and
   nothing to share a fragment between them. The markup is the same and this is
   the one place its rules live.

   **The links in index.html carry data-native.** There is no route for any of
   these three in site/index.js, so without it the router claims the click and
   renders an empty page. See components/app/router.js.

   A quiet closing rule rather than a band, so it reads as the end of the page
   and not as another section of it. */
.page-footer {
	width: 100%;
	max-width: 860px;
	box-sizing: border-box;
	padding: 24px;
	margin: 0 auto;
	border-top: 1px solid var(--hairline);
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px 24px;
}

.page-footer p {
	margin: 0;
	font-size: 0.82rem;
	color: var(--light-color-4);
}

.footer-links {
	display: flex;
	flex-wrap: wrap;
	gap: 20px;
}

/* Sized and coloured with the line beside it rather than as body links: these
   are the least important links on any page they appear on, and the .section
   link treatment would make them the loudest thing in the footer. */
.footer-links a {
	font-size: 0.82rem;
	color: var(--light-color-4);
	text-decoration: none;
}

.footer-links a:hover {
	color: var(--light-color-1);
	text-decoration: underline;
}

/* --- Utilities ----------------------------------------------------------- */

/* Honeypot field. Positioned off-screen rather than display:none, because a
   bot that reads computed styles skips a hidden field and fills a visible one. */
.hp-field {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

@keyframes fade-in {
	from { opacity: 0; }
	to { opacity: 1; }
}
