/*
 * Estatein — main stylesheet
 * ---------------------------------------------------------------------------
 * Order matters and is deliberate:
 *   1. Reset          2. Fonts        3. Tokens (:root)
 *   4. Base elements  5. Primitives   6. Components (added per phase)
 *
 * Mobile-first throughout: base rules describe the 390px case and min-width
 * queries scale up. Breakpoints: 480 / 768 / 1024 / 1280.
 */

/* ==========================================================================
   1. Reset
   ========================================================================== */

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

html {
	-webkit-text-size-adjust: 100%;
	text-size-adjust: 100%;
}

body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd {
	margin: 0;
}

ul[role="list"],
ol[role="list"] {
	list-style: none;
	margin: 0;
	padding: 0;
}

/*
 * Media defaults to block so the inline-element baseline gap never appears under an
 * image inside a card, which is otherwise a recurring 4px mystery.
 */
img,
picture,
video,
canvas,
svg {
	display: block;
	max-width: 100%;
	height: auto;
}

/* Form controls do not inherit typography by default -- make them. */
input,
button,
textarea,
select {
	font: inherit;
	color: inherit;
}

button {
	background: none;
	border: 0;
	cursor: pointer;
}

/* Browsers give <fieldset> a default border/padding/min-width that nothing else here
   accounts for -- reset it once, globally, rather than overriding it per component. */
fieldset {
	border: 0;
	margin: 0;
	padding: 0;
	min-inline-size: 0;
}

legend {
	padding: 0;
}

/* Long unbroken strings (URLs, property references) must not blow out a card. */
p, h1, h2, h3, h4, h5, h6, li, dd {
	overflow-wrap: break-word;
}

/*
 * Anchor targets and in-page skips land clear of the sticky header instead of
 * underneath it.
 */
:target {
	scroll-margin-block-start: var(--header-offset, 6rem);
}

/* ==========================================================================
   2. Fonts — self-hosted, no request to fonts.gstatic.com
   ========================================================================== */

/*
 * Urbanist ships from Google as a single variable woff2 spanning the full 100–900
 * weight axis, so one 27KB file replaces the four static weights the design uses
 * (400/500/600/700). font-display: swap renders body copy immediately in the fallback
 * rather than holding a blank first paint.
 */
@font-face {
	font-family: "Urbanist";
	src: url("../fonts/urbanist-variable-latin.woff2") format("woff2-variations"),
	     url("../fonts/urbanist-variable-latin.woff2") format("woff2");
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
	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;
}

/* ==========================================================================
   3. Design tokens
   --------------------------------------------------------------------------
   Colours are sampled pixel values from design-refs/, not estimates, and are
   consistent across the 1920 / 1440 / 390 exports. They intentionally differ from
   design-refs/tokens.md, whose palette (gold #C9A227 on #0B0B0F) matches none of the
   six comps; the deviation is documented in README.md. tokens.md remains the source
   of truth for the type scale and section rhythm.
   ========================================================================== */

:root {
	/* --- Surfaces --------------------------------------------------------- */
	--color-bg: #141414;            /* Page background — 72% of every comp. */
	--color-surface: #1A1A1A;       /* Header, footer, cards, inputs. */
	--color-surface-raised: #262626; /* Pills, hover states, nested boxes. */
	--color-border: #262626;
	/*
	 * The lighter ring drawn outside certain cards' borders (About's Our Achievements
	 * and Our Values, and the home/services feature strip). Reading across a card edge
	 * in the comps gives #141414 | 6px #191919 | 1px #262626 | #141414 -- the ring is
	 * what makes those borders read as "thick".
	 */
	--color-frame: #191919;

	/* --- Text ------------------------------------------------------------- */
	--color-text: #FFFFFF;
	--color-text-muted: #999999;    /* 6.4:1 on --color-bg — passes AA unaided. */
	--color-text-subtle: #808080;   /* 4.6:1 on --color-bg — AA for body size only. */

	/* --- Grey ramp (borders, dividers, disabled) -------------------------- */
	--color-grey-30: #4D4D4D;
	--color-grey-40: #666666;
	/*
	 * Placeholder text. The comps sample at #666666, which is 3.0:1 on the #1A1A1A
	 * input fill -- under WCAG AA's 4.5:1 for text. #8A8A8A is the nearest grey that
	 * clears it on both the surface and the page (5.0:1 / 5.3:1); one shade lighter
	 * than drawn, the one deliberate departure from the comp's palette. Logged in
	 * the README's contrast audit.
	 */
	--color-placeholder: #8A8A8A;
	--color-grey-50: #808080;
	--color-grey-60: #999999;

	/* --- Brand ------------------------------------------------------------ */
	--color-accent: #703BF7;
	--color-accent-hover: #8254F8;
	--color-accent-tint: #A685FA;   /* Icon strokes, focus ring. */
	--color-accent-pale: #DBCEFD;   /* Gradient washes. */
	--color-star: #FFE500;          /* Testimonial rating stars. */
	--color-on-accent: #FFFFFF;

	/* --- Typography ------------------------------------------------------- */
	--font-body: "Urbanist", ui-sans-serif, system-ui, -apple-system,
	             "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
	--font-heading: var(--font-body);

	--weight-regular: 400;
	--weight-medium: 500;
	--weight-semibold: 600;
	--weight-bold: 700;

	/*
	 * Fluid scale. Each clamp interpolates linearly between the 390px mobile comp and
	 * the 1280px container, so there are no size jumps at breakpoints — the type grows
	 * continuously with the viewport. Upper bounds are tokens.md's stated values.
	 */
	--step-h1: clamp(2.25rem, 1.48rem + 3.15vw, 4rem);      /* 36 → 64 */
	--step-h2: clamp(1.75rem, 1.20rem + 2.25vw, 3rem);      /* 28 → 48 */
	--step-h3: clamp(1.375rem, 1.10rem + 1.12vw, 2rem);     /* 22 → 32 */
	--step-h4: clamp(1.125rem, 0.96rem + 0.67vw, 1.5rem);   /* 18 → 24 */
	--step-body-lg: clamp(1rem, 0.94rem + 0.22vw, 1.125rem); /* 16 → 18 */
	--step-body: 1rem;                                       /* 16 */
	--step-body-sm: 0.875rem;                                /* 14 */
	/*
	 * Secondary copy that the comps set at 14px on the 390 export and 16px on the
	 * 1440 one (section leads, step/team card text) -- one ramp, used wherever that
	 * pairing is measured, instead of each component repeating the clamp.
	 */
	--step-body-fluid: clamp(0.875rem, 0.829rem + 0.19vw, 1rem);   /* 14 → 16 */

	--leading-tight: 1.1;    /* H1 */
	--leading-snug: 1.15;    /* H2 */
	--leading-heading: 1.2;  /* H3/H4 */
	--leading-body: 1.6;
	--leading-compact: 1.5;

	--tracking-h1: -0.02em;
	--tracking-h2: -0.01em;

	/* --- Space ------------------------------------------------------------ */
	--space-3xs: 0.25rem;
	--space-2xs: 0.5rem;
	--space-xs: 0.75rem;
	--space-s: 1rem;
	--space-m: 1.5rem;
	--space-l: 2rem;
	--space-xl: 2.5rem;
	--space-2xl: 3rem;
	--space-3xl: 4rem;

	/* 64 → 120, matching tokens.md's mobile/desktop section padding. */
	--space-section: clamp(4rem, 2.47rem + 6.29vw, 7.5rem);

	/* --- Layout ----------------------------------------------------------- */
	--container-max: 80rem;   /* 1280px — matches tokens.md and the 1440 comp exactly. */
	--gutter: 1rem;           /* Scales up at each breakpoint below. */
	--grid-gap: 1.25rem;      /* 20px at 390, 32px from 768 up. */

	/*
	 * Header chrome, measured from the comps. The 1440 export is the reference
	 * breakpoint (it is the one whose container matches --container-max exactly), so
	 * these step to their 1440 values and stay there; the 1920 comp's taller chrome is
	 * a fluid variant we deliberately do not chase. See README.
	 */
	--announcement-height: 3.5rem;  /* 56px */
	--header-height: 4.25rem;       /* 68px — matches the 390 comp. */
	--header-offset: calc(var(--header-height) + var(--announcement-height));

	/* --- Shape and depth -------------------------------------------------- */
	--radius-card: 1rem;      /* 16px */
	--radius-button: 0.5rem;  /* 8px */
	--radius-input: 0.5rem;
	--radius-pill: 62.4375rem;
	--shadow-card: 0 4px 24px rgba(0, 0, 0, 0.32);

	/* --- Motion ----------------------------------------------------------- */
	--transition-fast: 150ms ease;
	--transition-base: 250ms ease;

	/* --- Focus ------------------------------------------------------------ */
	--focus-ring-width: 3px;
	--focus-ring-offset: 3px;
	--focus-ring-color: var(--color-accent-tint);
}

@media (min-width: 30rem) {   /* 480 */
	:root {
		--gutter: 1.5rem;
	}
}

@media (min-width: 48rem) {   /* 768 */
	:root {
		--gutter: 2.5rem;
		--grid-gap: 2rem;
		--announcement-height: 3rem;   /* 48px — matches the 1440 comp. */
		--header-height: 4.875rem;     /* 78px — matches the 1440 comp. */
	}
}

@media (min-width: 64rem) {   /* 1024 */
	:root {
		--gutter: 5rem;
	}
}

/*
 * The announcement bar can be dismissed, and the sticky header's offset has to follow.
 * Set by the pre-paint script in inc/enqueue.php and by main.js on dismissal.
 */
.has-announcement-dismissed {
	--announcement-height: 0rem;
}

.has-announcement-dismissed .announcement {
	display: none;
}

/* ==========================================================================
   4. Base elements
   ========================================================================== */

html {
	/* Belt and braces with :target above — covers anchor-driven scrolling too. */
	scroll-behavior: smooth;
	scroll-padding-block-start: var(--header-offset);
}

body {
	background-color: var(--color-bg);
	color: var(--color-text);
	font-family: var(--font-body);
	font-size: var(--step-body);
	font-weight: var(--weight-regular);
	line-height: var(--leading-body);
	min-height: 100vh;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
	font-family: var(--font-heading);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-heading);
	color: var(--color-text);
	text-wrap: balance; /* Progressive enhancement: no orphaned last word in a heading. */
}

h1 {
	font-size: var(--step-h1);
	line-height: var(--leading-tight);
	letter-spacing: var(--tracking-h1);
}

h2 {
	font-size: var(--step-h2);
	line-height: var(--leading-snug);
	letter-spacing: var(--tracking-h2);
}

h3 { font-size: var(--step-h3); }
h4 { font-size: var(--step-h4); }
h5 { font-size: var(--step-body-lg); }
h6 { font-size: var(--step-body); }

p {
	text-wrap: pretty;
}

a {
	color: var(--color-text);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.25em;
	transition: color var(--transition-fast);
}

a:hover {
	color: var(--color-accent-tint);
}

strong, b {
	font-weight: var(--weight-semibold);
}

small {
	font-size: var(--step-body-sm);
}

hr {
	border: 0;
	border-block-start: 1px solid var(--color-border);
	margin-block: var(--space-l);
}

::selection {
	background-color: var(--color-accent);
	color: var(--color-on-accent);
}

/*
 * :focus-visible only, so a mouse click on a button does not leave a ring behind, while
 * every keyboard user gets one. The ring is --color-accent-tint (#A685FA) rather than the
 * brand purple because #703BF7 against #141414 is only 3.2:1 — too weak to be a reliable
 * focus indicator. #A685FA reaches 6.6:1 and passes WCAG 2.2 non-text contrast comfortably.
 */
:focus-visible {
	outline: var(--focus-ring-width) solid var(--focus-ring-color);
	outline-offset: var(--focus-ring-offset);
	border-radius: 2px;
}

/* Remove the default ring only where we have replaced it above. */
:focus:not(:focus-visible) {
	outline: none;
}

/* ==========================================================================
   5. Layout primitives
   ========================================================================== */

/* Horizontal rhythm. Every full-width band gets .container for its inner content. */
.container {
	inline-size: 100%;
	max-inline-size: calc(var(--container-max) + (var(--gutter) * 2));
	margin-inline: auto;
	padding-inline: var(--gutter);
}

/* Vertical rhythm. */
.section {
	padding-block: var(--space-section);
}

.section--tight {
	padding-block: calc(var(--space-section) * 0.6);
}

.section--flush-top {
	padding-block-start: 0;
}

/*
 * Card grid. auto-fit + minmax means the column count is driven by available width
 * rather than by a breakpoint, so a 1-up / 2-up / 3-up progression happens naturally
 * and a partially filled last row still fills the track.
 */
.grid {
	display: grid;
	gap: var(--grid-gap);
	grid-template-columns: 1fr;
}

