/*
 * Site footer — Figma node 6196:4606 (1425×990).
 *
 * Full-bleed photo with a black panel ("Main Footer", 1385×307 at 1425w,
 * fill #000000, corner radius 0) pinned to the bottom edge with 20px insets.
 * Exact fills read from Figma selection colors: panel #000000, cream text
 * #f9f4ee, muted text #7e7975, input rule #3d3a3b (white @20% also present).
 * Enqueued by inc/hooks/enqueue.php (hiplaces-site-footer).
 */

.hp-footer {
	position: relative;
	/*
	 * The photo band: the footer's top padding IS the height of the visible
	 * photo, because the opaque panel covers everything below it. Below 1180px
	 * the photo layer is bound to this same token (see the crop block after
	 * .hp-footer__bg), so the two can never drift apart when a breakpoint is
	 * re-tuned.
	 */
	--hp-footer-photo-h: 660px;
	/* Dark surface: every focus indicator inside the footer flips to paper.
	   See the --hp-focus-* token block in style.css. */
	--hp-focus-color: var(--hp-color-paper);
	--hp-focus-contrast: var(--hp-color-ink);
	/* Split, not `padding: var(--hp-footer-photo-h) 20px 20px`: a custom
	   property that fails to parse is invalid at computed-value time and takes
	   the WHOLE shorthand with it, so one bad token would drop the 20px side
	   gutters too and the panel would lose its inset-card look at every width.
	   This way a bad token can only affect the top. */
	padding: 0 20px 20px;
	padding-top: var(--hp-footer-photo-h);
	background-color: var(--hp-color-dark);
}

.hp-footer__bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

/*
 * Mobile/tablet crop (client feedback 2026-09, item 3: "the crop should be
 * focused on the subject not just default to the center").
 *
 * Desktop is untouched: at 1440 the box is 1425x995 (1.43:1) against the
 * photos' ~1.5:1, so cover keeps ~95% of the width and the design reads as
 * intended. Below the desktop breakpoint the panel stacks and the footer turns
 * portrait -- 360x1033 (0.35:1) on a 375px phone -- so cover threw away ~77%
 * of the photo's WIDTH, and because a portrait box leaves ZERO vertical slack
 * object-position's Y was a mathematical no-op: the band could only ever show
 * the TOP of the image (measured: the top ~23% at 375px). That is why a focal
 * point alone cannot fix this, and why the geometry change below is the actual
 * fix rather than a refinement.
 *
 * Binding the layer to the band restores a landscape box (360x280 = 1.29:1)
 * and with it both axes: measured 86% of the width and 100% of the height
 * visible at 375px, against 23% x top-quarter before.
 *
 * --hp-footer-focal is set per photo as an inline custom property (footer.php
 * for the base image, assets/js/footer-bg-carousel.js for the rotation layers)
 * and is consumed ONLY here, so every photo without one -- and every photo on
 * desktop -- keeps today's centred crop.
 *
 * Deliberately OUTSIDE the motion gate: a reduced-motion visitor must get the
 * same corrected crop, just without the parallax overscale.
 *
 * The ::after scrim is load-bearing, not decoration. A transform paints past
 * its border box, so the 1.18 parallax overscale reaches ~25px below the band
 * and would show as a photo sliver in the 20px strips BESIDE the panel (the
 * panel only spans x 20..W-20). The scrim fills everything under the band with
 * the panel's own black, so the panel reads full-bleed there. The panel needs
 * its z-index because ::after is the footer's LAST child and would otherwise
 * paint over it.
 */
@media (max-width: 1180px) {
	.hp-footer__bg {
		bottom: auto;
		height: var(--hp-footer-photo-h);
		object-position: var(--hp-footer-focal, 50% 50%);
	}

	/* Matches the scrim and the panel, so the band does not show a lighter
	   #1a1a1a strip against #000000 while the lazy-loaded photo is still
	   fetching (the footer is near the document bottom, so it usually is). */
	.hp-footer {
		background-color: #000000;
	}

	.hp-footer::after {
		content: "";
		position: absolute;
		z-index: 0;
		inset: var(--hp-footer-photo-h) 0 0;
		background-color: #000000;
	}

	.hp-footer__panel {
		z-index: 1;
	}
}

