/*
Theme Name: Photogenic
Theme URI: https://photogenicproperties.ca/
Author: Photogenic Properties
Description: Custom theme for Photogenic Properties. Glass, depth and a hand-written WebGL image layer. No page builder, no framework.
Version: 0.6.6
Requires at least: 6.5
Requires PHP: 8.0
License: Proprietary
Text Domain: photogenic
*/

/* =========================================================================
   Four rules, written down because they are decisions rather than taste and
   every later change has to answer to them:

   1. Depth is made with light, not with shadow. A dark interface lit from a
      moving source reads as material; the same interface with drop shadows
      reads as cardboard.
   2. Glass only where something sits on top of something. A frosted panel
      floating on flat colour is a sticker. Every `.glass` here has an image,
      a glow or another panel behind it.
   3. The compositor does the moving — transform, opacity, filter. Nothing
      animates a property that forces layout.
   4. It has to survive losing all of it. WebGL off, backdrop-filter absent,
      motion reduced, script failed: the page is still complete, legible and
      accessible. Every effect is additive.
   ========================================================================= */

@font-face {
	font-family: 'Space Grotesk';
	font-style: normal;
	font-weight: 300 700;
	font-display: swap;
	src: url('assets/fonts/space-grotesk-latin.woff2') format('woff2');
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
		U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
		U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
	/* Near-black with a blue cast rather than neutral. Neutral dark looks
	   switched off; a cast looks lit. */
	--void: #07070b;
	--void-2: #0c0c13;
	--pane: rgba(255, 255, 255, 0.045);
	--edge: rgba(255, 255, 255, 0.11);
	--edge-lit: rgba(255, 255, 255, 0.3);

	--text: #f2f1ee;
	--text-dim: #a8a8b2;
	/* 5.02:1 on --void. The first value here was #74747f, which measures 4.35:1 and
	   failed six elements. Any change to this token goes through
	   scripts/check_contrast.py first. */
	--text-faint: #7e7e8a;

	--bronze: #d59a4f;
	--bronze-deep: #9c6b2f;
	--violet: #6d5ed6;

	--step--1: clamp(0.8rem, 0.77rem + 0.14vw, 0.88rem);
	--step-0: clamp(1rem, 0.96rem + 0.2vw, 1.1rem);
	--step-1: clamp(1.18rem, 1.08rem + 0.48vw, 1.46rem);
	--step-2: clamp(1.45rem, 1.25rem + 0.95vw, 2rem);
	--step-3: clamp(1.9rem, 1.5rem + 1.9vw, 3.1rem);
	--step-4: clamp(2.4rem, 1.7rem + 3.4vw, 4.4rem);
	--step-5: clamp(2.9rem, 1.9rem + 4.6vw, 6.4rem);

	--gutter: clamp(1.25rem, 0.9rem + 1.8vw, 2.5rem);

	/* One spacing scale, used everywhere. The first build had twelve independent
	   clamp() expressions and the result read as a set of unrelated blocks floating
	   in the dark rather than as one page — which is exactly what "too much empty
	   space" describes. Four steps, and every gap on the page is one of them. */
	--sp-1: clamp(0.5rem, 0.4rem + 0.4vw, 0.75rem);
	--sp-2: clamp(0.75rem, 0.6rem + 0.7vw, 1.15rem);
	--sp-3: clamp(1.25rem, 0.9rem + 1.4vw, 2rem);
	--sp-4: clamp(2rem, 1.4rem + 2.4vw, 3.25rem);
	--section: clamp(3.25rem, 2.2rem + 4.4vw, 5.5rem);

	--round: 16px;

	--ease: cubic-bezier(0.22, 1, 0.36, 1);
	--fast: 260ms;

	/* Written by JS. The fallback keeps the light placed if script never runs. */
	--mx: 50%;
	--my: 26%;

	color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
	background: var(--void);
	color: var(--text);
	font-family: 'Space Grotesk', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
	font-size: var(--step-0);
	line-height: 1.6;
	letter-spacing: -0.006em;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
	position: relative;
}

/* The light. One warm wash following the pointer, one fixed cool counter-light.
   Moving it costs two custom-property updates, not a repaint of content. */