@media (min-width: 48rem) {
	.grid--2,
	.grid--3,
	.grid--4 {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 64rem) {
	.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Vertical stack with a single controllable gap — replaces ad-hoc margins. */
.stack {
	display: flex;
	flex-direction: column;
	gap: var(--stack-gap, var(--space-s));
}

/* Horizontal group that wraps instead of overflowing. */
.cluster {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--cluster-gap, var(--space-s));
}

/* Pushes a trailing element to the far edge of a flex row. */
.push-end {
	margin-inline-start: auto;
}

/*
 * Visually hidden but still announced by screen readers and still focusable.
 * The clip-path/1px technique rather than display:none, which would remove it from the
 * accessibility tree entirely.
 */
.visually-hidden:not(:focus):not(:active) {
	position: absolute !important;
	inline-size: 1px;
	block-size: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/*
 * Skip link: off-screen until focused, then pinned to the top-left. Built in Phase 0
 * because it is the first thing a keyboard tester reaches on every page.
 */
.skip-link {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: var(--space-s);
	z-index: 999;
	transform: translateY(-150%);
	padding: var(--space-2xs) var(--space-s);
	background-color: var(--color-accent);
	color: var(--color-on-accent);
	border-radius: 0 0 var(--radius-button) var(--radius-button);
	font-weight: var(--weight-medium);
	text-decoration: none;
	transition: transform var(--transition-fast);
}

/*
 * Revealed on :focus, not :focus-visible. A skip link is only ever reachable by keyboard
 * -- a pointer user cannot click something parked off-screen -- so there is no stray-ring
 * downside to the broader selector, and :focus-visible would leave the link invisible
 * while focused whenever focus arrives programmatically (element.focus() never matches
 * :focus-visible in Chromium). An invisible focused element is a keyboard trap.
 */
.skip-link:focus {
	transform: translateY(0);
	color: var(--color-on-accent);
}

/* ==========================================================================
   6. Motion preferences
   --------------------------------------------------------------------------
   Global guard rather than per-component opt-outs, so a component added in a later
   phase is covered by default instead of by remembering.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ==========================================================================
   7. Components — buttons
   --------------------------------------------------------------------------
   One shape, three treatments. Every button in the theme comes from
   estatein_get_button(), so these are the only button rules in the codebase.
   ========================================================================== */

/*
 * 49px tall at both 390 and 1440: 14px text on a 21px line inside 14px/20px of
 * padding -- the hero pair, the header's Contact Us and every "View All" button on
 * the comps all measure the same. The comps draw strokes inside the box, so the 1px
 * border is part of that padding rather than added to it.
 */
.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2xs);
	padding: calc(0.875rem - 1px) calc(1.25rem - 1px);
	border: 1px solid transparent;
	border-radius: var(--radius-button);
	font-size: var(--step-body-sm);
	font-weight: var(--weight-medium);
	line-height: 1.5;
	text-align: center;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
	transition: background-color var(--transition-fast),
	            border-color var(--transition-fast),
	            color var(--transition-fast);
}

.btn--primary {
	background-color: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-on-accent);
}

.btn--primary:hover,
.btn--primary:focus-visible {
	background-color: var(--color-accent-hover);
	border-color: var(--color-accent-hover);
	color: var(--color-on-accent);
}

.btn--secondary {
	background-color: var(--color-surface);
	border-color: var(--color-border);
	color: var(--color-text);
}

.btn--secondary:hover,
.btn--secondary:focus-visible {
	background-color: var(--color-surface-raised);
	color: var(--color-text);
}

.btn--ghost {
	background-color: transparent;
	border-color: transparent;
	color: var(--color-text-muted);
}

.btn--ghost:hover,
.btn--ghost:focus-visible {
	background-color: var(--color-surface-raised);
	color: var(--color-text);
}

.btn--sm {
	padding: 0.5rem 1rem;
	font-size: var(--step-body-sm);
}

/* Square footprint for controls whose only content is an icon. */
.btn--icon-only {
	padding: 0;
	inline-size: 2.75rem;
	block-size: 2.75rem;
	flex: 0 0 auto;
	border-radius: var(--radius-pill);
}

.btn--sm.btn--icon-only {
	inline-size: 2rem;
	block-size: 2rem;
}

.btn--block {
	display: flex;
	inline-size: 100%;
}

.btn .icon {
	flex: 0 0 auto;
}

/* ==========================================================================
   8. Components — site header
   ========================================================================== */

/*
 * Zero-height marker above the header, watched by an IntersectionObserver so the
 * condensed state costs one callback per crossing rather than a scroll handler.
 */
.header-sentinel {
	block-size: 1px;
	margin-block-end: -1px;
}

.site-header {
	position: sticky;
	inset-block-start: 0;
	z-index: 100;
	background-color: var(--color-surface);
}

/*
 * Condensed state. Only the announcement bar collapses -- the bar itself stays put, so
 * the nav never moves under the pointer mid-click. grid-template-rows animates cleanly
 * from 1fr to 0fr, which plain height cannot do without a magic pixel value.
 */
.site-header .announcement {
	display: grid;
	grid-template-rows: 1fr;
	transition: grid-template-rows var(--transition-base);
}

.site-header.is-scrolled {
	box-shadow: var(--shadow-card);
}

.site-header.is-scrolled .announcement {
	grid-template-rows: 0fr;
}

.site-header.is-scrolled .announcement__inner {
	overflow: hidden;
}

.site-header__bar {
	border-block-start: 1px solid var(--color-border);
}

/*
 * Three-column grid rather than a flex row. `1fr auto 1fr` forces the side columns to
 * equal width, which puts the navigation on the container's true centre line -- the
 * comps centre it there, and a flex row would instead centre it in whatever space the
 * logo and actions happen to leave over.
 */
.site-header__inner {
	display: grid;
	grid-template-columns: auto 1fr;
	align-items: center;
	gap: var(--space-m);
	min-block-size: var(--header-height);
}

@media (min-width: 64rem) {
	.site-header__inner {
		grid-template-columns: 1fr auto 1fr;
	}
}

.site-header__actions {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: var(--space-2xs);
}

/*
 * The header's own call to action is desktop-only; the panel repeats it on mobile.
 *
 * It also sits one step DARKER than the header bar (#141414 on #1A1A1A) rather than
 * using the default secondary treatment, which is the same colour as the bar and left
 * it almost invisible. Sampled off the comp.
 */
.site-header__cta {
	display: none;
}

.site-header__cta.btn--secondary {
	background-color: var(--color-bg);
}

.site-header__cta.btn--secondary:hover,
.site-header__cta.btn--secondary:focus-visible {
	background-color: var(--color-surface-raised);
}

@media (min-width: 64rem) {
	.site-header__cta {
		display: inline-flex;
	}
}

/* --- Announcement bar ----------------------------------------------------- */

/*
 * The announcement bar carries the supplied wavy line-art texture. Confirmed it is
 * the bar only, not the nav bar below it: a column scan of the comps shows the
 * announcement band broken up by texture pixels while the header band is one flat
 * run of #1A1A1A.
 *
 * background-color stays underneath so the bar is still correct if the image fails
 * to load, and cover keeps the artwork filling the strip at any viewport width.
 */
.announcement {
	position: relative;
	background-color: var(--color-surface);
	isolation: isolate; /* Keep the blend below from reaching the page behind the bar. */
}

/*
 * Overlay rather than a plain background-image. The artwork's own near-black field
 * would otherwise paint over --color-surface and drag the whole bar darker than the
 * comp; `screen` lets only the bright line-work through, leaving the base colour
 * intact. Tuned against the comp, where the texture is very faint.
 */
.announcement::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background-image: url("../images/wavy.png");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	mix-blend-mode: screen;
	opacity: 0.55;
	pointer-events: none;
}

.announcement__inner {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-s);
	min-block-size: var(--announcement-height);
}

.announcement__text {
	margin: 0;
	font-size: var(--step-body-sm);
	color: var(--color-text);
	text-align: center;
}

.announcement__sparkle {
	margin-inline-end: var(--space-3xs);
}

.announcement__link {
	margin-inline-start: var(--space-2xs);
	text-decoration: underline;
	white-space: nowrap;
}

/*
 * Pinned to the trailing edge rather than sitting in the flex flow, so the message stays
 * optically centred in the bar regardless of the dismiss button's width.
 */
.announcement__dismiss {
	position: absolute;
	inset-inline-end: var(--gutter);
}

.announcement__inner {
	position: relative;
}

/* --- Site logo ------------------------------------------------------------ */

.site-logo {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2xs);
	flex: 0 0 auto;
	color: var(--color-text);
	text-decoration: none;
	/*
	 * As a grid item, .site-header__inner would otherwise blockify and stretch this
	 * anchor across its whole 1fr column (grid's default justify-self: stretch),
	 * inflating the link's hit area far past the visible mark and wordmark.
	 */
	justify-self: start;
}

/* The lockup is one SVG (mark + wordmark); only its width is set, height follows. */
.site-logo__lockup {
	inline-size: 6.25rem;   /* 100px at mobile */
	block-size: auto;
	color: var(--color-text);
}

.site-logo__image {
	max-block-size: 2.625rem;
	inline-size: auto;
}

@media (min-width: 48rem) {
	.site-logo__lockup {
		inline-size: 7.125rem; /* 114px — the lockup's native width, matching the comp. */
	}
}

.site-logo--sm .site-logo__lockup {
	inline-size: 5.5rem;
}

/* --- Primary navigation --------------------------------------------------- */

.primary-nav__list {
	display: flex;
	align-items: center;
	gap: var(--space-3xs);
	margin: 0;
	padding: 0;
	list-style: none;
}

.primary-nav__list a {
	display: block;
	padding: 0.625rem 0.75rem;
	border: 1px solid transparent; /* Constant border-width so the active state's real
	                                   border below never shifts the layout by a pixel. */
	border-radius: var(--radius-button);
	font-size: var(--step-body);
	color: var(--color-text);
	text-decoration: none;
	white-space: nowrap;
	transition: background-color var(--transition-fast), color var(--transition-fast);
}

.primary-nav__list a:hover {
	background-color: var(--color-bg);
	color: var(--color-text);
}

/*
 * The active pill. Core marks the item with .current-menu-item and adds
 * aria-current="page" itself, so no custom walker is needed for either.
 */
.primary-nav__list .current-menu-item > a,
.primary-nav__list .current-menu-ancestor > a,
.primary-nav__list [aria-current="page"] {
	background-color: var(--color-bg);
	border: 1px solid var(--color-border);
	font-weight: var(--weight-medium);
}

/* Desktop bar hidden below the breakpoint; the off-canvas panel takes over. */
.primary-nav--desktop {
	display: none;
}

@media (min-width: 64rem) {
	.primary-nav--desktop {
		display: block;
	}
}

.nav-setup-notice {
	margin: 0;
	font-size: var(--step-body-sm);
	color: var(--color-text-muted);
}

/* --- Mobile navigation ---------------------------------------------------- */

.nav-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.75rem;   /* 44px — the minimum comfortable touch target. */
	block-size: 2.75rem;
	border-radius: var(--radius-button);
	color: var(--color-text);
	background: none;
	border: 0;
}

.nav-toggle:hover {
	background-color: var(--color-surface-raised);
}

@media (min-width: 64rem) {
	.nav-toggle {
		display: none;
	}
}

/* The scrim. The panel slides in from the inline-end edge. */
.mobile-nav {
	position: fixed;
	inset: 0;
	z-index: 200;
	background-color: rgba(0, 0, 0, 0.6);
	display: flex;
	justify-content: flex-end;
}

.mobile-nav[hidden] {
	display: none;
}

.mobile-nav__panel {
	display: flex;
	flex-direction: column;
	gap: var(--space-l);
	inline-size: min(20rem, 85vw);
	block-size: 100%;
	padding: var(--space-m);
	background-color: var(--color-surface);
	border-inline-start: 1px solid var(--color-border);
	overflow-y: auto;
	animation: estatein-slide-in var(--transition-base);
}

@keyframes estatein-slide-in {
	from { transform: translateX(100%); }
	to   { transform: translateX(0); }
}

.mobile-nav__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
}

.mobile-nav .primary-nav__list {
	flex-direction: column;
	align-items: stretch;
	gap: var(--space-3xs);
}

.mobile-nav .primary-nav__list a {
	padding: var(--space-xs) var(--space-s);
}

.mobile-nav__actions {
	margin-block-start: auto;
}

/* Background scroll lock, applied by main.js while the panel is open. */
.has-nav-open {
	overflow: hidden;
}

/* ==========================================================================
   9. Components — call-to-action band
   ========================================================================== */

/*
 * The band's backdrop is the design's perspective-grid artwork (cubes rising from the
 * lower-left and right corners). No file was supplied for it, so it is lifted from the
 * 1920 export's own band with the copy and button painted out -- those sit on a flat
 * part of the field -- and stretched with cover, which at 1440 crops ~8px top and
 * bottom and keeps the corners where the 1440 export shows them. Replace with the
 * source asset if one turns up; the CSS does not change.
 */