/*
 * Background rotation (footer-bg-carousel.js): incoming photos are stacked
 * as sibling .hp-footer__bg layers before the panel and cross-faded
 * opacity-only. They inherit the parallax drift by class, so every layer
 * scrubs in sync. The transition is motion-gated; without JS no layer is
 * ever added and these rules are inert.
 */
.hp-footer__bg.is-entering {
	opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
	.hp-footer__bg {
		transition: opacity 1.8s ease;
	}
}

/*
 * Panel grid (desktop): brand 318px | contact 390px | gallery pinned right.
 * Row 1 flexes; row 2 is the full-width bottom row (.hp-footer__meta: legal
 * links flush left, copyright flush right under the gallery — client round
 * 2026-10, items 12-13).
 */
.hp-footer__panel {
	position: relative;
	display: grid;
	grid-template-columns: 318px minmax(0, 390px) 1fr;
	grid-template-rows: 1fr auto;
	/*
	 * Caps at the 1385px design panel and lands the content on the site's
	 * 40/52 gutter lines: the .hp-footer wrapper already insets 20px, so the
	 * panel adds the difference (max() keeps a 20px floor on small screens
	 * where the tokens flip to 20 and the difference would hit 0).
	 */
	max-width: calc(var(--hp-container-max) - 40px);
	margin-inline: auto;
	padding: 20px max(20px, calc(var(--hp-pad-right) - 20px)) 20px max(20px, calc(var(--hp-pad-left) - 20px));
	background-color: #000000;
	color: #f9f4ee;
	font-family: var(--hp-font-body);
	font-weight: 300;
}

/*
 * Footer text links: the indicator is the link's own underline, thickened to
 * 2px in paper and dropped clear of the descenders (client feedback 2026-09,
 * item 5 — the old blanket rectangle boxed six text links, two image links and
 * the submit button alike, and read as a browser default).
 *
 * The two IMAGE links in the footer (.hp-footer__logo and a.hp-footer__tile)
 * are deliberately NOT in this list: they have no text to underline, and their
 * box IS their shape, so they keep the global ring from assets/css/focus.css.
 * A new footer text link must be added to this selector list.
 *
 * text-decoration-color: currentcolor is required because .hp-footer__legal-link
 * sets it to the muted grey.
 */
.hp-footer__nav a:focus-visible,
.hp-footer__social-link:focus-visible,
.hp-footer__gallery-handle a:focus-visible,
.hp-footer__legal-link:focus-visible {
	outline: none;
	color: var(--hp-color-paper);
	text-decoration: underline;
	text-decoration-color: currentcolor;
	text-decoration-thickness: var(--hp-focus-underline);
	/* 6px, not the 4px token: .hp-footer__legal-link is ALREADY underlined at
	   3px, so matching the token would make focus a ~1px non-event. */
	text-underline-offset: 6px;
}

/* Brand: logo + footer menu. */

.hp-footer__brand {
	grid-column: 1;
	grid-row: 1;
}

.hp-footer__logo {
	display: inline-block;
}

.hp-footer__logo img {
	display: block;
	width: 99px;
	height: 44px;
}

.hp-footer__nav {
	margin-top: 26px;
}