body::before {
	content: '';
	position: fixed;
	inset: 0;
	z-index: -3;
	pointer-events: none;
	background:
		radial-gradient(58vmax 58vmax at var(--mx) var(--my),
			rgba(213, 154, 79, 0.15), transparent 62%),
		radial-gradient(52vmax 52vmax at 88% 112%,
			rgba(109, 94, 214, 0.13), transparent 60%),
		linear-gradient(180deg, var(--void-2), var(--void) 55%);
}

img, picture, video, canvas, svg { display: block; max-width: 100%; }
img { height: auto; }

h1, h2, h3 { font-weight: 500; line-height: 1.05; letter-spacing: -0.035em; text-wrap: balance; }
p { text-wrap: pretty; }
a { color: inherit; text-decoration: none; }

:where(a, button, [tabindex]):focus-visible {
	outline: 2px solid var(--bronze);
	outline-offset: 4px;
	border-radius: 6px;
}

.skip-link {
	position: absolute; left: -9999px; top: 0; z-index: 200;
	background: var(--text); color: var(--void); padding: 0.8rem 1.4rem;
}
.skip-link:focus { left: 0; }

.sr-only {
	position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* ------------------------------------------------------------------- canvas */

.gl-canvas { position: fixed; inset: 0; z-index: -1; pointer-events: none; }

/* The real image keeps its place in the document for layout, alt text and
   crawlers; the canvas paints over it. Opacity, not display — it still has to
   occupy exactly the rectangle the canvas is drawing into. */
.gl-hidden { opacity: 0; }

/* ------------------------------------------------------------------- layout */

.shell { width: min(100% - (var(--gutter) * 2), 1480px); margin-inline: auto; }
.section { padding-block: var(--section); position: relative; }
.section + .section { padding-top: var(--section); }

/* The hero already ends with its own space; a full section padding on top of that is
   the single biggest void on the page. */
.hero + .section { padding-top: var(--sp-4); }

.eyebrow {
	font-size: var(--step--1);
	letter-spacing: 0.26em;
	text-transform: uppercase;
	color: var(--bronze);
	font-weight: 500;
	display: flex;
	align-items: center;
	gap: 0.9rem;
}

.eyebrow::before {
	content: '';
	width: 2.2rem;
	height: 1px;
	background: linear-gradient(90deg, var(--bronze), transparent);
}

.lede { font-size: var(--step-1); line-height: 1.5; color: var(--text-dim); max-width: 56ch; }

.head {
	display: grid;
	gap: var(--sp-2) var(--sp-4);
	margin-bottom: var(--sp-4);
	align-items: end;
}

.head h2 { font-size: var(--step-4); max-width: 16ch; }
.head .eyebrow { grid-column: 1 / -1; }

@media (min-width: 1000px) {
	.head { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); }
	/* The supporting line sits beside the heading rather than under it, and keeps a
	   little optical offset so the two do not read as one block of text. */
	.head .lede { padding-bottom: 0.35em; }
}

/* -------------------------------------------------------------------- glass

   One recipe used everywhere: a translucent pane, a hairline edge, a bright
   inner top edge where light would catch, and a bloom underneath. */

.glass {
	position: relative;
	background: var(--pane);
	border: 1px solid var(--edge);
	border-radius: var(--round);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.14),
		inset 0 -1px 0 rgba(0, 0, 0, 0.3),
		0 24px 70px -34px rgba(0, 0, 0, 0.95);
	overflow: hidden;
	isolation: isolate;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
	.glass {
		-webkit-backdrop-filter: blur(22px) saturate(150%);
		backdrop-filter: blur(22px) saturate(150%);
		background: rgba(255, 255, 255, 0.035);
	}
}

/* The sheen: a highlight that exists only while the pointer is over the pane,
   positioned from properties JS writes per element. */
.glass::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 3;
	pointer-events: none;
	background: radial-gradient(20rem 20rem at var(--px, 50%) var(--py, 0%),
		rgba(255, 255, 255, 0.11), transparent 68%);
	opacity: 0;
	transition: opacity var(--fast) var(--ease);
}

.glass:hover::after, .glass:focus-within::after { opacity: 1; }

/* ------------------------------------------------------------------ header */

.site-header {
	position: fixed;
	inset: 0 0 auto 0;
	z-index: 90;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: clamp(0.8rem, 0.5rem + 0.8vw, 1.2rem) var(--gutter);
}

.site-header::before {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgba(7, 7, 11, 0.85), transparent);
	opacity: 0;
	transition: opacity var(--fast) var(--ease);
	pointer-events: none;
}