.cta-band {
	border-block-start: 1px solid var(--color-border);
	background-color: var(--color-bg);
	background-image: url("../images/cta-bg.png");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

/*
 * Measured on the Properties comp: a 258px band (rule to rule) with 60px padding, a
 * one-line 40px title, 8px, a 16px lead wrapping inside ~960px, and the 49px button
 * centred on the band's height at the far right. The 390 export stacks the button
 * full-width under the copy.
 */
.cta-band__inner {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 1.875rem;             /* 30 between copy and button on the 390 export */
	padding-block: 3.125rem;   /* 50 → 60 */
}

/* 928px: the comp's lead breaks "…real estate advice, | Estatein is here…" -- its first
   line is 929px and "Estatein" would need ~993, which at the browser's ~1% tighter
   setting brackets the cap between 920 and 983. */
.cta-band__content {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	max-inline-size: 58rem;
}

.cta-band__title {
	margin: 0;
	font-size: clamp(1.75rem, 1.471rem + 1.143vw, 2.5rem);   /* 28 → 40 */
	line-height: 1.5;
}

.cta-band__text {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.cta-band__action .btn {
	inline-size: 100%;
}

@media (min-width: 64rem) {
	.cta-band__inner {
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: var(--space-3xl);
		padding-block: 3.75rem;
	}

	.cta-band__action {
		flex: 0 0 auto;
	}

	.cta-band__action .btn {
		inline-size: auto;
	}
}

/* ==========================================================================
   10. Components — site footer
   ========================================================================== */

.site-footer {
	border-block-start: 1px solid var(--color-border);
	background-color: var(--color-bg);
}

.site-footer__inner {
	display: grid;
	gap: var(--space-2xl);
	padding-block: var(--space-2xl);
	grid-template-columns: 1fr;
}

.site-footer__brand {
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	align-items: flex-start;
}

/*
 * 1440 comp: logo 80 below the rule, the five nav columns starting at x=486 -- brand
 * column 304 wide plus 102 of gap -- each column as wide as its longest link with the
 * leftover spread evenly (486 / 665 / 846 / 1015 / 1265 = space-between), 18px
 * headings and links on a 40px pitch, and a 304x52 newsletter field.
 */
@media (min-width: 64rem) {
	.site-footer__inner {
		grid-template-columns: 19rem minmax(0, 1fr);
		gap: 6.375rem;
		padding-block: 5rem;
	}
}

/* --- Newsletter ----------------------------------------------------------- */

.newsletter {
	inline-size: 100%;
	max-inline-size: 19rem;   /* 304px on the comp */
}

.newsletter__field {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	block-size: 3.25rem;   /* 52px */
	padding-inline: calc(1.25rem - 1px) calc(0.75rem - 1px);
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-input);
}

/* Move the ring to the wrapper so the whole control reads as one focused field. */
.newsletter__field:focus-within {
	outline: var(--focus-ring-width) solid var(--focus-ring-color);
	outline-offset: var(--focus-ring-offset);
}

.newsletter__icon {
	display: flex;
	color: var(--color-text-muted);
}

.newsletter__input {
	flex: 1 1 auto;
	min-inline-size: 0;
	padding-block: var(--space-xs);
	background: none;
	border: 0;
	color: var(--color-text);
	font-size: var(--step-body);
}

.newsletter__input::placeholder {
	color: var(--color-text-muted);
	opacity: 1;
}

.newsletter__input:focus-visible {
	outline: none;
}

.newsletter__submit {
	color: var(--color-text);
}

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

.footer-nav {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--space-l) var(--space-m);
}

.footer-nav__column {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
	padding-block-end: var(--space-s);
	border-block-end: 1px solid var(--color-border);
}

.footer-nav__heading {
	margin: 0;
	font-size: var(--step-body-lg);   /* 18 */
	font-weight: var(--weight-regular);
	line-height: var(--leading-compact);
	color: var(--color-text-muted);
}

.footer-nav__heading a {
	color: inherit;
	text-decoration: none;
}

.footer-nav__list {
	display: flex;
	flex-direction: column;
	gap: 0.8125rem;   /* 27px line + 13 = the comp's 40px pitch */
	margin: 0;
	padding: 0;
	list-style: none;
}

.footer-nav__link {
	font-size: var(--step-body-lg);   /* 18 */
	line-height: var(--leading-compact);
	color: var(--color-text);
	text-decoration: none;
}

.footer-nav__link:hover {
	color: var(--color-accent-tint);
	text-decoration: underline;
}

@media (min-width: 48rem) {
	.footer-nav {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

@media (min-width: 64rem) {
	.footer-nav {
		display: flex;
		justify-content: space-between;
		gap: var(--space-l);
	}

	/* The 1440 comp drops the separators once the columns sit side by side. */
	.footer-nav__column {
		flex: 0 0 auto;
		gap: 1.25rem;
		padding-block-end: 0;
		border-block-end: 0;
	}
}

/* --- Colophon ------------------------------------------------------------- */

.site-footer__colophon {
	background-color: var(--color-surface);
}

.site-footer__colophon-inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-m);
	padding-block: 0.875rem;   /* 14px: a 68px bar around 40px social discs on the comp */
	text-align: center;
}

.site-footer__copyright {
	margin: 0;
	font-size: var(--step-body);
	color: var(--color-text);
}

@media (min-width: 48rem) {
	.site-footer__colophon-inner {
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		text-align: start;
	}
}

/* --- Social --------------------------------------------------------------- */

.social {
	display: flex;
	align-items: center;
	gap: var(--space-2xs);
	margin: 0;
	padding: 0;
	list-style: none;
}

.social__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.5rem;
	block-size: 2.5rem;
	border-radius: var(--radius-pill);
	background-color: var(--color-bg);
	color: var(--color-text);
	transition: background-color var(--transition-fast), color var(--transition-fast);
}

.social__link:hover {
	background-color: var(--color-accent);
	color: var(--color-on-accent);
}

.site-footer__legal {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-xs) var(--space-m);
}

.site-footer__terms {
	font-size: var(--step-body);
	color: var(--color-text);
	text-decoration: none;
}

.site-footer__terms:hover {
	color: var(--color-text);
}

/* ==========================================================================
   11. Components — cards
   --------------------------------------------------------------------------
   .card is the shared shell (radius, border, surface, overflow) that every card
   partial (property/agent/testimonial/post) builds on, so the six box-model
   declarations exist once instead of four times.
   ========================================================================== */

/*
 * Cards sit on the page colour and are delineated by their border alone -- sampled
 * across the home, properties, property-details and about comps, every content card
 * interior is #141414 (identical to the page) with a 1px #262626 edge. An earlier
 * pass filled them with --color-surface, which made every card visibly lighter than
 * the design. The hero stat cards and the feature strip genuinely ARE --color-surface
 * and set that themselves, so they are unaffected.
 */
.card {
	display: flex;
	flex-direction: column;
	background-color: var(--color-bg);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
	overflow: hidden;
}

/* Small rounded label used for taxonomy tags and property specs. */
.pill {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3xs);
	padding: var(--space-3xs) var(--space-xs);
	border-radius: var(--radius-pill);
	background-color: var(--color-bg);
	font-size: var(--step-body-sm);
	color: var(--color-text);
	white-space: nowrap;
}

.pill .icon {
	color: var(--color-accent-tint);
	flex: 0 0 auto;
}

/* Inline "Read More" / "Learn More" affordance appended to trimmed body copy. */
.text-link {
	color: var(--color-text);
	font-weight: var(--weight-medium);
	text-decoration: underline;
	text-underline-offset: 0.2em;
	white-space: nowrap;
}

.text-link:hover {
	color: var(--color-accent-tint);
}

/* --- Property card ---------------------------------------------------- */

/*
 * One card, both variants, measured on the Home (spec) and Properties (tag) comps at
 * 1440 and 390. The two pages agree on every shared number:
 *
 *   card 413 wide, 1px border, 12px radius, padding 30 (20 at 390)
 *   image full content width, 250 tall (211 at 390), 10px radius
 *   24 → tag pill 33 tall → 16 → title 20/1.5 (18 at 390) → 4 → excerpt 16/1.5 (14)
 *   24 → spec pills 33 tall (Home) → 24 → footer: price block, 32 gap, button filling
 *   the rest of the row
 *
 * The sums close on both: 30+250+24+33+16+30+4+48+24+51+30 = 540 (Properties, 869–1408)
 * and 30+250+24+30+4+48+24+33+24+51+30 = 548 (Home, 1223–1771, 1px out).
 */
.property-card {
	--property-pad: calc(1.25rem - 1px);           /* 20 → 30, minus the inside stroke */

	align-self: start;   /* own height in a carousel; see .carousel__track */
	--property-image: clamp(13.1875rem, 12.32rem + 3.714vw, 15.625rem);   /* 211 → 250 */

	display: flex;
	flex-direction: column;
	padding: var(--property-pad);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-bg);
}

@media (min-width: 64rem) {
	.property-card {
		--property-pad: calc(1.875rem - 1px);
	}
}

.property-card__media {
	display: block;
	block-size: var(--property-image);
	margin-block-end: var(--space-m);
	border-radius: 0.625rem;   /* 10px */
	overflow: hidden;
	background-color: var(--color-surface);
}

.property-card__media img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

.property-card__media-placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	inline-size: 100%;
	block-size: 100%;
	color: var(--color-grey-30);
}

.property-card__body {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	flex: 1 1 auto;   /* Fill the card so the footer can be pushed to the bottom. */
}

/* Tag and spec pills share one shape: 33px tall, surface fill, 1px border, 14px text. */
.property-card__tag,
.property-card__spec {
	block-size: 2.0625rem;
	padding-inline: calc(0.75rem - 1px);
	border: 1px solid var(--color-border);
	background-color: var(--color-surface);
	font-size: var(--step-body-sm);
}

.property-card__tag {
	margin-block-end: var(--space-s);
}

/*
 * Fixed one-line height so every card's excerpt starts on the same baseline. Titles
 * are short enough to fit on one line at every breakpoint in the comps; clamping
 * guarantees a stray long title cannot knock one card's rows out of alignment with
 * its neighbours.
 */
.property-card__title {
	margin: 0 0 var(--space-3xs);
	font-size: clamp(1.125rem, 1.079rem + 0.19vw, 1.25rem);   /* 18 → 20 */
	line-height: 1.5;
	display: -webkit-box;
	-webkit-line-clamp: 1;
	line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.property-card__title a {
	color: var(--color-text);
	text-decoration: none;
}

.property-card__title a:hover {
	color: var(--color-accent-tint);
}

/*
 * Clamped to exactly two lines. Excerpt length varies per listing, and without this
 * a three-line excerpt pushed that card's spec pills, price and button below its
 * neighbours' -- the comp keeps all three cards' rows on the same baselines.
 */
.property-card__excerpt {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	min-block-size: calc(2em * var(--leading-compact));
}

/* One row of three pills, 6px apart, 24 above and below -- see the model at the top. */
.property-card__specs {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
	inline-size: 100%;
	margin-block-start: var(--space-m);
}

.property-card__spec {
	gap: 0.375rem;
	padding-inline: calc(0.875rem - 1px);
}

.property-card__spec .icon {
	inline-size: 1.25rem;
	block-size: 1.25rem;
	color: var(--color-text);
}

/*
 * Price block and a button that takes whatever width is left -- on the comp the
 * button measures 226, 237 and 238 across the three cards while the gap from the
 * price digits stays exactly 32, which only a stretching button produces.
 */
.property-card__footer {
	display: flex;
	align-items: center;
	gap: var(--space-l);
	inline-size: 100%;
	margin-block-start: auto;
	padding-block-start: var(--space-m);
}

.property-card__price {
	display: flex;
	flex-direction: column;
	flex: 0 0 auto;
}

.property-card__price-label {
	font-size: var(--step-body-sm);
	line-height: var(--leading-compact);
	color: var(--color-text-muted);
}

.property-card__price-value {
	font-size: 1.25rem;
	line-height: 1.5;
	font-weight: var(--weight-semibold);
}

.property-card__cta {
	flex: 1 1 auto;
}

/* --- Agent card --------------------------------------------------------- */

/*
 * Meet the Estatein Team. Every number below is read off the About comps -- 390 and
 * 1440 -- and, unusually for this design, the mobile card is the LARGER one: taller
 * portrait, taller button, bigger send disc. So the base styles are the 390 case and
 * the desktop values switch on at 1024, which is also where the grid goes 4-up and
 * the cards drop to the 305px width the 1440 values were drawn for. Text sizes stay
 * fluid like everywhere else.
 *
 * Column gap is 20px, not the site's 32 -- borders sit at x 80–384, 405–709, 730–1034,
 * 1055–1359 on the 1440 comp -- and the same 20 separates stacked cards at 390.
 */
.team-grid {
	gap: 1.25rem;
}

.agent-card {
	/*
	 * The comps' strokes are drawn INSIDE the box (Figma default), so the 20/24px
	 * padding they show is measured from the outer edge and already includes the
	 * 1px border. Subtracting it keeps the portrait and button at exactly the
	 * comp's width.
	 */
	--agent-pad: calc(1.25rem - 1px);     /* 20 → 24 */
	--agent-portrait: 16.75rem;           /* 268 → 220 */
	--agent-badge-hang: 1.125rem;         /* 18px of the 60×40 badge sits below the portrait, both comps */
	--agent-cta-size: 4rem;               /* 64 → 56 */
	--agent-cta-pad: 1.5rem;              /* 24 → 20 */
	--agent-disc: 3rem;                   /* 48 → 36 */
	--agent-disc-inset: 0.5rem;           /* 8 → 10 */

	display: flex;
	flex-direction: column;
	align-items: center;
	padding: var(--agent-pad);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;               /* 12px, both comps -- tighter than --radius-card */
	background-color: var(--color-bg);
	text-align: center;
}

@media (min-width: 64rem) {
	.agent-card {
		--agent-pad: calc(1.5rem - 1px);
		--agent-portrait: 13.75rem;
		--agent-cta-size: 3.5rem;
		--agent-cta-pad: 1.25rem;
		--agent-disc: 2.25rem;
		--agent-disc-inset: 0.625rem;
	}
}

/*
 * The portrait is a fixed-height, full-width crop -- the comps give 268px at 390 and
 * 220px at 1440 for slightly different aspect ratios, so it is a box the image covers,
 * not an intrinsic-ratio image. Bottom margin = the badge's overhang plus the gap from
 * badge to name, which the comps put at 20px (390) → 31px (1440).
 */
.agent-card__portrait {
	position: relative;
	inline-size: 100%;
	block-size: var(--agent-portrait);
	margin-block-end: calc(var(--agent-badge-hang) + clamp(1.25rem, 0.995rem + 1.048vw, 1.9375rem));
}

.agent-card__portrait img,
.agent-card__portrait-placeholder {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	border-radius: 0.625rem;   /* 10px */
	object-fit: cover;
}

.agent-card__portrait-placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: var(--color-surface-raised);
	color: var(--color-accent-tint);
	font-size: var(--step-h3);
	font-weight: var(--weight-semibold);
}