.hp-footer__nav ul {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.hp-footer__nav a {
	color: #f9f4ee;
	font-size: 14px;
	line-height: 22px;
	text-decoration: none;
}

.hp-footer__nav a:hover {
	color: var(--hp-color-paper);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* Contact: heading, blurb, newsletter sign-up, social links. */

.hp-footer__contact {
	display: flex;
	flex-direction: column;
	grid-column: 2;
	grid-row: 1 / 3;
}

.hp-footer__heading {
	margin: 0 0 10px;
	font-family: var(--hp-font-display);
	font-weight: 400;
	font-size: 25px;
	line-height: 1.2;
	color: #f9f4ee;
}

.hp-footer__text {
	margin: 0 0 16px;
	font-size: 14px;
	line-height: 22px;
}

/* The signup form submits into a prefilled mailto compose (client request
   2026-09, footer-subscribe.js) — there is deliberately no backend. */
.hp-footer__signup {
	display: flex;
	align-items: center;
	/* #3d3a3b over the footer's black was 1.87:1 -- the only visible boundary of
	   a form control, and well under the 3:1 of SC 1.4.11. The theme's own muted
	   token clears it at 4.9:1. */
	border-bottom: 1px solid var(--hp-color-muted);
}

.hp-footer__signup:focus-within {
	border-bottom-color: var(--hp-color-paper);
	/*
	 * The field's focus indicator: the form's own rule goes from 1px muted to
	 * a 3px paper band. A box-shadow rather than a border-width change, so the
	 * row's height never moves; offset 0 2px with no blur or spread paints
	 * exactly a 2px strip directly under the 1px border, contiguous with it.
	 */
	box-shadow: 0 2px 0 0 var(--hp-color-paper);
}

.hp-footer__email {
	flex: 1;
	min-width: 0;
	padding: 12px 0 10px;
	border: 0;
	background: transparent;
	color: #f9f4ee;
	font-family: var(--hp-font-body);
	font-size: 13px;
	line-height: 20px;
	caret-color: var(--hp-color-paper);
	/* iOS paints its own inner chrome on a type=email with a transparent
	   background; matches .hp-header__toggle and .hp-say-hi__close. */
	-webkit-appearance: none;
	appearance: none;
}

.hp-footer__email::placeholder {
	color: #7e7975;
	opacity: 1;
}

/*
 * THE bug the client reported (2026-09, item 5, screenshot 1): "a white box
 * outline shows up around a lot of the clickable areas, including the footer
 * email field".
 *
 * A text input matches :focus-visible on ANY focus per spec, not just keyboard
 * focus, so the old 2px paper outline at a 4px offset fired on tap and during
 * the iOS "Hide My Email" flow too — a white rectangle around the whole
 * flex: 1 input on the black panel. The indicator is now the form's own rule
 * (see :focus-within above), which reads as "this field is active" on every
 * input modality and cannot be mistaken for a browser default.
 *
 * `outline: none` on :focus as WELL as :focus-visible: without the plain
 * :focus the UA ring simply takes the deleted rule's place.
 *
 * WCAG: SC 2.4.7 (AA) is met — the rule changes colour AND thickness, and
 * paper on #000000 is 21:1, clearing SC 1.4.11 (AA). SC 2.4.13 Focus
 * Appearance is AAA in published WCAG 2.2 and its minimum-area part is not met
 * by a bottom rule; that is the accepted trade, and it is the criterion the
 * comment here used to call "SC 2.4.11" (its number in the drafts only).
 */
.hp-footer__email:focus,
.hp-footer__email:focus-visible {
	outline: none;
}

/*
 * Autofill guard. Safari paints its own background on an autofilled input,
 * which on a transparent field over the black panel renders as a filled light
 * box with dark text. The client's third screenshot shows the Hide My Email
 * popover open on this field, so this is very likely the other half of what
 * they photographed. The box-shadow lives on the INPUT while the focus
 * indicator's box-shadow lives on the FORM, so the two never collide.
 */
.hp-footer__email:-webkit-autofill,
.hp-footer__email:-webkit-autofill:hover,
.hp-footer__email:-webkit-autofill:focus {
	-webkit-text-fill-color: #f9f4ee;
	-webkit-box-shadow: 0 0 0 1000px #000000 inset;
	caret-color: var(--hp-color-paper);
}

.hp-footer__submit {
	position: relative;
	z-index: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 8px 4px 8px 12px;
	border: 0;
	background: transparent;
	cursor: pointer;
}

/*
 * Icon-button focus language: an inverted disc behind the glyph, not a ring.
 * Centred on the 16px arrow rather than on the button box, whose padding is
 * deliberately asymmetric so the arrow sits near the rule's right end. The
 * 4px of right padding above keeps the disc inside the button at every
 * breakpoint, so it can never overhang the gallery column.
 *
 * Declared only inside :focus-visible, so there is no hidden state for the
 * html.js + reduced-motion gate to worry about. z-index -1 inside the button's
 * own stacking context puts it behind the glyph — the .hp-button::after
 * pattern.
 */
.hp-footer__submit:focus-visible {
	outline: none;
}

.hp-footer__submit:focus-visible::before {
	content: "";
	position: absolute;
	top: 50%;
	right: 0;
	z-index: -1;
	width: 24px;
	height: 24px;
	margin-top: -12px;
	border-radius: 50%;
	background: var(--hp-focus-color);
}

/* The exported arrow asset is black and inverted for the dark panel; on the
   paper disc it goes back to black. */
.hp-footer__submit:focus-visible img {
	filter: none;
}

/* The exported arrow asset is black; invert it for the black panel. */
.hp-footer__submit img {
	display: block;
	width: 16px;
	height: 16px;
	filter: invert(1);
}

@media (prefers-reduced-motion: no-preference) {
	.hp-footer__submit img {
		transition: transform 150ms ease;
	}

	.hp-footer__submit:hover img {
		transform: translateX(3px);
	}
}

.hp-footer__social {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 42px;
	margin: auto 0 0;
	padding: 24px 0 0;
	list-style: none;
}

.hp-footer__social-link {
	color: #f9f4ee;
	font-size: 13px;
	line-height: 16px;
	text-decoration: none;
}

.hp-footer__social-link:hover {
	color: var(--hp-color-paper);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* Gallery: 2×2 photo grid pinned to the panel's right edge. */

.hp-footer__gallery {
	grid-column: 3;
	grid-row: 1;
	justify-self: end;
	width: 338px;
	max-width: 100%;
	margin: 0;
}

.hp-footer__gallery-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 11px 18px;
}

/* Tiles: the aspect ratio lives on the wrapper (not the img) so the rotation
   script can stack an incoming img over the current one and cross-fade. */
.hp-footer__tile {
	position: relative;
	display: block;
	overflow: hidden;
	aspect-ratio: 160 / 118;
}

.hp-footer__tile-img {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Incoming rotation image: the hidden state is JS-created (never
   server-rendered), so no html.js gate is needed; the fade itself stays
   behind the motion gate — reduced motion never runs the rotation at all. */
.hp-footer__tile-img.is-entering {
	opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
	.hp-footer__tile-img {
		transition: opacity 0.8s ease;
	}
}

.hp-footer__gallery-handle {
	margin-top: 12px;
	color: #7e7975;
	font-size: 12px;
	line-height: 16px;
}

.hp-footer__gallery-handle a {
	color: inherit;
	text-decoration: none;
}

.hp-footer__gallery-handle a:hover {
	color: var(--hp-color-paper);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/*
 * Bottom row — legal links flush left, copyright flush right under the
 * gallery (client round 2026-10, items 12-13). One flex row spanning the
 * panel: both ends land on the panel's padding edges, i.e. the brand's left
 * edge and the gallery's right edge, with no per-column placement to keep
 * in sync. margin-left: auto (not space-between) keeps the copyright right
 * even when no legal page is published and the nav is not rendered.
 */
.hp-footer__meta {
	grid-column: 1 / -1;
	grid-row: 2;
	display: flex;
	align-items: flex-end;
	gap: 16px 24px;
	margin-top: 20px;
	font-size: 12px;
	line-height: 16px;
}

.hp-footer__copyright {
	margin: 0 0 0 auto;
	text-align: right;
}

.hp-footer__legal-sep {
	margin: 0 8px;
	color: var(--hp-color-muted);
}

.hp-footer__legal-link {
	color: inherit;
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-color: var(--hp-color-muted);
}

.hp-footer__legal-link:hover {
	text-decoration-color: currentcolor;
}

/* Tablet: brand + contact side by side, gallery full width below. */
@media (max-width: 1180px) {
	.hp-footer {
		--hp-footer-photo-h: 420px;
	}

	.hp-footer__panel {
		grid-template-columns: 1fr 1fr;
		grid-template-rows: auto;
		gap: 40px;
	}

	.hp-footer__brand {
		grid-column: 1;
		grid-row: 1;
	}

	.hp-footer__contact {
		grid-column: 2;
		grid-row: 1;
	}

	.hp-footer__social {
		margin-top: 32px;
	}

	.hp-footer__gallery {
		grid-column: 1 / 3;
		grid-row: 2;
		justify-self: stretch;
		width: auto;
	}

	.hp-footer__meta {
		grid-column: 1 / 3;
		grid-row: 3;
		margin-top: 0; /* the panel's 40px gap already separates it from the gallery */
	}
}

/* Mobile: single column; fluid widths keep 375px free of horizontal scroll. */
@media (max-width: 768px) {
	.hp-footer {
		--hp-footer-photo-h: 280px;
	}

	.hp-footer__panel {
		grid-template-columns: 1fr;
		gap: 36px;
	}

	.hp-footer__brand,
	.hp-footer__contact,
	.hp-footer__gallery,
	.hp-footer__meta {
		grid-column: 1;
		grid-row: auto;
	}

	/* Stacked: links first, copyright under them, both flush left. */
	.hp-footer__meta {
		flex-direction: column;
		align-items: flex-start;
		gap: 8px;
	}

	.hp-footer__copyright {
		margin-left: 0;
		text-align: left;
	}

	/* 16px prevents the iOS focus zoom. */
	.hp-footer__email {
		font-size: 16px;
	}
}

/*
 * Scroll motion (client feedback: nothing should feel static).
 *
 * Reveals: hidden states double-gated behind html.js + reduced-motion, same
 * contract as the block reveals; assets/js/animations.js adds .is-inview and
 * staggers the gallery tiles for free (sibling stagger). The copyright line is
 * deliberately NOT a target: with the observer's bottom rootMargin an element
 * hugging the page's absolute bottom could never intersect and would stay
 * invisible at max scroll.
 */
@media (prefers-reduced-motion: no-preference) {
	html.js .hp-footer__brand,
	html.js .hp-footer__contact,
	html.js .hp-footer__gallery-grid > * {
		opacity: 0;
		transform: translateY(24px);
		transition:
			opacity var(--hp-reveal-duration) var(--hp-reveal-ease),
			transform var(--hp-reveal-duration) var(--hp-reveal-ease);
	}

	html.js .hp-footer__gallery-grid > * {
		transform: translateY(24px) scale(0.94);
	}

	html.js .hp-footer__brand.is-inview,
	html.js .hp-footer__contact.is-inview,
	html.js .hp-footer__gallery-grid > .is-inview {
		opacity: 1;
		transform: none;
	}

	/*
	 * Background parallax: the photo scrubs with scroll position (CSS view
	 * timeline — the process-timeline precedent; browsers without it simply
	 * keep the static photo). Scroll-bound, so a reader who stops scrolling
	 * sees a still page (WCAG 2.2 SC 2.2.2 stance). The 18% overscale leaves
	 * 9% of the photo's own height hanging past each edge, so the ±7.5%
	 * travel can never expose a gap; inset:0 + object-fit keep layout out of
	 * it, and .hp-footer's overflow:hidden keeps the overscale from widening
	 * the page.
	 */
	@supports (animation-timeline: view()) {
		.hp-footer {
			view-timeline: --hp-footer block;
			overflow: hidden; /* the 1.08 overscale would otherwise widen the page */
		}

		.hp-footer__bg {
			animation: hp-footer-drift linear both;
			animation-timeline: --hp-footer;
			animation-range: cover 0% cover 100%;
		}
	}
}

@keyframes hp-footer-drift {
	from {
		transform: scale(1.18) translateY(-7.5%);
	}

	to {
		transform: scale(1.18) translateY(7.5%);
	}
}