.site-header[data-scrolled='true']::before { opacity: 1; }

/* The brand lockup. A fixed height rather than a width, so the monogram and the wordmark keep
   their proportions and the header's row height never moves. */
.brand { display: block; z-index: 2; line-height: 0; }

.brand img {
	display: block;
	height: clamp(1.7rem, 1.45rem + 1.1vw, 2.4rem);
	width: auto;
}

/* The navigation lives in assets/css/nav.css: the bar, the three mega panels and the
   mobile sheet are one component and were drifting apart while split across two files. */

.btn {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	padding: 0.9rem 1.7rem;
	border-radius: 999px;
	font-size: var(--step--1);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	font-weight: 500;
	position: relative;
	isolation: isolate;
	border: 1px solid var(--edge-lit);
	background: rgba(255, 255, 255, 0.04);
	transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
	z-index: 2;
}

@supports (backdrop-filter: blur(1px)) {
	.btn { -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); }
}

.btn::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	background: linear-gradient(135deg, var(--bronze), var(--bronze-deep));
	opacity: 0;
	transition: opacity var(--fast) var(--ease);
}

.btn:hover { color: #140d04; border-color: transparent; }
.btn:hover::before { opacity: 1; }

.btn--solid { color: #140d04; border-color: transparent; }
.btn--solid::before { opacity: 1; }
.btn--solid:hover::before { filter: brightness(1.14); }

/* -------------------------------------------------------------------- hero */

.hero {
	position: relative;
	min-height: 100svh;
	display: grid;
	align-items: center;
	/* The hero centres its own content inside 100svh, so a full section padding at the
	   bottom stacks with the next section's top padding and leaves about 320px of nothing
	   between them. */
	padding: calc(var(--section) * 1.6) var(--gutter) var(--sp-4);
}

.hero__grid {
	width: min(100%, 1480px);
	margin-inline: auto;
	display: grid;
	gap: var(--sp-4);
	align-items: center;
}

@media (min-width: 1000px) {
	.hero__grid { grid-template-columns: 1.02fr 0.98fr; }
}

.hero__copy { display: grid; gap: var(--sp-3); }
.hero h1 { font-size: var(--step-5); max-width: 13ch; letter-spacing: -0.05em; }
.hero h1 em { font-style: normal; color: var(--bronze); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }

/* The stack: three photographs layered in perspective. This is what says
   "depth" before a single pixel has moved. */
.stack {
	position: relative;
	perspective: 1500px;
	aspect-ratio: 4 / 3.6;
	width: 100%;
	max-width: 42rem;
	margin-inline: auto;
	/* Room for the rotation. A plate inset to the edge of this box still renders
	   outside it once rotateY tips it toward the viewer, which is how the right-hand
	   plate ended up running off the screen. */
	padding: 4% 7%;
}

.stack__item {
	position: absolute;
	border-radius: var(--round);
	overflow: hidden;
	box-shadow: 0 44px 110px -44px rgba(0, 0, 0, 0.96);
	border: 1px solid var(--edge);
	transition: transform 700ms var(--ease);
}

.stack__item img { width: 100%; height: 100%; object-fit: cover; }

.stack__item:nth-child(1) { inset: 4% 22% 30% 4%; z-index: 3; transform: rotateY(9deg) rotateX(2deg); }
.stack__item:nth-child(2) { inset: 20% 4% 12% 40%; z-index: 2; transform: rotateY(-7deg); }
.stack__item:nth-child(3) { inset: 54% 48% 4% 10%; z-index: 4; transform: rotateY(5deg) translateZ(60px); }

/* On a phone the plates stop being a 3D stack and become a grid.

   The perspective version is the right idea at 42rem and the wrong one at 375px: rotateY
   renders a plate wider than its box, so at phone width the right-hand plate's near edge
   reaches the screen edge and the whole arrangement reads as clipped rather than as depth.
   Three overlapping rectangles that small also stop looking like three photographs.

   So: a tall frame beside two stacked ones, flat, no rotation, no perspective. The same
   three photographs, bigger, and none of them cut off. */
@media (max-width: 640px) {
	.stack {
		perspective: none;
		aspect-ratio: 1 / 1.02;
		padding: 0;
		display: grid;
		grid-template-columns: 1.32fr 1fr;
		grid-template-rows: 1fr 1fr;
		gap: 0.5rem;
	}

	/* The nth-child selectors are repeated rather than reset on `.stack__item` alone,
	   because the desktop rules ARE nth-child selectors and out-specify a bare class. The
	   first version of this block set `inset: auto` on the class and lost: the plates kept
	   their desktop percentage insets, which on a `position: relative` grid item became
	   offsets from the cell, and the right-hand plate landed 32px past the screen edge. */
	.stack__item,
	.stack__item:nth-child(1),
	.stack__item:nth-child(2),
	.stack__item:nth-child(3) {
		position: relative;
		inset: auto;
		transform: none;
		z-index: auto;
		box-shadow: 0 18px 40px -22px rgba(0, 0, 0, 0.9);
	}

	.stack__item:nth-child(1) { grid-row: span 2; }
}

.hero__facts {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--sp-3) var(--sp-4);
	padding-top: var(--sp-3);
	border-top: 1px solid var(--edge);
	font-size: var(--step--1);
	color: var(--text-faint);
	letter-spacing: 0.05em;
}

@media (min-width: 1300px) {
	.hero__facts { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.hero__facts strong {
	display: block;
	color: var(--text);
	font-size: var(--step-1);
	font-weight: 500;
	letter-spacing: -0.02em;
}

/* ---------------------------------------------------------------- services */

.services {
	display: grid;
	gap: var(--sp-2);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
	perspective: 1800px;
}

@media (min-width: 1080px) {
	/* Three deliberate rows rather than whatever six items happen to flow into.
	   The spans used to be 3,2,2,2,2,3 over six columns, which fills row one to five
	   of six, pushes the third tile down, and strands the last one alone beside half
	   a row of nothing. Two halves, three thirds, one full band. */
	.services { grid-template-columns: repeat(6, 1fr); }
	.tile { grid-column: span 2; }
	.tile:nth-child(1), .tile:nth-child(2) { grid-column: span 3; }
	.tile:nth-child(6) { grid-column: span 6; min-height: clamp(220px, 16vw, 300px); }
	/* A full-width band reads as a band, so its text should not sit in the far
	   corner of it. */
	.tile:nth-child(6) p { max-width: 54ch; }
}

.tile {
	min-height: clamp(280px, 21vw, 380px);
	display: grid;
	align-content: end;
	gap: 0.5rem;
	padding: var(--sp-3);
	transition: transform 520ms var(--ease);
	transform-style: preserve-3d;
}

.tile__media { position: absolute; inset: 0; z-index: 0; }
.tile__media img { width: 100%; height: 100%; object-fit: cover; opacity: 0.58; }

.tile::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(to top, rgba(7, 7, 11, 0.95), rgba(7, 7, 11, 0.22) 62%);
}

/* Lift the card's content above its background image — but NOT the overlay link.
   `.tile__link` is an empty <a> whose ::after covers the whole card, and ::after is positioned
   against the nearest positioned ancestor. Including the link here made the link itself that
   ancestor, and the link's own box is zero pixels tall, so the overlay collapsed to nothing
   and **no card on this site was clickable** — not a guide, not a service tile. It was
   reported on the blog and it was true everywhere `.tile` is used. */
.tile > *:not(.tile__media):not(.tile__link) { position: relative; z-index: 2; }

.tile h3 { font-size: var(--step-2); }
.tile p { color: var(--text-dim); font-size: var(--step--1); max-width: 40ch; }

.tile__price {
	font-size: var(--step--1);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--bronze);
	margin-top: 0.35rem;
}

.tile__link::after { content: ''; position: absolute; inset: 0; z-index: 4; }

/* ----------------------------------------------------------------- gallery */

.rail {
	display: flex;
	gap: var(--sp-2);
	overflow-x: auto;
	padding-inline: var(--gutter);
	scroll-snap-type: x proximity;
	scrollbar-width: none;
	overscroll-behavior-x: contain;
}

.rail::-webkit-scrollbar { display: none; }

.rail figure { flex: none; width: clamp(250px, 28vw, 420px); scroll-snap-align: center; display: grid; gap: var(--sp-1); }
.rail .frame { border-radius: var(--round); overflow: hidden; border: 1px solid var(--edge); }
.rail img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.rail figcaption { font-size: var(--step--1); color: var(--text-faint); letter-spacing: 0.08em; }

/* ------------------------------------------------------------------- steps */

.steps {
	display: grid;
	gap: var(--sp-2);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
}

.step { padding: var(--sp-3); display: grid; gap: var(--sp-1); align-content: start; }
.step__n { font-size: var(--step--1); letter-spacing: 0.2em; color: var(--bronze); font-weight: 600; margin-bottom: 0.4rem; }
.step h3 { font-size: var(--step-1); }
.step p { color: var(--text-dim); font-size: var(--step--1); }

/* ---------------------------------------------------------------- coverage */

.places { display: flex; flex-wrap: wrap; gap: var(--sp-1); max-width: 64ch; padding: 0; }

.places li {
	list-style: none;
	border: 1px solid var(--edge);
	border-radius: 999px;
	padding: 0.5rem 1.05rem;
	font-size: var(--step--1);
	color: var(--text-dim);
	background: rgba(255, 255, 255, 0.03);
	transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}

.places li:hover { color: var(--text); border-color: var(--edge-lit); }
.places li:first-child { border-color: var(--bronze); color: var(--text); }

/* --------------------------------------------------------------------- cta */

.cta { padding: var(--sp-4); display: grid; gap: var(--sp-3); justify-items: start; }
.cta h2 { font-size: var(--step-4); max-width: 16ch; }
.cta__row { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }

/* ------------------------------------------------------------------ footer */

.site-footer { border-top: 1px solid var(--edge); padding-block: var(--section) var(--sp-3); }

.footer__grid {
	display: grid;
	gap: var(--sp-4);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
}

/* The footer column labels. These were <h2> until 2026-10-24; they are now the visible label
   of a <nav> landmark, so the selector names both and the appearance is unchanged. */
.footer__grid h2,
.footer__heading {
	font-size: var(--step--1);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--bronze);
	margin-bottom: 1rem;
	font-weight: 500;
}