/* 60×40 accent pill straddling the portrait's bottom edge, centred. */
.agent-card__social {
	position: absolute;
	inset-block-end: calc(-1 * var(--agent-badge-hang));
	inset-inline-start: 50%;
	transform: translateX(-50%);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 3.75rem;
	block-size: 2.5rem;
	border-radius: var(--radius-pill);
	background-color: var(--color-accent);
	color: var(--color-on-accent);
}

.agent-card__social:hover {
	background-color: var(--color-accent-hover);
	color: var(--color-on-accent);
}

.agent-card__name {
	margin: 0;
	font-size: 1.25rem;        /* 20px at both comps */
	line-height: 1.5;
}

.agent-card__role {
	margin: var(--space-3xs) 0 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

/*
 * "Say Hello 👋": a full-width surface pill with the send icon on its own accent disc
 * at the trailing end. The disc is the icon's own padding + background rather than an
 * extra wrapper span, so estatein_button() needs no new markup to produce it. Sizes:
 * pill 64/56 tall, disc 48/36 inset 8/10, label 14→16 with 24/20 leading padding.
 */
.agent-card__contact {
	inline-size: 100%;
	justify-content: space-between;
	block-size: var(--agent-cta-size);
	margin-block-start: clamp(1.0625rem, 1.016rem + 0.19vw, 1.1875rem);   /* 17 → 19 */
	/* Same inside-stroke correction as the card padding: the pill's 1px border is part of the inset. */
	padding: 0 calc(var(--agent-disc-inset) - 1px) 0 calc(var(--agent-cta-pad) - 1px);
	border-radius: var(--radius-pill);
	font-size: var(--step-body-fluid);
}

.agent-card__contact .icon {
	box-sizing: border-box;
	inline-size: var(--agent-disc);
	block-size: var(--agent-disc);
	padding: calc((var(--agent-disc) - 1.375rem) / 2);
	border-radius: var(--radius-pill);
	background-color: var(--color-accent);
	color: var(--color-on-accent);
	transition: background-color var(--transition-fast);
}

.agent-card__contact:hover .icon {
	background-color: var(--color-accent-hover);
}

/* --- Testimonial card ---------------------------------------------------- */

/*
 * Measured on the 1440 Home comp: 413x362, 40px padding. Five 40px star badges 6
 * apart (#1A1A1A disc, 18px #FFE500 star), 24 to a 20px title, 12 to the 16px quote
 * -- white, not muted, on the comp -- then 30 to a 50px avatar beside an 18px name
 * over a 16px muted location. 40+40+24+30+12+96+30+50+40 = 362.
 */
.testimonial-card {
	padding: calc(2.5rem - 1px);
	gap: 0;
}

.testimonial-card__stars {
	display: flex;
	gap: 0.375rem;
}

.testimonial-card__star {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.5rem;
	block-size: 2.5rem;
	border-radius: var(--radius-pill);
	background-color: var(--color-surface);
	color: var(--color-grey-30);
}

.testimonial-card__star .icon {
	inline-size: 1.125rem;
	block-size: 1.125rem;
}

.testimonial-card__star.is-filled {
	color: var(--color-star);
}

.testimonial-card__title {
	margin: var(--space-m) 0 0;
	font-size: clamp(1.125rem, 1.079rem + 0.19vw, 1.25rem);   /* 18 → 20 */
	line-height: 1.5;
}

.testimonial-card__quote {
	margin: 0.75rem 0 0;
	color: var(--color-text);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.testimonial-card__author {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	margin-block-start: auto;
	padding-block-start: 1.875rem;
}

.testimonial-card__avatar {
	inline-size: 3.125rem;
	block-size: 3.125rem;
	border-radius: var(--radius-pill);
	object-fit: cover;
	flex: 0 0 auto;
}

.testimonial-card__avatar-placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: var(--color-surface-raised);
	color: var(--color-accent-tint);
	font-weight: var(--weight-semibold);
}

.testimonial-card__author-info {
	display: flex;
	flex-direction: column;
}

.testimonial-card__author-name {
	margin: 0;
	font-size: clamp(1rem, 0.954rem + 0.19vw, 1.125rem);   /* 16 → 18 */
	font-weight: var(--weight-semibold);
	line-height: var(--leading-compact);
}

.testimonial-card__author-location {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

/* ==========================================================================
   12. Components — section header
   ========================================================================== */

.section-header {
	display: flex;
	flex-direction: column;
	gap: var(--space-l);
	/* Lead-to-content gap measures 40px on the 390 comps and 60px on the 1440 ones. */
	margin-block-end: clamp(2.5rem, 2.036rem + 1.905vw, 3.75rem);
}

.section-header__marker {
	display: inline-flex;
	margin-block-end: var(--space-s);
}

.section-header__heading {
	margin: 0;
	max-inline-size: 30ch;
}

/*
 * The lead is 14px at 390 and 16px at 1440 (the 1920 export bumps it to 18, a fluid
 * variant not chased). Its measure is a pixel cap, not a character count: on the About
 * comp the three full-width leads wrap at "…platform that | transcended", "…guide | to
 * how" and "…real estate | dreams", which brackets the box between 1027 and 1038px
 * wide. Browsers set Urbanist about 1% tighter than the Figma export does (the same
 * "…platform that" measures 1018px here against 1027 there), so 1020px is the value
 * that reproduces every one of those breaks on screen. The gap below the heading is
 * 8px, not the stack's usual 16.
 */
.section-header__lead {
	margin: var(--space-2xs) 0 0;
	max-inline-size: 63.75rem;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

@media (min-width: 64rem) {
	/*
	 * When a header carries a button, the Home comps wrap its lead well short of the
	 * 1030px cap: Featured Properties (155px button) breaks at "…available | through",
	 * FAQ (127px button) at "…clarity and | assist", Testimonials (180px button) fits
	 * its lead on one 946px line. All three resolve only if roughly 150px separates
	 * the intro column from the button -- so that is the gap, oversized as it looks.
	 */
	.section-header {
		flex-direction: row;
		align-items: flex-end;
		justify-content: space-between;
		gap: 9.375rem;
	}

	.section-header__action {
		flex: 0 0 auto;
	}
}

/* ==========================================================================
   13. Components — carousel
   ========================================================================== */

.carousel__track {
	display: grid;
	grid-auto-flow: column;
	/*
	 * The gap has to be declared here. The track deliberately does NOT carry the
	 * generic .grid utility class (that would set grid-template-columns and break
	 * grid-auto-columns -- see estatein_carousel_open), and dropping that class also
	 * dropped the gap it used to supply, leaving every card butted against the next
	 * with no spacing and a stray sliver of the following card poking past the edge.
	 *
	 * 20px, not the 32px grid default: every 3-up carousel on the comps -- Featured
	 * Properties, Testimonials and FAQ on Home, the listings on Properties -- puts its
	 * cards at x 87-499 / 520-932 / 953-1366, i.e. 413 wide with 20 between. Set as the
	 * local --grid-gap so the column-width calcs below pick it up as well.
	 */
	--grid-gap: 1.25rem;
	gap: var(--grid-gap);
	grid-auto-columns: 100%;
	/*
	 * Cards stretch to the row by default -- the testimonial and FAQ comps show equal
	 * heights. Property cards opt out (see .property-card), since one listing whose
	 * spec pills wrap was otherwise making all sixty 38px taller than the comp.
	 */
	align-items: stretch;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x mandatory;
	scroll-padding-inline-start: 0;
	/* Scrollbar stays invisible but the track remains fully keyboard/touch operable. */
	scrollbar-width: none;
}

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

.carousel__track > * {
	scroll-snap-align: start;
	min-inline-size: 0;
}

@media (min-width: 48rem) {
	.carousel__track {
		grid-auto-columns: calc(50% - (var(--grid-gap) / 2));
	}
}

@media (min-width: 64rem) {
	.carousel__track {
		grid-auto-columns: calc(((100% - (var(--grid-gap) * 2)) / 3));
	}
}

/*
 * Two different arrangements, both taken from the comps. At 390 the arrows sit hard
 * against the container's edges with the counter centred between them and no rule
 * above; from 768 the rule appears and the row becomes counter-left / arrows-right.
 * Ordering the counter second and giving it `order` at the small size keeps the DOM
 * in reading order (position, then controls) at both.
 */
.carousel__controls {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-m);
	margin-block-start: var(--space-l);
}

.carousel__controls .carousel__counter {
	order: 1;
	margin-inline: auto;
}

.carousel__controls .carousel__buttons {
	display: contents;
}

.carousel__buttons .carousel__prev {
	order: 0;
}

.carousel__buttons .carousel__next {
	order: 2;
}

@media (min-width: 48rem) {
	.carousel__controls {
		/* Comp: card bottom 3991 → rule 4032 → arrow top 4048, i.e. 40 above, 16 below. */
		margin-block-start: var(--space-xl);
		padding-block-start: var(--space-s);
		border-block-start: 1px solid var(--color-border);
	}

	.carousel__controls .carousel__counter {
		order: 0;
		margin-inline: 0;
	}

	.carousel__controls .carousel__buttons {
		display: flex;
		order: 1;
	}
}

.carousel__counter {
	color: var(--color-text-muted);
	font-size: var(--step-body);
}

.carousel__buttons {
	display: flex;
	gap: var(--space-2xs);
}

.carousel__prev .icon {
	transform: rotate(180deg);
}

.carousel__prev:disabled,
.carousel__next:disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

/* ==========================================================================
   14. Components — page hero (shared: About / Services / Contact / Properties)
   ========================================================================== */

/*
 * The Properties, Services and Contact comps all open with the same band: the header's
 * surface colour carrying the wavy line-art (same overlay as the announcement bar),
 * closed by a 1px rule. Measured on the 1440 exports the h1 cap top sits 116px below
 * the header and the rule 100px below the lead; on the 390 export both are 50px.
 */
/*
 * The band is a gradient, not an image. Sampling the Properties, Services and Contact
 * exports row by row gives the same ramp on all three: #1F1F1F at the left edge
 * falling to the #141414 page colour by x≈640 (#1D at 100, #1B at 190, #1A at 280,
 * #18 at 370, #15 at 550), with a slight vertical fall as well (#1F at the top-left
 * corner, #1C by the bottom-left). One radial gradient anchored at the top-left
 * corner reproduces both axes. An earlier pass painted the wavy line-art here; the
 * comps keep that for the announcement bar only.
 */
.page-hero {
	position: relative;
	padding-block: 3.125rem;                 /* 50 → 100 */
	background-color: var(--color-bg);
	background-image: radial-gradient(ellipse 40rem 70rem at 0 0, #1F1F1F 0%, #141414 100%);
	border-block-end: 1px solid var(--color-border);
}

/*
 * Left-aligned at the container's gutter, NOT centred in a narrow column. An earlier
 * pass capped .page-hero__inner at 48rem, but since that element IS the .container,
 * the cap shrank the container itself and `margin-inline: auto` then centred the whole
 * hero -- every comp (Properties, About, Services, Contact) left-aligns it flush with
 * the logo instead. The lead paragraph keeps its own measure cap so long copy still
 * wraps at a readable line length.
 */
.page-hero__heading {
	margin: 0;
	font-size: clamp(1.75rem, 1.471rem + 1.143vw, 2.5rem);   /* 28 → 40, smaller than the H1 scale */
	line-height: 1.5;
}

/*
 * 14 → 16px, not the 18px body-lg ramp, and 8px under the heading. The cap is the
 * comp's own wrap point: its first line breaks at x=1211 ("...a unique | story") in an
 * 80–1360 container, which with the browser's ~1% tighter Urbanist lands at 1140px.
 */
.page-hero__lead {
	margin: var(--space-2xs) 0 0;
	max-inline-size: 71.25rem;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

@media (min-width: 64rem) {
	.page-hero {
		padding-block: 6.25rem;   /* 100px */
	}

	/*
	 * The Properties band runs 30px deeper than the other two (rule at 470 against
	 * 440) because the search bar straddles its bottom edge: the bar's box starts 52px
	 * above the rule and hangs 30px below it.
	 */
	.page-hero--properties {
		padding-block-end: 8.125rem;   /* 130px */
	}
}

/* ==========================================================================
   15. Components — home hero
   ========================================================================== */

.hero__inner {
	position: relative; /* Positioning context for the seam ornament. */
	display: grid;
	gap: 3.5625rem;   /* 57 at 390: the badge hangs 41 under the image, then 16 to the h1 */
	padding-block: var(--space-xl) var(--space-3xl);
}

/* The 390 comp leads with the image, then the copy. Source order keeps the h1 first
   for assistive tech and SEO; only the visual order is swapped. */
.hero__media {
	order: -1;
}

@media (min-width: 64rem) {
	.hero__media {
		order: 0;
	}
}

/*
 * Rhythm and sizes from the 1440 comp: a 46px two-line h1, 20 to a 16px lead that
 * wraps inside ~620px ("…to find the | home"), 50 to the 49px buttons, 50 to a row of
 * three 194x100 stat cards (20px padding, 30px number on a 1.2 line, 16px label).
 */
.hero__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}

.hero__heading {
	margin: 0;
	max-inline-size: 29rem;
	font-size: clamp(1.75rem, 1.281rem + 1.924vw, 2.875rem);   /* 28 → 46 */
	line-height: 1.2;
}

.hero__lead {
	margin: 1rem 0 0;   /* 16 at 390, 20 at 1440 (set below) */
	max-inline-size: 38.25rem;   /* 612: reproduces the comp's wrap at the browser's tighter setting */
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.hero__actions {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
	inline-size: 100%;
	margin-block-start: 2.5rem;
}

/* Stacked, full-width buttons on the 390 comp; a row from 768. */
.hero__actions .btn {
	inline-size: 100%;
}

@media (min-width: 48rem) {
	.hero__actions {
		flex-direction: row;
		flex-wrap: wrap;
	}

	.hero__actions .btn {
		inline-size: auto;
	}
}

/* 390: two cards then one spanning, 12 apart, centred text, 24px number. */
.hero__stats {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 0.75rem;
	inline-size: 100%;
	margin-block-start: 2.5rem;
}

.hero__stat:last-child:nth-child(odd) {
	grid-column: 1 / -1;
}

.hero__stat {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	padding: calc(1.25rem - 1px);
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: 0.625rem;
}

.hero__stat-number {
	font-size: clamp(1.5rem, 1.314rem + 0.76vw, 1.875rem);   /* 24 → 30 */
	line-height: 1.2;
	font-weight: var(--weight-semibold);
}

.hero__stat-label {
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.hero__media {
	position: relative;
}

.hero__image {
	inline-size: 100%;
	aspect-ratio: 358 / 302;   /* the 390 comp's 358x302 frame */
	block-size: auto;
	border-radius: 0.75rem;
	object-fit: cover;
}

/* Keep the bled-out image from creating a horizontal scrollbar. */
.hero {
	overflow: hidden;
}

.hero__image--placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	background: linear-gradient(135deg, var(--color-surface-raised), var(--color-bg));
	color: var(--color-grey-30);
}

/*
 * Decorative rotating badge. Purely visual (aria-hidden), so its rotation is governed
 * entirely by the reduced-motion guard in section 6 -- no separate opt-out needed here.
 */
/*
 * Seam ornament (client-supplied artwork). Hidden on mobile: it has no meaning of its
 * own, there is no text/image seam in the stacked layout, and leaving it in overlapped
 * and obscured the h1.
 */
/*
 * 390: the 118px ornament overlaps the image's bottom-left corner -- left edge flush
 * with the container, centre 18px above the image's bottom -- and the h1 starts 16px
 * under it. From 1024 it moves to the text/image seam (below).
 */
.hero__badge {
	display: block;
	position: absolute;
	z-index: 2;
	inset-inline-start: 0;
	inset-block-end: -2.5625rem;
	inline-size: 7.375rem;
	pointer-events: none;
}

.hero__badge img {
	display: block;
	inline-size: 100%;
	block-size: auto;
}

@media (min-width: 64rem) {
	.hero__inner {
		/*
		 * 52%, not 50%: the comp's text/image seam sits at x=750 of 1440 (measured off
		 * the export, allowing for its 7px frame), which is 30px right of the true
		 * midpoint. The right column is only a spacer reserving flow space for the
		 * absolutely-positioned image.
		 *
		 * 615px tall at 1440 (rule to strip), with the copy centred in it -- the comp
		 * leaves 98 above the h1 and 92 below the stat cards.
		 */
		grid-template-columns: 52% minmax(0, 1fr);
		align-items: center;
		gap: 0;
		min-block-size: 38.4375rem;
		padding-block: 3rem;
	}

	.hero__lead {
		margin-block-start: 1.25rem;
	}

	.hero__actions,
	.hero__stats {
		margin-block-start: 3.125rem;   /* 50px */
	}

	.hero__stats {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: var(--space-s);
	}

	.hero__stat,
	.hero__stat:last-child:nth-child(odd) {
		grid-column: auto;
		align-items: flex-start;
		text-align: start;
	}

	/* Keep copy and stat cards clear of the seam instead of butting against it. */
	.hero__content {
		padding-inline-end: var(--space-2xl);
	}

	/*
	 * Full-bleed right half. The comp splits the hero at the viewport's midpoint and
	 * runs the image flush to the right edge with no radius -- not a contained,
	 * rounded image sitting inside the grid cell.
	 *
	 * inset-inline-start: 50% resolves against the container's *content* box, whose
	 * midpoint is always the viewport midpoint because .container is centred; 50vw
	 * then carries it exactly to the viewport's right edge at every width. Verified
	 * at 1024/1440/1920 rather than assumed.
	 */
	.hero__media {
		position: absolute;
		inset-block: 0;
		inset-inline-start: 52%;
		/*
		 * Bleeds to the viewport's right edge at any width: 50% resolves against the
		 * container's content box and 50vw against the viewport, so their difference is
		 * exactly the distance from the content edge to the viewport edge. Checked at
		 * 1024/1440/1920.
		 */
		inset-inline-end: calc(-1 * (50vw - 50%));
	}

	/*
	 * Square corners, matching the comp. Verified by sampling the seam at 1440 and
	 * 1920: the #141414 -> image transition is a hard edge at the same x for every
	 * row from the hero's top to its bottom, with no antialiased inset at either
	 * corner.
	 */
	/*
	 * Pinned to the media block's box rather than sized by percentage: inside a
	 * <picture> the img's 100% height has nothing definite to resolve against in some
	 * engines and the image stopped ~100px short of the hero's bottom. inset:0 against
	 * the positioned .hero__media is unambiguous everywhere.
	 */
	.hero__image {
		position: absolute;
		inset: 0;
		inline-size: 100%;
		block-size: 100%;
		aspect-ratio: auto;
		border-radius: 0;
	}

	/*
	 * Placed to the comp, not by eye. Measured on the 1440 export (locating the
	 * artwork by its unique gold sparkle, then its centre): the circle is 129px
	 * across, centred exactly on the 52% text/image seam, with its centre 145px
	 * below the top of the hero -- i.e. 145 - 64.5 = 80px from .hero__inner's top
	 * edge, which is flush with the hero section's own top.
	 */
	/* Inside .hero__media, whose left edge IS the 52% seam. */
	.hero__badge {
		inset-block-start: 5rem;          /* 80px */
		inset-block-end: auto;
		inset-inline-start: 0;
		transform: translateX(-50%);
		inline-size: 8.0625rem;           /* 129px */
	}
}

/* ==========================================================================
   16. Components — feature strip
   ========================================================================== */

/*
 * Near full-bleed in the comps, not held inside the 1280 container, and built as two
 * nested bands. Reading a column straight down the 1440 export gives, in order:
 *   6px #191919 | 1px #262626 | 10px #141414 | card | 10px #141414 | 1px #262626 | 6px #191919
 * -- i.e. a lighter outer band (the "light gray top and bottom" visible around the
 * cards), then a bordered inner panel on the page colour holding the card grid.
 */
.feature-strip {
	background-color: var(--color-frame);
	padding-block: 0.375rem;   /* the same 6px frame, as a band above and below */
}

.feature-strip__panel {
	background-color: var(--color-bg);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
	padding: 0.625rem;         /* 10px inset before the cards */
}

.feature-strip .grid {
	gap: 0.625rem;   /* 10px between the four cards, measured on Home and Services */
}

/* Centre-aligned: icon centred above centred text, with the arrow pinned top-right. */
.feature-strip__card {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1.25rem;
	min-block-size: 10rem;   /* 160px on the comps */
	padding: var(--space-m) var(--space-m);
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	color: var(--color-text);
	text-align: center;
	text-decoration: none;
	transition: border-color var(--transition-fast);
}

.feature-strip__card:hover {
	border-color: var(--color-accent);
	color: var(--color-text);
}

/*
 * No background or radius of its own -- the supplied PNGs already carry their own ring
 * ornament, so wrapping them in a second circle double-ringed every icon.
 */
.feature-strip__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 3.75rem;   /* 60px */
	block-size: 3.75rem;
}

.feature-strip__icon img {
	inline-size: 100%;
	block-size: 100%;
}

.feature-strip__corner {
	position: absolute;
	inset-block-start: var(--space-m);
	inset-inline-end: var(--space-m);
	color: var(--color-grey-40);
}

.feature-strip__title {
	font-weight: var(--weight-semibold);
	font-size: var(--step-body);   /* 16px: cap/x-height measure 11/8 rows on the comp */
	line-height: var(--leading-compact);
}

/* ==========================================================================
   17. Components — FAQ card
   ========================================================================== */

/*
 * 413x281 on the 1440 Home comp: 40px padding, a 20px question reserved at two
 * lines so every card's answer and button start on the same row regardless of how
 * long each question is (the comp's one-line third card confirms this), 24 to a 16px
 * answer, 24 to the Read More button.
 */
.faq-card {
	padding: calc(2.5rem - 1px);
	gap: var(--space-m);
}

.faq-card__question {
	margin: 0;
	min-block-size: 3.75rem;   /* two 30px lines */
	font-size: 1.25rem;
	line-height: 1.5;
	font-weight: var(--weight-semibold);
}

.faq-card__answer {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.faq-card__action {
	margin-block-start: auto;
}

/* ==========================================================================
   18. Components — split layout (Our Journey / Our Values)
   ========================================================================== */

.eyebrow {
	display: inline-flex;
	margin-block-end: var(--space-s);
}

/*
 * 60px, not the 48 of --space-2xl: the comp's Our Journey columns are 610px each in
 * the 1280 container, which leaves exactly 60 between them.
 */
.split {
	display: grid;
	gap: 3.75rem;
}

/*
 * 64ch, not 46ch: the comp's Our Journey lead runs the full width of its column
 * (four lines to "...trust of countless clients."), where the narrower measure was
 * squeezing it into five short lines unlike the design.
 */
.split__lead {
	margin: var(--space-m) 0 0;
	color: var(--color-text-muted);
	max-inline-size: 64ch;
}

.split__stats {
	margin-block-start: var(--space-l);
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

.split__media {
	display: flex;
}

.split__media-picture {
	display: block;
	inline-size: 100%;
}

/*
 * Natural height, no radius, no cover-crop: the artwork carries its own rounded
 * corners and edge fade in its alpha channel, so any of those would clip it. The
 * placeholder below still needs a fixed box, since it has no intrinsic ratio.
 */
.split__image {
	inline-size: 100%;
	block-size: auto;
}

.split__image--placeholder {
	block-size: 20rem;
	border-radius: var(--radius-card);
}

.split__image--placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	background: linear-gradient(135deg, var(--color-surface-raised), var(--color-bg));
	color: var(--color-grey-30);
}

@media (min-width: 64rem) {
	.split {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		align-items: center;
	}

	.split--reverse .split__content {
		order: 2;
	}

	.split__image--placeholder {
		block-size: 28rem;
	}
}

/* --- Values grid: 2x2, hairline-divided -------------------------------- */

.value-grid {
	display: grid;
	grid-template-columns: repeat(1, minmax(0, 1fr));
	gap: 0;
	padding: var(--space-l);
	background-color: var(--color-bg);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
	box-shadow: 0 0 0 8px var(--color-frame); /* Same framed treatment as .plain-card, but its ring measures 8px here, not 6. */
}

/*
 * Icon and title share the first row, with the description spanning both columns
 * beneath -- the comp aligns the description with the icon's left edge rather than
 * indenting it under the title.
 */
.value-grid__item {
	display: grid;
	grid-template-columns: auto 1fr;
	align-items: center;
	gap: var(--space-2xs) var(--space-s);
	padding: var(--space-l);
}

/*
 * No ring or background of its own -- the supplied PNGs already carry their ring, so
 * adding one here would double it up (the same trap the feature strip hit).
 */
.value-grid__icon {
	grid-row: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 3rem;
	block-size: 3rem;
}

.value-grid__icon img {
	inline-size: 100%;
	block-size: 100%;
}

.value-grid__title {
	grid-row: 1;
	margin: 0;
	font-size: var(--step-body-lg);
	font-weight: var(--weight-semibold);
}

.value-grid__text {
	grid-column: 1 / -1;
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);
	line-height: var(--leading-body);
}

/*
 * Hairlines between the quadrants, drawn on the inner edges only so the grid reads as
 * a divided block rather than four separate boxes.
 */
@media (min-width: 40rem) {
	.value-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.value-grid__item:nth-child(odd) {
		border-inline-end: 1px solid var(--color-border);
	}

	.value-grid__item:nth-child(-n + 2) {
		border-block-end: 1px solid var(--color-border);
	}
}

@media (max-width: 39.999rem) {
	.value-grid__item:not(:last-child) {
		border-block-end: 1px solid var(--color-border);
	}
}

/* ==========================================================================
   19. Components — plain card (Our Achievements)
   ========================================================================== */

/*
 * Framed: 1px border plus a 6px --color-frame ring outside it. Measured on the comp
 * across both a card's outer edge and the gap between two cards -- the ring plateaus
 * at #191919 for ~6px on every side before falling back to the page colour.
 */
.plain-card {
	padding: var(--space-l);
	box-shadow: 0 0 0 6px var(--color-frame);
}

.plain-card__title {
	margin: 0 0 var(--space-s);
	font-size: var(--step-h4);
}

.plain-card__text {
	margin: 0;
	color: var(--color-text-muted);
}

/* ==========================================================================
   20. Components — step card (Navigating the Estatein Experience)
   ========================================================================== */

/*
 * This grid is deliberately tighter than the site default. Measuring the three card
 * boxes on the 1440 comp puts their borders at x 81–493, 514–926 and 947–1359, i.e. a
 * 20px column gap, where every other 3-up on the same page (Our Achievements, borders
 * at 81–486 / 517–923 / 954–1359) sits on the standard 32px. Rows are 40px apart at
 * 1440 and 30px at 390 — the same ramp as the cards' own padding, so both read from
 * --step-rhythm rather than repeating the clamp.
 *
 * The three clamps interpolate between the two comps we actually have (390 and 1440)
 * the same way the global type scale does, so nothing jumps at a breakpoint.
 */
.step-grid {
	--step-rhythm: clamp(1.875rem, 1.643rem + 0.95vw, 2.5rem);   /* 30 → 40 */
	--step-text: var(--step-body-fluid);                         /* 14 → 16 */
	--step-title: clamp(1.125rem, 1.079rem + 0.19vw, 1.25rem);   /* 18 → 20 */

	row-gap: var(--step-rhythm);
}

/* Only meaningful once the grid is multi-column; below 768 it is a single column. */
@media (min-width: 48rem) {
	.step-grid {
		column-gap: 1.25rem;   /* 20px */
	}
}

.step-card {
	display: flex;
	flex-direction: column;
}

/*
 * The "Step 01" label sits above the card in its own band — 52px tall in both the 390
 * and the 1440 comp, so it is fixed, not fluid — carrying a single 1px accent rule down
 * its leading edge and nothing else: no top hairline, no background. Checked directly:
 * scanning the comp across the label row finds only #141414. (An earlier draft added a
 * top border the design does not have.)
 */
.step-card__label {
	display: flex;
	align-items: center;
	min-block-size: 3.25rem;                /* 52px */
	padding-inline-start: var(--space-s);   /* 16px, measured on both comps */
	border-inline-start: 1px solid var(--color-accent);
	color: var(--color-text);
	font-size: var(--step-body);            /* 16px at both comps */
}

/*
 * The card's BORDER is itself a radial gradient anchored at the top-left corner, which
 * is what lets the label's accent rule flow straight on into it: full #703BF7 at the
 * corner, falling linearly to the ordinary #262626 border colour by 152px along BOTH
 * the top and the left edge. One circular ramp, in other words — not a flat border with
 * a separate highlight painted over it.
 *
 * That continuity is also why the top-left corner is square while the other three are
 * rounded: the comp still shows full accent at the exact corner pixel, which a radius
 * would have cut away. The other three step 6, 4, 3, 2, 1, 0px inward over five rows,
 * which is an 8px radius (--radius-button).
 *
 * Painted with the two-background-clip technique — the fill clipped to padding-box and
 * the gradient clipped to border-box, showing through a transparent border — because
 * border-image cannot be used here without losing those corner radii.
 *
 * A softer glow sits inside the card over the same corner: 22% accent at the corner,
 * gone by 64px measured diagonally in.
 *
 * Cross-check on the whole model: 40 padding + 28 title + 16 gap + 4 × 24 body + 40
 * padding + 2 borders = 222px, which is exactly the card height on the 1440 comp; the
 * same sum at the mobile end gives 185px, exactly the 390 comp.
 */
.step-card__body {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	padding: var(--step-rhythm);
	border: 1px solid transparent;
	border-radius: 0 var(--radius-button) var(--radius-button) var(--radius-button);
	background:
		radial-gradient(circle 64px at 0 0, rgba(112, 59, 247, 0.22), transparent 100%) padding-box,
		linear-gradient(var(--color-bg), var(--color-bg)) padding-box,
		radial-gradient(circle 152px at 0 0, var(--color-accent), var(--color-border)) border-box;
}

.step-card__title {
	margin: 0 0 var(--step-text);   /* 14 → 16, tracking the body copy */
	font-size: var(--step-title);
	line-height: 1.4;
}

.step-card__text {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-text);
	line-height: var(--leading-compact);   /* 1.5 — 24px on 16px copy at 1440 */
}

/* ==========================================================================
   21. Components — client card (Our Valued Clients)
   ========================================================================== */

/*
 * Client case-study card, measured on the 1440 About comp: border box 620x415 at
 * x 81-700 and 740-1360, so a 40px column gap, 40px padding, a 12px radius and the
 * page's framed treatment -- 1px #262626 border plus an 8px #191919 ring outside it,
 * the same ring the Our Values panel carries.
 *
 * The three blocks are 30px apart, which is what makes the sum land exactly:
 * 40 + 66 head + 30 + 51 meta + 30 + 157 quote + 40 + 2 borders = 415.
 */
.client-card {
	--client-pad: var(--space-m);      /* 24 at 390 → 40 at 1440 */
	--client-gap: var(--space-m);      /* 24 → 30 */
	--client-quote-pad: 1.25rem;       /* 20 → 24 */

	display: flex;
	flex-direction: column;
	gap: var(--client-gap);
	padding: var(--client-pad);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;            /* 12px */
	background-color: var(--color-bg);
	box-shadow: 0 0 0 8px var(--color-frame);
}

/*
 * Stacked at 390, where the comp gives the button its own full-width row under the
 * name, and a centred row at 1024 up -- there the button spans y 3627-3673 against a
 * 3618-3684 identity block, i.e. the two are centred on each other.
 */
.client-card__head {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: var(--space-s);
}

@media (min-width: 64rem) {
	.client-card {
		--client-pad: var(--space-xl);     /* 40px */
		--client-gap: 1.875rem;            /* 30px */
		--client-quote-pad: var(--space-m);/* 24px */
	}

	.client-card__head {
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: var(--space-m);
	}
}

.client-card__since {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body);       /* 16px */
	line-height: var(--leading-compact);
}

.client-card__name {
	margin: var(--space-2xs) 0 0;
	font-size: 1.5rem;                 /* 24px */
	line-height: 1.4;
}

/*
 * Two equal columns split by a single hairline. The comp puts that rule at x=390.5
 * of a 121-660 content box -- dead centre -- and runs it the full height of the row,
 * so it is a border on the second column rather than a separate element.
 */
.client-card__meta {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
}

.client-card__meta-item + .client-card__meta-item {
	padding-inline-start: var(--space-m);
	border-inline-start: 1px solid var(--color-border);
}

.client-card__meta-label {
	display: flex;
	align-items: center;
	gap: var(--space-2xs);
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);    /* 14px */
	line-height: var(--leading-compact);
}