.footer__grid ul { list-style: none; padding: 0; display: grid; gap: 0.55rem; }
.footer__grid a { color: var(--text-dim); transition: color var(--fast) var(--ease); }
.footer__grid a:hover { color: var(--text); }

/* Twelve services in one column made the footer taller than a laptop viewport and pushed
   the copyright line off the end of the page. The brief was not to shorten the list, so the
   list is two columns wide instead — which is also the shape it has in the menu. */
.footer__col--wide { grid-column: span 2; }

@media (min-width: 560px) {
	.footer__col--wide > ul {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: var(--sp-3);
	}
}

.footer__bottom {
	margin-top: var(--sp-4);
	padding-top: var(--sp-3);
	border-top: 1px solid var(--edge);
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
	justify-content: space-between;
	font-size: var(--step--1);
	color: var(--text-faint);
}

/* ==================================================================== inner pages

   One header band and one prose block serve all thirty pages. The content was
   imported as ordinary semantic HTML, so everything below is typography rather
   than layout — which is the whole reason for rebuilding without a page builder. */

.page-head {
	position: relative;
	padding: calc(var(--section) * 2.1) var(--gutter) var(--sp-4);
	overflow: clip;
}

.page-head__inner { display: grid; gap: var(--sp-2); }
.page-head h1 { font-size: var(--step-4); max-width: 17ch; }
.page-head .lede { max-width: 54ch; }