.client-card__meta-value {
	display: block;
	margin-block-start: var(--space-2xs);
	font-size: var(--step-body);       /* 16px */
	line-height: var(--leading-compact);
}

/*
 * Same 1px border and 12px radius as the card, inset by the card's 40px padding.
 *
 * It grows to absorb whatever height the row's tallest card sets: on the comp both
 * quote boxes run 3795-3951 even though GreenTech's quote is two lines to ABC's
 * three, so the box stretches and the text stays top-aligned inside it.
 */
.client-card__quote-box {
	flex: 1 1 auto;
	padding: var(--client-quote-pad);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
}

.client-card__quote-label {
	margin: 0 0 var(--space-xs);
	color: var(--color-text-muted);
	font-size: var(--step-body);
	line-height: var(--leading-compact);
}

.client-card__quote {
	margin: 0;
	font-size: var(--step-body);
	line-height: var(--leading-compact);
}

/* Two-per-page variant for carousels that don't use the 3-up default. */
@media (min-width: 48rem) {
	.carousel__track--cols-2 {
		grid-auto-columns: calc(50% - (var(--grid-gap) / 2));
	}
}

/*
 * Our Valued Clients runs a wider gutter than the 32px default -- the comp's two card
 * boxes sit at x 81-700 and 740-1360, so 40 between them. Set as a local --grid-gap so
 * the column-width calc above picks it up too rather than needing its own override.
 */
.carousel--gap-40 .carousel__track {
	--grid-gap: 2.5rem;
	gap: var(--grid-gap);
}

/* ==========================================================================
   22. Components — property search bar
   ========================================================================== */

/*
 * Search bar + filter panel, measured on the Properties comps.
 *
 * 1440: the bar is a 1100x81 box centred in the container (x 170–1269), 10px #191919
 * ring outside a 1px border, 16px padding around an 18px input and a 148x49 Find
 * Property button; it starts 52px above the hero's rule and hangs 30 below it. The
 * filter panel below is the full 1280, #1A1A1A, 10px padding, five 52px selects 20px
 * apart, and it tucks 2px under the bar's bottom edge.
 *
 * 390: the bar is full-width, 68 tall with 10px padding and a 60x48 icon-only disc,
 * sits 40px clear under the hero rather than straddling it, and the panel stacks the
 * selects with 20px padding and 20px gaps.
 */
.property-search {
	position: relative;
	margin-block-start: var(--space-xl);
	margin-block-end: var(--space-2xl);
}

.property-search__bar {
	display: flex;
	align-items: center;
	gap: var(--space-s);
	padding: calc(0.625rem - 1px);
	background-color: var(--color-bg);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	box-shadow: 0 0 0 10px var(--color-frame);
}

.property-search__input {
	flex: 1 1 auto;
	min-inline-size: 0;
	padding: 0 calc(1.25rem - 0.625rem + 1px);   /* 20px from the bar's outer edge */
	background: transparent;
	border: 0;
	color: var(--color-text);
	font-size: var(--step-body);
	line-height: 1.5;
}

.property-search__input::placeholder {
	color: var(--color-placeholder);
}

.property-search__input:focus-visible {
	outline: 2px solid var(--color-accent);
	outline-offset: 4px;
	border-radius: var(--radius-button);
}

.property-search__submit {
	flex: 0 0 auto;
	inline-size: 3.75rem;   /* 60 */
	block-size: 3rem;       /* 48 */
	padding: 0;
}

.property-search__submit-label {
	display: none;
}

/* Panel. position/z-index so it paints over the bar's ring where the two meet. */
.property-search__filters {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
	margin-block-start: 1.25rem;
	padding: 1.25rem;
	background-color: var(--color-surface);
	border-radius: 0.75rem;
}

/*
 * Icon cell | label | chevron, drawn around a native <select> that is stretched over
 * the whole field and made transparent -- so keyboard, screen reader and mobile
 * pickers all still get the real control, and the visuals match the comp.
 */
.property-search__field {
	position: relative;
	display: flex;
	align-items: center;
	block-size: 3.25rem;   /* 52 */
	background-color: var(--color-bg);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-button);
}

.property-search__field-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	align-self: stretch;
	padding-inline: calc(1rem - 1px) 0.5rem;
	border-inline-end: 1px solid var(--color-border);
	color: var(--color-text-muted);
}

/*
 * margin-inline-start: auto is what parks this at the trailing edge -- the <select>
 * between icon and chevron is absolutely positioned, so without it the chevron sat
 * flush against the icon cell, over the first letter of the label.
 */
.property-search__field-chevron {
	display: flex;
	align-items: center;
	flex: 0 0 auto;
	margin-inline-start: auto;
	padding-inline: 0.5rem calc(1.25rem - 1px);
	color: var(--color-text);
}

.property-search__select {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	padding-inline: 3.25rem 3rem;   /* clear of the icon cell and the chevron */
	background: transparent;
	border: 0;
	border-radius: inherit;
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);
	appearance: none;
	cursor: pointer;
}

.property-search__select option {
	color: var(--color-text);
	background-color: var(--color-surface);
}

.property-search__select:disabled {
	cursor: not-allowed;
}

.property-search__select:focus-visible {
	outline: 2px solid var(--color-accent);
	outline-offset: 2px;
}

@media (min-width: 48rem) {
	.property-search__filters {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

@media (min-width: 64rem) {
	.property-search {
		margin-block-start: -3.25rem;   /* 52px up over the hero's rule */
	}

	.property-search__bar {
		inline-size: 68.75rem;   /* 1100 */
		max-inline-size: 100%;
		margin-inline: auto;
		padding: calc(1rem - 1px);
	}

	.property-search__input {
		font-size: var(--step-body-lg);   /* 18 */
		padding-inline: calc(1.25rem - 1rem + 1px);
	}

	.property-search__submit {
		inline-size: auto;
		block-size: auto;
		padding: calc(0.875rem - 1px) calc(1.25rem - 1px);
		gap: var(--space-2xs);
	}

	.property-search__submit-label {
		display: inline;
	}

	.property-search__filters {
		grid-template-columns: repeat(5, minmax(0, 1fr));
		gap: 1.25rem;
		margin-block-start: 0.125rem;
		padding: 0.625rem;
	}
}

/* ==========================================================================
   24. Components — forms (enquiry + contact, shared)
   --------------------------------------------------------------------------
   Measured on the Properties comp's "Let's Make it Happen" card at 1440: a 1280-wide
   card with 50px padding and 12px radius; four 273px columns 30px apart; 16px
   semibold labels 14px above 52px controls (#1A1A1A fill, #262626 border, 6px
   radius, 14px placeholder in #666 inset 20); rows 30px apart; a 122px textarea; and
   a footer row of 24px checkbox + 16px consent text against a 191x52 submit. The
   Contact comp uses the same card with three columns.
   ========================================================================== */

.enquiry__form {
	--form-pad: calc(1.25rem - 1px);   /* 20 → 50 at 1024, minus the inside stroke */
	--form-gap: 1.5rem;                /* 24 → 30 */
	--form-label-gap: 0.375rem;        /* 6 → 14 */

	position: relative;
	padding: var(--form-pad);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-bg);
}

@media (min-width: 64rem) {
	.enquiry__form {
		--form-pad: calc(3.125rem - 1px);
		--form-gap: 1.875rem;
		--form-label-gap: 0.875rem;
	}
}

.form-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--form-gap);
}

.form-field--full {
	grid-column: 1 / -1;
}

.form-field label,
.form-field legend {
	display: block;
	margin-block-end: var(--form-label-gap);
	padding: 0;
	border: 0;
	font-size: var(--step-body);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-compact);
}

/*
 * The controls. Height is fixed at 52 rather than derived from padding so the select
 * (whose intrinsic height differs from an input's in every browser) lines up with the
 * inputs beside it.
 */