/* With a photograph the band gets taller and the image is dimmed enough that the
   heading keeps its contrast whatever the frame happens to contain. */
.page-head--media { padding-bottom: calc(var(--section) * 1.1); }

.page-head__media { position: absolute; inset: 0; z-index: -1; }
.page-head__media img { width: 100%; height: 100%; object-fit: cover; opacity: 0.4; }

.page-head--media::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: -1;
	background:
		linear-gradient(to top, var(--void) 2%, rgba(7, 7, 11, 0.55) 48%, rgba(7, 7, 11, 0.8)),
		linear-gradient(to right, rgba(7, 7, 11, 0.85), transparent 70%);
}

.crumbs {
	display: flex;
	gap: 0.6rem;
	font-size: var(--step--1);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--text-faint);
}

.crumbs a { color: var(--bronze); }
.crumbs a:hover { color: var(--text); }

/* ------------------------------------------------------------------- prose */

.prose {
	padding: clamp(1.5rem, 1rem + 2.4vw, 3.25rem);
	max-width: 78ch;
	display: grid;
	gap: var(--sp-3);
}

/* min-width:0 is not cosmetic here. .prose is a grid, and a grid item defaults to
   min-width:auto, which means it refuses to shrink below the width of its content.
   Without this the scroll container around a 672px table is itself 672px wide, so
   it has nothing to scroll and overflows the panel exactly as before — the fix
   looks applied and changes nothing. */
.prose > * { margin: 0; min-width: 0; }

.prose h2 {
	font-size: var(--step-3);
	max-width: 22ch;
	margin-top: var(--sp-3);
}

.prose > h2:first-child { margin-top: 0; }

.prose h3 { font-size: var(--step-1); max-width: 30ch; margin-top: var(--sp-2); }

.prose p, .prose li { color: var(--text-dim); }

.prose a {
	color: var(--bronze);
	text-decoration: underline;
	text-underline-offset: 0.2em;
	text-decoration-thickness: 1px;
	text-decoration-color: rgba(213, 154, 79, 0.45);
	transition: text-decoration-color var(--fast) var(--ease);
}

.prose a:hover { text-decoration-color: var(--bronze); }

.prose ul, .prose ol { padding-left: 1.25rem; display: grid; gap: var(--sp-1); }
.prose li::marker { color: var(--bronze); }
.prose strong { color: var(--text); font-weight: 500; }

/* A table of prices is the most-read thing on this site. It gets real styling
   rather than browser defaults. */
.prose table { width: 100%; border-collapse: collapse; font-size: var(--step--1); }
.prose th, .prose td { padding: 0.7rem 0.8rem; text-align: left; border-bottom: 1px solid var(--edge); }
.prose th { color: var(--text); font-weight: 500; letter-spacing: 0.04em; }
.prose td { color: var(--text-dim); }
.prose tr:hover td { color: var(--text); }

/* The heading row needs to read as a heading row. It used to be told apart by a
   #f5f5f5 bar baked into the page content — light-theme presentation that
   survived the rebuild and rendered near-white text on a near-white background,
   a measured contrast ratio of 1.01:1. photogenic-core's tables module removes
   that inline style; this is what separates the row instead. */
.prose thead th {
	border-bottom: 1px solid var(--edge-lit);
	color: var(--text);
}

/* The headings wrap on purpose. The content's inline `white-space:nowrap` was
   what made the price table 1041px wide inside an 870px column, so even a
   1440px desktop could not see the last column without scrolling. Letting
   "0 – 2,000 sq ft" take two lines costs one line of height and makes the table
   fit exactly — measured 1041px -> 870px, from scrolling to not scrolling. */
.prose th, .prose td { white-space: normal; }

/* A price table is wider than a phone and there is no honest way around that:
   seven columns of "$249.99" cannot be squeezed into 375px and stay readable.
   So it scrolls sideways instead of being clipped.
   It was clipped before — .glass sets overflow:hidden, the generator's
   `overflow-x:auto` on the wrapper is stripped by wp_kses because that property
   is not on safecss_filter_attr's allow-list, and the `min-width:720px` it sets
   on the table is. Measured on live: 960px of table inside a 333px panel, about
   two thirds of the price list unreachable with no scrollbar. */
.table-scroll {
	overflow-x: auto;
	overscroll-behavior-x: contain;
	-webkit-overflow-scrolling: touch;
}
.table-scroll table { min-width: 42rem; }
.table-scroll:focus-visible { outline: 2px solid var(--bronze); outline-offset: 3px; }

/* Scrolled right, the service name is what you lose first and it is the one
   column that makes the row mean anything. The background has to be opaque or
   the prices slide visibly underneath it, and it is mixed from the same tokens
   as the panel rather than typed in as a hex value — a hardcoded colour in a
   themed stylesheet is the bug this whole block exists to fix. */