.form-field input[type="text"],
.form-field input[type="email"],
.form-field input[type="tel"],
.form-field input[type="search"],
.form-field select,
.form-field textarea,
.form-static-value {
	inline-size: 100%;
	block-size: 3.25rem;
	padding: 0 calc(1.25rem - 1px);
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: 0.375rem;
	color: var(--color-text);
	font-size: var(--step-body-sm);
	line-height: 1.5;
}

/* display:block -- an inline textarea leaves a ~6px baseline gap under itself that
   pushed the footer 6px off the comp. */
.form-field textarea {
	display: block;
	block-size: 7.625rem;   /* 122px */
	padding-block: calc(1rem - 1px);
	resize: vertical;
}

.form-field input::placeholder,
.form-field textarea::placeholder {
	color: var(--color-placeholder);
}

/* Native select made transparent over its own chevron, same trick as the search bar. */
.form-select {
	position: relative;
	display: block;
}

.form-select select {
	appearance: none;
	padding-inline-end: 3rem;
	cursor: pointer;
}

.form-select select option {
	color: var(--color-text);
	background-color: var(--color-surface);
}

.form-select__chevron {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-end: calc(1.25rem - 1px);
	transform: translateY(-50%);
	color: var(--color-text);
	pointer-events: none;
}

.form-static-value {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-2xs);
	color: var(--color-text);
}

.form-static-value .icon {
	flex: 0 0 auto;
}

/*
 * Two side-by-side fields, each laid out as [icon] [input] [radio] inside a single
 * bordered control -- matching the comp, where the radio sits at the trailing edge of
 * its own field rather than in front of it.
 *
 * Selectors are scoped under .form-field because the generic `.form-field label`
 * rule above sets display:block and, at (0,1,1), outranks a bare `.radio-pair__option`
 * class at (0,1,0) -- which was silently collapsing this row into a vertical stack.
 */
.radio-pair {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--space-s);
}

.form-field .radio-pair__option {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	margin: 0;
	block-size: 3.25rem;
	padding: 0 calc(1.25rem - 1px);
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: 0.375rem;
	font-weight: var(--weight-regular);
}

.form-field .radio-pair__option:focus-within {
	outline: var(--focus-ring-width) solid var(--focus-ring-color);
	outline-offset: var(--focus-ring-offset);
}

.radio-pair__option .icon {
	flex: 0 0 auto;
	color: var(--color-text);
}

/*
 * The radio is drawn to the comp: a 10px accent ring at the trailing edge that fills
 * solid when checked. Source order keeps it first for the label; `order` moves it.
 */
.form-field .radio-pair__option input[type="radio"] {
	order: 3;
	flex: 0 0 auto;
	appearance: none;
	inline-size: 0.625rem;
	block-size: 0.625rem;
	margin: 0;
	border: 1.5px solid var(--color-accent);
	border-radius: 50%;
	background-color: transparent;
}

.form-field .radio-pair__option input[type="radio"]:checked {
	background-color: var(--color-accent);
}

.form-field .radio-pair__option input[type="radio"]:focus-visible {
	outline: var(--focus-ring-width) solid var(--focus-ring-color);
	outline-offset: 3px;
}

.form-field .radio-pair__option input[type="tel"],
.form-field .radio-pair__option input[type="email"] {
	order: 2;
	flex: 1 1 auto;
	min-inline-size: 0;
	block-size: auto;
	padding: 0;
	background: none;
	border: 0;
	border-radius: 0;
}

.form-field .radio-pair__option input[type="tel"]:focus-visible,
.form-field .radio-pair__option input[type="email"]:focus-visible {
	outline: none;
}

/* Footer: consent left, submit right; stacked at 390 where the comp stacks them. */
.form-footer {
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	margin-block-start: var(--form-gap);
}

.form-consent {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	color: var(--color-text-muted);
	font-size: var(--step-body);
	line-height: var(--leading-compact);
}

/* 24px box, surface fill, 1px border, 4px radius -- accent tick when checked. */
.form-consent input {
	flex: 0 0 auto;
	appearance: none;
	inline-size: 1.5rem;
	block-size: 1.5rem;
	margin: 0;
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: 0.25rem;
	display: grid;
	place-content: center;
}

.form-consent input::before {
	content: "";
	inline-size: 0.75rem;
	block-size: 0.75rem;
	border-radius: 2px;
	background-color: var(--color-accent);
	transform: scale(0);
	transition: transform var(--transition-fast);
}

.form-consent input:checked::before {
	transform: scale(1);
}

.form-consent input:focus-visible {
	outline: var(--focus-ring-width) solid var(--focus-ring-color);
	outline-offset: var(--focus-ring-offset);
}

.form-consent a {
	color: var(--color-text);
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* 191x52 on the comp: 14px text in 34px side padding, as tall as the inputs. */
.form-submit {
	block-size: 3.25rem;
	padding-inline: calc(2.125rem - 1px);
}

/* --- Validation and status ------------------------------------------------ */

.form-honeypot {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.form-status {
	margin-block-end: var(--form-gap);
	padding: var(--space-s) var(--space-m);
	border: 1px solid var(--color-border);
	border-radius: 0.375rem;
	background-color: var(--color-surface);
	font-size: var(--step-body);
}

.form-status--success {
	border-color: var(--color-accent);
}

.form-status--error {
	border-color: #E5484D;
}

.form-error {
	margin: var(--space-2xs) 0 0;
	color: #F28B8E;   /* 4.9:1 on #141414 */
	font-size: var(--step-body-sm);
}

.form-field [aria-invalid="true"] {
	border-color: #E5484D;
}

/* Contact's consent block is a simple row; enquiry wraps it so the error can sit under it. */
.form-consent-wrap {
	display: flex;
	flex-direction: column;
}

@media (min-width: 40rem) {
	.radio-pair {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 48rem) {
	.form-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.form-footer {
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
	}
}

@media (min-width: 64rem) {
	.form-grid--4 {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}

	.form-grid--3 {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	/* Budget and Preferred Contact Method each take half the four-column row. */
	.form-grid--4 .form-field--half {
		grid-column: span 2;
	}
}

/* ==========================================================================
   25. Components — pagination
   ========================================================================== */

.pagination,
.nav-links {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: var(--space-2xs);
	margin-block-start: var(--space-2xl);
}

.page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-inline-size: 2.75rem;
	block-size: 2.75rem;
	padding-inline: var(--space-s);
	border-radius: var(--radius-pill);
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	color: var(--color-text);
	text-decoration: none;
	font-size: var(--step-body-sm);
}

.page-numbers:hover {
	border-color: var(--color-accent);
}

.page-numbers.current {
	background-color: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-on-accent);
}

.page-numbers.dots {
	border-color: transparent;
	background: none;
}

.pagination__prev-icon {
	transform: rotate(180deg);
}

.empty-state {
	color: var(--color-text-muted);
	text-align: center;
	padding-block: var(--space-2xl);
}

/* ==========================================================================
   26. Components — single property
   --------------------------------------------------------------------------
   Measured on the 1440 property-details comp throughout.
   ========================================================================== */

/* --- Header: title + location pill left, price right ------------------------ */

.property-header {
	padding-block: 3.75rem 2.5rem;
}

.property-header__inner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s) var(--space-l);
}

.property-header__title-group {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1.25rem;
}

.property-header__title {
	margin: 0;
	font-size: clamp(1.5rem, 1.314rem + 0.76vw, 1.875rem);   /* 24 → 30 */
	line-height: 1.5;
}

/* 37px bordered pill with the pin and 14px text, the comp's "Malibu, California". */
.property-header__location {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	block-size: 2.3125rem;
	padding-inline: calc(0.75rem - 1px);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-button);
	background-color: var(--color-bg);
	font-size: var(--step-body-sm);
	line-height: 1;
}

.property-header__price {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}

.property-header__price-value {
	font-size: 1.5rem;
	line-height: 1.5;
	font-weight: var(--weight-semibold);
}

@media (min-width: 64rem) {
	.property-header {
		padding-block: 5.3125rem 2.875rem;   /* row top at 211, panel at 299 */
	}

	.property-header__price {
		align-items: flex-end;
	}
}

/* --- Gallery panel ---------------------------------------------------------- */

/*
 * 1280 x 780 framed panel (#1A1A1A, 40px padding). Thumb strip: a #141414 bar, 10px
 * padding, 122x74 thumbs 10px apart. Two 585x506 frames 30px apart, 20px under the
 * strip. Pager: a 206x60 pill, two 44px discs, dashes between.
 */
.property-gallery {
	--gallery-pad: 1.25rem;

	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	padding: calc(var(--gallery-pad) - 1px);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-surface);
}

.property-gallery__thumbs {
	display: flex;
	gap: 0.625rem;
	margin: 0;
	padding: calc(0.625rem - 1px);
	list-style: none;
	overflow-x: auto;
	scrollbar-width: thin;
	border: 1px solid var(--color-border);
	border-radius: 0.625rem;
	background-color: var(--color-bg);
}

.property-gallery__thumbs li {
	flex: 0 0 auto;
}

.property-gallery__thumb {
	display: block;
	inline-size: 7.625rem;
	block-size: 4.625rem;
	border-radius: 0.5rem;
	overflow: hidden;
	opacity: 0.55;
	transition: opacity var(--transition-fast);
}

.property-gallery__thumb img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

.property-gallery__thumb.is-current,
.property-gallery__thumb:hover,
.property-gallery__thumb:focus-visible {
	opacity: 1;
}

.property-gallery__stage {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
}

.property-gallery__frame {
	margin: 0;
	aspect-ratio: 585 / 506;
	border-radius: 0.625rem;
	overflow: hidden;
	background-color: var(--color-bg);
}

.property-gallery__frame img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

.property-gallery__pager {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	align-self: center;
	padding: 0.5rem;
	border-radius: var(--radius-pill);
	background-color: var(--color-bg);
}

.property-gallery__prev .icon {
	transform: rotate(180deg);
}

.property-gallery__dots {
	display: flex;
	gap: 0.25rem;
}

.property-gallery__dot {
	inline-size: 0.625rem;
	block-size: 0.1875rem;
	border-radius: var(--radius-pill);
	background-color: var(--color-border);
}

.property-gallery__dot.is-active {
	background-color: var(--color-accent);
}

@media (min-width: 64rem) {
	.property-gallery {
		--gallery-pad: 2.5rem;
	}

	.property-gallery__stage {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 1.875rem;
	}
}

/* --- Description + features ---------------------------------------------- */

.split--top {
	align-items: start;
	gap: 1.25rem;
}

.property-description,
.property-features {
	display: flex;
	flex-direction: column;
	padding: calc(2.5rem - 1px);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-bg);
}

.property-description__heading,
.property-features__heading {
	margin: 0 0 0.875rem;
	font-size: 1.25rem;
	line-height: 1.5;
}

.property-description__content {
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.property-description__content > :last-child {
	margin-block-end: 0;
}

/* Three equal cells split by hairlines, under a rule 30px below the copy. */
.property-description__meta {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	margin-block-start: 1.875rem;
	padding-block-start: 1.25rem;
	border-block-start: 1px solid var(--color-border);
}

.property-description__meta-item + .property-description__meta-item {
	padding-inline-start: 1.25rem;
	border-inline-start: 1px solid var(--color-border);
}

.property-description__meta-label {
	display: flex;
	align-items: center;
	gap: 0.375rem;
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);
	line-height: var(--leading-compact);
}

.property-description__meta-value {
	display: block;
	margin-block-start: 0.5rem;
	font-size: 1.25rem;
	line-height: 1.5;
	font-weight: var(--weight-semibold);
}

.property-features__list {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	margin: 1.25rem 0 0;
	padding: 0;
	list-style: none;
}

/* Accent bar down the left, surface fading to the page colour across the row. */
.property-features__item {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	min-block-size: 3.5rem;
	padding: 1rem 1.25rem 1rem calc(1.25rem - 1px);
	border-inline-start: 1px solid var(--color-accent);
	background: linear-gradient(90deg, var(--color-surface), transparent 100%);
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.property-features__item .icon {
	flex: 0 0 auto;
	color: var(--color-text);
}

/* --- Inquire: split layout -------------------------------------------------- */

@media (min-width: 64rem) {
	.enquiry--split .enquiry__inner {
		display: grid;
		grid-template-columns: 25.8125rem minmax(0, 1fr);   /* 413 | the rest, 80 between */
		gap: 5rem;
		align-items: start;
	}

	.enquiry--split .section-header {
		margin-block-end: 0;
	}

	/* 40px in the narrow column -- "Inquire About Seaside | Serenity Villa" on the comp. */
	.enquiry--split .section-header__heading {
		font-size: 2.5rem;
	}

	.form-grid--2 {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* --- Pricing ------------------------------------------------------------------ */

/* Note strip: "Note" label | rule | copy, 70px tall. */
.pricing-note {
	display: flex;
	align-items: center;
	gap: var(--space-s);
	padding: calc(1.25rem - 1px) calc(2.5rem - 1px);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-surface);
}

.pricing-note__label {
	flex: 0 0 auto;
	padding-inline-end: var(--space-s);
	border-inline-end: 1px solid var(--color-border);
	font-size: 1.25rem;
	font-weight: var(--weight-semibold);
}

.pricing-note p {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);
	line-height: var(--leading-compact);
}

/* Listing price in a 236px aside (80–316), panels from 316 to the right edge. */
.pricing-layout {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem 0;
	margin-block-start: 2.5rem;
}

.pricing-layout__price {
	display: flex;
	flex-direction: column;
}

.pricing-layout__price-value {
	font-size: 1.875rem;
	line-height: 1.5;
	font-weight: var(--weight-semibold);
}

.pricing-panels {
	display: flex;
	flex-direction: column;
	gap: 2.5rem;
}

.pricing-panel {
	padding: calc(2.5rem - 1px);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-bg);
}