.table-scroll th:first-child,
.table-scroll td:first-child {
	position: sticky;
	left: 0;
	background: color-mix(in srgb, #ffffff 4.5%, var(--void));
}

.prose iframe, .prose img { max-width: 100%; border-radius: var(--round); }

/* ------------------------------------------------------------- pagination */

.navigation.pagination { margin-top: var(--sp-4); }
.nav-links { display: flex; flex-wrap: wrap; gap: var(--sp-1); align-items: center; }

.page-numbers {
	display: inline-grid;
	place-items: center;
	min-width: 2.75rem;
	padding: 0.6rem 0.9rem;
	border: 1px solid var(--edge);
	border-radius: 999px;
	font-size: var(--step--1);
	color: var(--text-dim);
	transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}

.page-numbers:hover { color: var(--text); border-color: var(--edge-lit); }
.page-numbers.current { color: var(--void); background: var(--bronze); border-color: var(--bronze); }
.screen-reader-text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* ----------------------------------------------------------- inner layout

   The prose column plus the sticky aside. Without the aside the content sat at
   78ch inside a 1480px shell and left about forty per cent of every inner page
   as empty black — the same defect the home page had, in a different place. */

.layout { display: grid; gap: var(--sp-3); align-items: start; }

@media (min-width: 1040px) {
	.layout { grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); gap: var(--sp-4); }
}

.prose { max-width: none; }

.aside__card { padding: var(--sp-3); display: grid; gap: var(--sp-3); }

@media (min-width: 1040px) {
	/* Clears the fixed header, which is about 70px tall. */
	.aside { position: sticky; top: 5.5rem; }
}

.aside__facts { display: grid; gap: var(--sp-2); margin: 0; }
.aside__facts > div { display: grid; gap: 0.2rem; }

.aside__facts dt {
	font-size: var(--step--1);
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--text-faint);
}

.aside__facts dd { margin: 0; font-size: var(--step--1); color: var(--text); line-height: 1.45; }

.aside__actions { display: grid; gap: var(--sp-2); justify-items: start; }
.aside__tel { color: var(--bronze); letter-spacing: 0.04em; font-size: var(--step--1); }

/* The footer carries the whole link map, so it needs more room for its first column
   and tighter rows for the four link lists beside it. */
.footer__brand { display: grid; gap: var(--sp-2); align-content: start; }
.footer__brand p { color: var(--text-dim); font-size: var(--step--1); max-width: 34ch; }
.footer__contact a { color: var(--text-dim); }
.footer__contact a:hover { color: var(--bronze); }
.footer__order { justify-self: start; margin-top: var(--sp-1); }

/* The social profiles, added 2026-10-24. Laid out as a row that wraps rather than a column,
   so four short labels do not lengthen a footer this theme has already had to keep short. */
.footer__social {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem 1rem;
	list-style: none;
	margin: 0;
	padding: 0;
}
.footer__social a { color: var(--text-dim); }
.footer__social a:hover,
.footer__social a:focus-visible { color: var(--bronze); }
.footer__grid ul { gap: 0.4rem; }
.footer__grid a { font-size: var(--step--1); }

@media (max-width: 559px) {
	/* Below the two-column list the wide block is an ordinary column again, otherwise it
	   claims two of the two tracks that exist and the other three lists drop beneath it. */
	.footer__col--wide { grid-column: auto; }
}

/* Five tracks: the brand, two for the services list, and one each for areas, guides and
   company. The services block spans two of them. */
@media (min-width: 1100px) {
	.footer__grid { grid-template-columns: 1.35fr repeat(5, minmax(0, 1fr)); }
}

/* ---------------------------------------------------------------------- faq

   <details> rather than a scripted accordion: it opens without JavaScript, it is
   keyboard operable for free, and the browser handles the semantics. */

.faq { display: grid; gap: var(--sp-2); max-width: 70rem; }

.faq__item { padding: 0; }

.faq__item summary {
	cursor: pointer;
	list-style: none;
	padding: var(--sp-3);
	font-size: var(--step-1);
	letter-spacing: -0.02em;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--sp-2);
}

.faq__item summary::-webkit-details-marker { display: none; }

.faq__item summary::after {
	content: '';
	flex: none;
	width: 0.7rem;
	height: 0.7rem;
	border-right: 1px solid var(--bronze);
	border-bottom: 1px solid var(--bronze);
	transform: rotate(45deg);
	transition: transform var(--fast) var(--ease);
}

.faq__item[open] summary::after { transform: rotate(-135deg); }

.faq__body {
	padding: 0 var(--sp-3) var(--sp-3);
	color: var(--text-dim);
	max-width: 70ch;
}

.faq__body p + p { margin-top: var(--sp-2); }

/* A guide card's heading. Was an inline style on home.php, which meant the one place a
   reader's eye lands first could not be adjusted without editing a template. */
.tile__title { font-size: var(--step-2); letter-spacing: -0.02em; }