/* Title | Learn More, a rule 30px under the button. */
.pricing-panel__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
	padding-block-end: 1.875rem;
	border-block-end: 1px solid var(--color-border);
}

.pricing-panel__head h3 {
	margin: 0;
	font-size: 1.25rem;
	line-height: 1.5;
}

/*
 * Rows are a two-column grid: a hairline between the two cells of a row, a rule
 * between rows, 30px of padding all round each cell. A lone last cell spans the row.
 */
.pricing-panel__body {
	display: grid;
	grid-template-columns: 1fr;
}

.pricing-row {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	padding: 1.875rem 0;
	border-block-end: 1px solid var(--color-border);
}

.pricing-row:last-child {
	padding-block-end: 0;
	border-block-end: 0;
}

.pricing-row__label {
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);
	line-height: var(--leading-compact);
}

.pricing-row__figure {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.625rem;
}

.pricing-row__value {
	font-size: 1.25rem;
	line-height: 1.5;
	font-weight: var(--weight-semibold);
}

.pricing-row__descriptor {
	block-size: 2.0625rem;
	padding-inline: calc(0.75rem - 1px);
	border: 1px solid var(--color-border);
	background-color: var(--color-surface);
	color: var(--color-text-muted);
	white-space: normal;
}

@media (min-width: 48rem) {
	.pricing-panel__body:not(.pricing-panel__body--single) {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: 0;
	}

	.pricing-panel__body:not(.pricing-panel__body--single) .pricing-row:nth-child(odd):not(:last-child) {
		padding-inline-end: 1.25rem;
		border-inline-end: 1px solid var(--color-border);
	}

	.pricing-panel__body:not(.pricing-panel__body--single) .pricing-row:nth-child(even) {
		padding-inline-start: 1.25rem;
	}

	/* The second-to-last cell keeps its rule when a lone last cell follows it. */
	.pricing-panel__body:not(.pricing-panel__body--single) .pricing-row:nth-last-child(2):nth-child(odd) {
		border-block-end: 1px solid var(--color-border);
		padding-block-end: 1.875rem;
	}

	.pricing-panel__body:not(.pricing-panel__body--single) .pricing-row:last-child:nth-child(odd) {
		grid-column: 1 / -1;
	}
}

@media (min-width: 64rem) {
	.pricing-layout {
		grid-template-columns: 14.75rem minmax(0, 1fr);
	}
}

/* ==========================================================================
   27. Components — service group (Services page)
   --------------------------------------------------------------------------
   Measured on the 1440 Services comp. Icon cards are 413 wide, 20 apart, 40px
   padding: a 60px ring with a 20px accent-tint glyph, the 20px title centred on it
   16px to the right, then 16px copy 20px below, aligned with the ring's left edge.
   The wide promo takes two cells (846x232) with a 24px title, 16px copy and a
   secondary button centred on the right; the third group turns the whole thing
   sideways -- header and a stacked promo in a 413px column, the four cards in a
   #1A1A1A panel with 10px padding and gaps on the right.
   ========================================================================== */

.service-group__grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
}

.service-card {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	padding: calc(2.5rem - 1px);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-bg);
}

.service-card__head {
	display: flex;
	align-items: center;
	gap: var(--space-s);
}

/*
 * The ring is a conic gradient, not a flat stroke: sampling round the comp's circle
 * at 45° steps gives #65 52 93 at the top, #5E4E89 lower-left, fading to near the
 * card colour at 45° and 225°. Two background-clip layers paint it through a
 * transparent border so the circle keeps its radius.
 */
.service-card__icon,
.ring-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	inline-size: 3.75rem;
	block-size: 3.75rem;
	border: 1px solid transparent;
	border-radius: 50%;
	background:
		linear-gradient(var(--color-bg), var(--color-bg)) padding-box,
		conic-gradient(from 45deg, #1A1A1A 0deg, #5E4E89 90deg, #3A3250 135deg, #26212F 180deg, #655293 270deg, #544579 315deg, #1A1A1A 360deg) border-box;
	color: var(--color-accent-tint);
}

.service-card__title {
	margin: 0;
	font-size: clamp(1.125rem, 1.079rem + 0.19vw, 1.25rem);   /* 18 → 20 */
	line-height: 1.5;
}

.service-card__text {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

/* Promo: surface fill with the wavy line-art showing through, same overlay as the hero. */
.service-promo {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	padding: calc(2.5rem - 1px);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-surface);
	isolation: isolate;
	overflow: hidden;
}

.service-promo::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background-image: url("../images/wavy.png");
	background-size: cover;
	background-position: center;
	mix-blend-mode: screen;
	opacity: 0.35;
	pointer-events: none;
}

.service-promo__copy {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

.service-promo__title {
	margin: 0;
	font-size: clamp(1.25rem, 1.157rem + 0.38vw, 1.5rem);   /* 20 → 24 */
	line-height: 1.5;
}

.service-promo__text {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

/* Stacked promo's button spans the card (390 and the split column both do this). */
.service-promo__cta {
	inline-size: 100%;
}

/* Split layout */
.service-group__split {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
}

.service-group__aside .section-header {
	margin-block-end: 2.5rem;   /* 40 from the lead to the stacked promo */
}

.service-group__panel {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0.625rem;
	padding: 0.625rem;
	border-radius: 0.75rem;
	background-color: var(--color-surface);
}

@media (min-width: 48rem) {
	.service-group__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.service-group__panel {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 64rem) {
	.service-group__grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	.service-promo--wide {
		grid-column: span 2;
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: var(--space-l);
	}

	.service-promo--wide .service-promo__copy {
		max-inline-size: 47.5rem;   /* 760: the comp's copy wraps inside ~760 of the 766 available */
	}

	.service-promo--wide .service-promo__cta {
		inline-size: auto;
		flex: 0 0 auto;
	}

	.service-group__split {
		grid-template-columns: 25.8125rem minmax(0, 1fr);   /* 413 | 819, 48 between */
		gap: 3rem;
		align-items: start;
	}

	/* The panel's top lines up with the heading, not the sparkle marker above it. */
	.service-group__panel {
		margin-block-start: 2.5rem;
	}
}

/* ==========================================================================
   28. Components — contact page
   ========================================================================== */

/* Contact channels reuse the feature strip; the ring-icon class draws the ring the
   pre-ringed PNGs bring with them on Home/Services. */
.contact-channels .feature-strip__icon.ring-icon {
	inline-size: 3.75rem;
	block-size: 3.75rem;
}

.feature-strip__card--social {
	cursor: default;
}

/* Three links in a row, 20px apart, as the comp's fourth card. */
.feature-strip__social-links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.75rem 1rem;
}

.feature-strip__social-links a {
	color: var(--color-text);
	text-decoration: none;
}

.feature-strip__social-links a:hover {
	color: var(--color-accent-tint);
	text-decoration: underline;
}

/* --- Office filter ---------------------------------------------------------- */

/*
 * A #1A1A1A tray, 10px padding, holding three 124x49 buttons 10px apart; the active
 * one drops to the page colour with a visible border. Measured on the 1440 Contact
 * comp (tray 80–493 x 1855–1924).
 */
.office-filter {
	display: flex;
	gap: 0.625rem;
	padding: 0.625rem;
	margin-block-end: 2.5rem;
	background-color: var(--color-surface);
	border-radius: 0.75rem;
}

@media (min-width: 48rem) {
	.office-filter {
		display: inline-flex;
	}

	.office-filter__btn {
		flex: 0 0 auto;
		min-inline-size: 7.75rem;
		padding-inline: var(--space-m);
	}
}

.office-filter__btn {
	flex: 1 1 0;
	min-inline-size: 0;
	padding: calc(0.875rem - 1px) var(--space-s);
	border: 1px solid transparent;
	border-radius: var(--radius-button);
	color: var(--color-text);
	font-size: var(--step-body-sm);
	font-weight: var(--weight-medium);
	line-height: 1.5;
	transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.office-filter__btn:hover {
	border-color: var(--color-border);
}

.office-filter__btn.is-active {
	background-color: var(--color-bg);
	border-color: var(--color-border);
}

/* --- Office card ----------------------------------------------------------- */

/*
 * 631 wide, 40px padding, 12px radius. Label 14 / address 24 / 16px copy, then a row
 * of three contact pills 30px below and a full-width Get Direction 30px under those.
 */
.office-grid {
	gap: 1.25rem;
}

.office-card {
	display: flex;
	flex-direction: column;
	padding: calc(2.5rem - 1px);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-bg);
}

.office-card__label {
	margin: 0 0 0.375rem;
	font-size: var(--step-body-sm);
	line-height: var(--leading-compact);
}

.office-card__address {
	margin: 0 0 var(--space-xs);
	font-size: clamp(1.25rem, 1.157rem + 0.38vw, 1.5rem);   /* 20 → 24 */
	line-height: 1.5;
}

.office-card__text {
	margin: 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

.office-card__meta {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin: 1.875rem 0 0;
	padding: 0;
	list-style: none;
}

.office-card__pill {
	gap: 0.5rem;
	block-size: 2.5625rem;   /* 41px */
	padding-inline: calc(1.25rem - 1px);
	border: 1px solid var(--color-border);
	background-color: var(--color-surface);
}

.office-card__pill .icon {
	color: var(--color-text);
}

.office-card__cta {
	inline-size: 100%;
	margin-block-start: 1.875rem;
}

.office-empty {
	color: var(--color-text-muted);
	text-align: center;
	padding-block: var(--space-l);
}

/* --- Gallery panel --------------------------------------------------------- */

/*
 * 1280 x 775 framed panel, surface fill with the wavy line-art, 60px padding, two
 * 570px columns 20px apart with 20px between the photos; the heading block sits at
 * the foot of the left column under its two photos. Photo heights are the comp's
 * (192 / 191 / 220) and scale with the column on narrower screens.
 */
.contact-gallery {
	position: relative;
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
	padding: var(--space-m);
	border: 1px solid var(--color-border);
	border-radius: 0.75rem;
	background-color: var(--color-surface);
	isolation: isolate;
	overflow: hidden;
}

.contact-gallery::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background-image: url("../images/wavy.png");
	background-size: cover;
	background-position: center;
	mix-blend-mode: screen;
	opacity: 0.35;
	pointer-events: none;
}

.contact-gallery__col {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	min-inline-size: 0;
}

.contact-gallery__pair {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 1.25rem;
}

.contact-gallery__img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	aspect-ratio: 570 / 192;
	object-fit: cover;
	border-radius: 0.625rem;
}

.contact-gallery__img--meeting-table,
.contact-gallery__img--team-a,
.contact-gallery__img--team-b {
	aspect-ratio: 570 / 191;
}

.contact-gallery__img--team-a,
.contact-gallery__img--team-b {
	aspect-ratio: 275 / 191;
}

.contact-gallery__img--handshake {
	aspect-ratio: 570 / 220;
}

.contact-gallery__copy {
	margin-block-start: auto;
	padding-block-start: var(--space-m);
}

.contact-gallery__heading {
	margin: 0;
	font-size: clamp(1.75rem, 1.471rem + 1.143vw, 2.5rem);   /* 28 → 40 */
	line-height: 1.5;
}

.contact-gallery__lead {
	margin: var(--space-2xs) 0 0;
	color: var(--color-text-muted);
	font-size: var(--step-body-fluid);
	line-height: var(--leading-compact);
}

@media (min-width: 64rem) {
	.contact-gallery {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		padding: calc(3.75rem - 1px);
	}
}

/* ==========================================================================
   29. Components — single post
   ========================================================================== */

.post-single__title {
	margin: 0;
	max-inline-size: 30ch;
}

.post-single__meta {
	display: flex;
	align-items: center;
	gap: var(--space-2xs);
	margin-block-start: var(--space-s);
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);
}

.post-single__image {
	inline-size: 100%;
	block-size: auto;
	max-block-size: 32rem;
	object-fit: cover;
	border-radius: var(--radius-card);
}

.post-single__content {
	max-inline-size: 70ch;
	color: var(--color-text-muted);
	line-height: var(--leading-body);
}

.post-single__content > * + * {
	margin-block-start: var(--space-m);
}

.post-single__content h2,
.post-single__content h3,
.post-single__content h4 {
	color: var(--color-text);
	margin-block-start: var(--space-xl);
}

.post-single__content a {
	color: var(--color-accent-tint);
	text-decoration: underline;
}

.post-single__content img {
	border-radius: var(--radius-card);
}

.post-single__tags {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2xs);
	margin-block-start: var(--space-xl);
}

.post-single__tags a {
	display: inline-flex;
	padding: var(--space-3xs) var(--space-s);
	border-radius: var(--radius-pill);
	background-color: var(--color-surface);
	border: 1px solid var(--color-border);
	color: var(--color-text-muted);
	font-size: var(--step-body-sm);
	text-decoration: none;
}

/* ==========================================================================
   30. Components — 404
   ========================================================================== */

.not-found {
	padding-block: var(--space-3xl);
}

.not-found__inner {
	max-inline-size: 40rem;
	margin-inline: auto;
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-l);
}

.not-found__code {
	font-size: clamp(4rem, 2rem + 8vw, 8rem);
	font-weight: var(--weight-bold);
	color: var(--color-surface-raised);
	line-height: 1;
}

.not-found__heading {
	margin: 0;
}

.not-found__lead {
	margin: 0;
	color: var(--color-text-muted);
}

.not-found__search {
	inline-size: 100%;
	margin-block: 0;
}

.not-found__actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--space-s);
}
