/**
 * PropOffice Designs — Property Card.
 *
 * ONE stylesheet for every surface: the native PropOffice screen, the
 * [propoffice_property_cards] shortcode, and the Elementor widget. Hand-authored,
 * no build step, enqueued with filemtime cache-busting (see PropOffice\Designs\Assets).
 *
 * PALETTE. Every colour reads a PropOffice design token with the token's own
 * value as the fallback: `var(--po-ds-color-text)`. Inside the workspace
 * (.powk / #propoffice-app / [data-po-root]) the card inherits the live design
 * system; on a public theme page — where those tokens are not defined — it
 * renders in the identical palette. One rule, both contexts, no drift.
 *
 * ISOLATION. Every selector is namespaced under `.po-pc`, so the card can never
 * restyle a theme, and the card resets the handful of properties themes most
 * often impose on images, lists, headings and buttons.
 */

/* ─────────────────────────────────────────────────────────── collection grid */

.po-pc-grid {
	--po-pc-gap: 24px;

	/* Declared here, not only consumed: the renderer supplies a column count as
	   an inline style on this element (which wins over this declaration), and a
	   custom property that is only ever set from PHP reads as an undeclared
	   reference to anything auditing the stylesheet — including the design
	   language's own usage check. The value is the design's default density. */
	--po-pc-columns: 3;

	/* Cards per row below desktop, when a placement authors them (the Property
	   Search widget's tablet/mobile column controls write these on the grid).
	   Declared `initial` — the guaranteed-invalid value — so an unauthored grid
	   still takes each breakpoint rule's own fallback below. */
	--po-pc-columns-tablet: initial;
	--po-pc-columns-mobile: initial;

	/* Intrinsic layout, deliberately NOT viewport media queries.
	   A track is at least a third of the CONTAINER (so a collection never
	   exceeds three across, the density the design is drawn for) and at least
	   260px (so it drops to two, then one, as the container narrows). The result
	   is 3 / 2 / 1 at desktop / tablet / mobile — and, unlike a viewport rule, it
	   is still right inside a one-third Elementor column, where a viewport-based
	   "desktop means three" would squeeze three cards into 300px. */
	display: grid;
	grid-template-columns: repeat(
		auto-fill,
		minmax(max(260px, (100% - (2 * var(--po-pc-gap))) / 3), 1fr)
	);
	gap: var(--po-pc-gap);
	align-items: stretch;
}

/* Placement asked for an exact column count (an Elementor / shortcode control).
   It still collapses on narrow viewports — a 4-up row on a phone is not a
   layout choice, it is a broken one. */
.po-pc-grid--fixed {
	grid-template-columns: repeat(var(--po-pc-columns, 3), minmax(0, 1fr));
}

.po-pc-empty {
	padding: 24px;
	border: 1px dashed var(--po-ds-color-border);
	border-radius: 8px;
	color: var(--po-ds-color-muted);
	font-size: 14px;
	text-align: center;
}

/* ─────────────────────────────────────────────────────────── collection list */

/* One card per row, composed sideways. The LIST is an arrangement, not a second
   card: every rule below addresses the collection or overrides a card's own
   axis, and none of them changes what a card says. That is why a card renderer
   needs no idea a list exists. */
.po-pc-list {
	--po-pc-gap: 20px;

	/* The share of the row the photograph takes on a wide screen. Declared as a
	   custom property so a placement can retune the balance without a new rule,
	   and consumed only inside the media query that composes sideways. */
	--po-pc-list-media: 38%;

	display: flex;
	flex-direction: column;
	gap: var(--po-pc-gap);
}

/* Composed sideways only where there is genuinely width for it. Below this the
   card keeps the column it was drawn as — a photograph squeezed to a third of a
   phone is not a list item, it is a thumbnail, and the card was never designed
   to be read that way. This is a deliberate collapse, not an accident of
   wrapping. */
@media (min-width: 768px) {
	.po-pc-list .po-pc {
		flex-direction: row;
		align-items: stretch;
	}

	/* The photograph takes a share of the row and stops. `min-width: 0` because a
	   flex item's automatic minimum is its CONTENT, and an image track without it
	   refuses to shrink and pushes the row wider than the page. */
	.po-pc-list .po-pc > .po-pc__media {
		flex: 0 0 var(--po-pc-list-media);
		min-width: 0;
	}

	/* The body takes the rest. A card with no photograph still has a body, so it
	   simply takes the whole row rather than leaving a gap where a picture would
	   have been. */
	.po-pc-list .po-pc > .po-pc__body {
		flex: 1 1 auto;
		min-width: 0;
	}
}

/* The held-back tail, exactly as the grid holds it: out of flow, still ordered,
   still findable by the script through `data-po-pc-reserve`. */
.po-pc-list__reserve,
.po-pc-list__reserve[hidden] {
	display: none;
}

/* ───────────────────────────────────────────────────────── collection slider */

/* The carousel's own chrome.
   The shared slider stylesheet declares every nav, dot and counter token it
   draws with, but four of those declarations point at tokens it does NOT
   declare — those belong to whichever component is drawing it. Featured Card
   supplies them from its base stylesheet, and a Property Card page must not
   load that, because it would carry a different card's entire styling along
   with the four values wanted from it.
   So the Property Card declares its own four, scoped to its own wrapper. The
   values are the design system's with Featured Card's current numbers as the
   fallback, which is how the rest of this stylesheet states a colour: a card on
   a plain theme page keeps exactly the appearance it has today, and one inside
   the PropOffice workspace takes the live palette instead. */
.po-pc-slider {
	--ppui-cta-bg: var(--po-ds-color-primary, #1d4ed8);
	--ppui-cta-text: var(--po-ds-color-on-primary, #ffffff);
	--ppui-border: var(--po-ds-color-border, #e5e7eb);
	--ppui-text-inverse: var(--po-ds-color-on-primary, #ffffff);

	/* The dot row's inputs, written by the Style tab (the dot dimensions) and by
	   the markup (the dot count). Declared `initial` - the guaranteed-invalid
	   value - so each var() below still takes its own fallback until something
	   authors the value, and so the design-language gate can see them declared.
	   The markup's inline --po-pc-dots and an Elementor rule on the slider root
	   both outrank this. */
	--po-pc-dots: initial;
	--po-pc-dot-w: initial;
	--po-pc-dot-active-w: initial;
	--po-pc-dot-h: initial;
	--po-pc-dot-active-h: initial;
}

/* Where the two control rows sit when both are on screen.
   The shared stylesheet anchors the arrows and the dots to the same point
   under the cards, so with both enabled they were drawn on top of each other.
   The dots keep that slot — it is the one an authored widget already has — and
   the arrow row is stacked beneath them by the height of the tallest dot plus a
   12px gap. Only the arrows' vertical OFFSET moves, and it is a default like
   any other: choosing a vertical position in the Style tab writes
   --ppui-nav-dy beside the anchor, and an explicit offset after it, both of
   which replace this value outright rather than adding to it. */
.po-pc-slider[data-po-stacked-controls] .propoffice-pui-slider {
	--ppui-nav-dy: calc(12px + max(var(--po-pc-dot-h, 10px), var(--po-pc-dot-active-h, 10px)) + 12px);
}

/* Fitting the dot row to the slider.
   The row is absolutely positioned and as wide as its dots, gaps and the
   side margins Swiper gives every bullet, so many slides, large dots and a
   generous gap made it wider than the slider on a phone - and, being out of
   flow, widened the whole page. It is capped at the slider's width, and the
   spacing gives way before the dots do.

   --po-pc-slack is the room each boundary between two dots has to spare once
   the dots themselves are laid out: the slider's width, less the dots, over the
   boundaries. The authored gap may take up to half of it and each bullet's
   side margin up to a quarter, so gap + both margins never exceed it. On a wide
   slider the slack is far larger than any authored value, every min() returns
   the authored value, and nothing changes. Only when the slack runs out do the
   dots themselves shrink, down to 3px. Nothing wraps, so the row stays one line
   tall and the stacked arrows never meet it. Nothing the editor configured is
   rewritten; this only decides how much of it fits.

   The rules are layered for older engines; see the note above the second
   half. */
.po-pc-slider[data-po-dots] .propoffice-pui-slider .swiper-pagination-bullet {
	flex: 0 1 auto;
	min-width: 3px;
}

/* An explicit max-content width, because an absolutely positioned row anchored
   at left: 50% is only offered the half of the slider to the right of its
   anchor, and would shrink its dots to fit THAT. The cap is the whole slider. */
.po-pc-slider[data-po-dots] .propoffice-pui-slider .propoffice-pui-slider__pagination.swiper-pagination {
	max-width: 100%;
	width: max-content;
}

/* Every engine gets the fit; the slider's own width is the better yardstick
   where it is available. The slack below is first estimated from the VIEWPORT
   (less a 32px allowance for the page's side padding), which is the widest the
   slider can plausibly be and works in every engine that understands min() and
   max() - Chrome 79, Safari 11.1, Firefox 75. Where container query units exist
   (Chrome 105, Safari 16, Firefox 110) the slider becomes a size container and
   the estimate is replaced by its true width, so a slider narrower than the
   viewport - a column, a sidebar - is fitted exactly. An engine that
   understands neither keeps the authored spacing and the 100% cap. */
.po-pc-slider[data-po-dots] .propoffice-pui-slider .propoffice-pui-slider__pagination.swiper-pagination {
	--po-pc-slack: max(
		0px,
		calc(
			(100vw - 32px - (var(--po-pc-dots) - 1) * var(--po-pc-dot-w, var(--ppui-dot-size)) - var(--po-pc-dot-active-w, var(--ppui-dot-active-size)))
			/ (var(--po-pc-dots) - 1)
		)
	);
	column-gap: min(var(--ppui-dot-gap), calc(var(--po-pc-slack) / 2));
}

.po-pc-slider[data-po-dots] .propoffice-pui-slider .swiper-pagination-bullet {
	margin-inline: min(var(--swiper-pagination-bullet-horizontal-gap, 4px), calc(var(--po-pc-slack) / 4));
}

@supports (width: 1cqw) {
	.po-pc-slider[data-po-dots] .propoffice-pui-slider {
		container-type: inline-size;
	}

	.po-pc-slider[data-po-dots] .propoffice-pui-slider .propoffice-pui-slider__pagination.swiper-pagination {
		--po-pc-slack: max(
			0px,
			calc(
				(100cqw - (var(--po-pc-dots) - 1) * var(--po-pc-dot-w, var(--ppui-dot-size)) - var(--po-pc-dot-active-w, var(--ppui-dot-active-size)))
				/ (var(--po-pc-dots) - 1)
			)
		);
	}
}

/* ──────────────────────────────────────────────────────────────────── card */

.po-pc {
	position: relative;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	background: var(--po-ds-color-surface);
	/* Minimal corner treatment and a hairline border — a listing card, not a
	   heavily rounded dashboard tile. The shadow is barely there; the border
	   does the separating. */
	border: 1px solid #e7e9ee;
	border-radius: 3px;
	box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
	color: var(--po-ds-color-text);
	font-family: var(--po-ds-font-family);
	transition: box-shadow 160ms ease;
}

.po-pc:hover {
	box-shadow: 0 2px 10px rgba(15, 23, 42, 0.09);
}

/* Focus lands on the title link, but the ring belongs on the card — the link is
   stretched over the whole card, so ringing the text alone would point at the
   wrong target. */
/* The card ring answers to the KEYBOARD only.

   `:focus-within` fires for any descendant that has focus, and a pointer click
   focuses whatever it lands on — so clicking an arrow, the heart or the title
   painted a blue perimeter around the whole card and left it there until
   something else took focus. It read as a selected card, which is not a state
   this design has.

   `:has(:focus-visible)` asks the narrower question: does a descendant have
   focus that the browser judges worth indicating? That is Tab, and not a
   pointer click on a button or a link. The keyboard ring survives exactly as
   it was; the pointer one never appears.

   Note this cannot be fixed from the focused child: a `box-shadow: none` on
   the descendant does not reach the card that draws the ring. */
.po-pc:has(:focus-visible) {
	box-shadow: 0 0 0 2px var(--po-ds-color-primary);
}

.po-pc__icon,
.po-pc svg {
	flex: none;
}

/* ─────────────────────────────────────────────────────────────────── media */

/* The media ranks ABOVE the stretched title link, and that is deliberate.

   The link's `::after` is `inset: 0` against the card, so it covers the
   photograph as well as the body — and it belongs to the title, not to the
   frame. Two things followed from that. Arrow clicks landed on the overlay and
   opened the property (the media used to sit at z-index 1, beneath it). And
   pointing at the photograph hovered the overlay rather than the frame, so
   `.po-pc__frame:hover` never fired and the zoom, though correctly written,
   was never seen.

   Lifting the media over the overlay answers both: the buttons take their own
   clicks and the frame takes its own hover. The cost is that clicking the
   photograph no longer opens the property — the title, the price and the rest
   of the body still do, which is where the stretched link earns its keep. */
.po-pc__media {
	position: relative;
	z-index: 3;
}

.po-pc__frame {
	/* Declared here, not only consumed (same reason as --po-pc-columns above):
	   the media scrim is an opt-in tint a style control sets on the frame, and a
	   custom property only ever set from PHP reads as an undeclared reference to
	   anything auditing the stylesheet. The default is no tint, which is exactly
	   what the ::after fallback already resolves to. */
	--po-pc-media-overlay: transparent;
	position: relative;
	width: 100%;
	/* The reference's own media proportion: 395 x 309 at the reference card
	   width, i.e. ~1.28:1. A ratio rather than a fixed height so the band scales
	   with the card — a three-up grid and a full-width card keep the same shape.
	   object-fit: cover absorbs whatever aspect the photograph actually has. */
	aspect-ratio: 395 / 309;
	overflow: hidden;
}

/* No-media cards keep the frame so a mixed grid stays aligned, but nothing
   pretends a photo exists. */
.po-pc__frame--empty {
	background:
		linear-gradient(135deg, var(--po-ds-color-background) 0%, var(--po-ds-color-background) 100%);
}

/* The image scrim — a layer the frame always draws and, by default, never shows.

   It exists so an Elementor "Image Overlay" colour has something real to land on
   rather than the control being a slider that moves nothing. Transparent until a
   colour is set, so the approved design is untouched: this rule paints nothing on
   a card nobody has styled.

   Two properties make it safe. `pointer-events: none` keeps the frame's hover
   zoom, the gallery arrows and the card's stretched link behaving exactly as they
   did — the scrim is never the thing a pointer lands on. And `z-index: 1` places
   it above the photograph but below every control over the picture, all of which
   sit at 3: the badge, the heart, the arrows, the dots and the counter stay on
   top of a tint, which is the whole reason to have one. */
.po-pc__frame::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 1;
	background: var(--po-pc-media-overlay, transparent);
	pointer-events: none;
}

/* ─────────────────────────────────────────────────────────── the slide track

   ONE track per photograph, and that separation is the whole design.

   A single element cannot hold two independent transforms. Writing the gallery
   slide and the hover zoom onto the same box means one `transform:
   translateX(-100%) scale(1.08)`, and whichever is written last wins — hover
   the photograph mid-slide and it snaps back to the centre; leave the slide in
   place and the zoom stops. So the TRACK carries translateX() and the
   PHOTOGRAPH inside it carries scale(), and neither can overwrite the other.

   Two layers also make the wrap honest. The outgoing photograph travels one way
   while the incoming travels the other, at the same time — which one long strip
   of images cannot do without dragging every image between them through the
   frame, so 3 → 1 would flash 2 on the way past. Here only the two photographs
   actually involved are ever displayed, and every step is one step.

   The track is a plain full-frame layer, so it changes no geometry: the frame
   still states the aspect ratio, still clips, and the photograph inside still
   fills it. Nothing here can move the media band or the card. */
.po-pc__frame .po-pc__track {
	position: absolute;
	inset: 0;
	display: none;
	width: 100%;
	height: 100%;
	max-width: none;
	max-height: none;
	min-height: 0;
	margin: 0;
	padding: 0;
	/* Stated rather than left at `none`, so the transition always has a
	   definite value to travel from — including the very first arrow click. */
	transform: translateX(0%);
	transition: transform 300ms ease;
}

/* The showing photograph, plus — for the length of one transition only — the
   one leaving. Three classes, so it wins over the geometry rule above whatever
   the source order, and so no theme or Elementor `display` rule can empty the
   frame. */
.po-pc__frame .po-pc__track.is-active,
.po-pc__frame .po-pc__track.is-leaving {
	display: block;
}

/* The frame owns the dimensions; the photograph fills them and is cropped to
   fit. object-position is stated rather than left to its 50% 50% initial value
   because it is part of the contract every design inherits — a portrait, a
   landscape and a square upload all produce the same card height, cropped from
   the middle. */
/* Two classes, and that is the whole point of the selector.

   Elementor ships `.elementor img { height: auto; max-width: 100% }`. That is
   one class plus an element — specificity (0,1,1) — and a lone `.po-pc__image`
   is (0,1,0), so inside an Elementor column the card LOST: height fell back to
   auto and each photograph's own proportions decided how tall the media stood.
   Paging the gallery then resized the card, because image two was not shaped
   like image one. Qualifying by the frame makes this (0,2,0) and the frame owns
   the geometry again, everywhere it is rendered.

   `max-width: none` matters for the same reason: the theme cap would otherwise
   shrink a photograph that is deliberately wider than its frame. */
.po-pc__frame .po-pc__image {
	position: absolute;
	inset: 0;
	/* WHICH photograph is showing is the track's business now, not the
	   image's — a `display: none` here would hide the photograph inside a
	   track that is mid-slide. Every image is drawn; only two tracks are ever
	   displayed at once. */
	display: block;
	width: 100%;
	height: 100%;
	max-width: none;
	max-height: none;
	min-height: 0;
	object-fit: cover;
	object-position: center center;
	border-radius: 0;
	margin: 0;
	transition: transform 320ms ease;
}

/* Image hover zoom — a property of every Property Card, not a per-design
   option, so it is declared once here and any design that draws a
   `.po-pc__image` inside a `.po-pc__frame` inherits it.

   Only the photograph moves. The frame already clips (`overflow: hidden`) and
   the transform is confined to an absolutely positioned child, so nothing
   reflows: the card does not grow, and the badge, gallery buttons and overlay
   sit outside the image and stay exactly where they were. */
.po-pc__frame:hover .po-pc__image,
.po-pc__frame:focus-within .po-pc__image {
	transform: scale(1.08);
}

/* ─────────────────────────────────────────────────────────────────── badge */

/* Flush to the image's top-left corner, rectangular, no rounding — the
   reference badge is a solid navy tab sitting ON the photograph. */
.po-pc__badge {
	position: absolute;
	top: 0;
	left: 0;
	z-index: 3;
	padding: 13px 26px;
	/* ONE fixed colour for every status. The badge's WORD changes with the
	   canonical status; its colour never does. */
	background: #0f284b;
	border-radius: 0;
	color: #ffffff;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.2;
	letter-spacing: 0.005em;
}

/* ───────────────────────────────────────────────────────── gallery controls */

/* A translucent VERTICAL PANEL, not a circular carousel button: a tall dark
   rectangle holding a large white chevron, always visible (the reference shows
   both arrows at rest), vertically centred over the image. */
.po-pc__nav {
	position: absolute;
	top: 50%;
	z-index: 3;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 92px;
	margin: 0;
	padding: 0;
	transform: translateY(-50%);
	background: rgba(15, 23, 42, 0.28);
	border: 0;
	border-radius: 2px;
	box-shadow: none;
	color: #ffffff;
	cursor: pointer;
	opacity: 1;
	transition: background-color 160ms ease;
}

/* Elementor-proofing, and the reason it is qualified by `.po-pc`.

   A gallery arrow is a `<button>`, and Elementor styles buttons generously —
   `.elementor button`, `.elementor-widget-container button` and the kit's
   global typography reach it with more weight than a lone `.po-pc__nav`, which
   is (0,1,0). That is why the arrows rendered wrong inside Elementor and right
   everywhere else: a background, a radius, padding, a font and a line-height
   arrived from somewhere the card never asked. Two classes wins, and every
   property Elementor might set is stated rather than left to chance.

   These are UI controls, not content buttons; nothing here is a design change,
   only a restatement of the approved appearance in terms Elementor cannot
   quietly override. */
.po-pc .po-pc__nav {
	box-sizing: border-box;
	width: 46px;
	height: 92px;
	min-width: 0;
	min-height: 0;
	max-width: none;
	padding: 0;
	margin: 0;
	border: 0;
	border-radius: 2px;
	background: rgba(15, 23, 42, 0.28);
	box-shadow: none;
	color: #ffffff;
	font: inherit;
	line-height: 1;
	letter-spacing: normal;
	text-transform: none;
	text-decoration: none;
	text-shadow: none;
	-webkit-appearance: none;
	appearance: none;
}

/* The artwork is the supplied artwork and Elementor does not get to resize or
   repaint it. `.elementor svg` and `svg:not(:root)` both reach an inline icon;
   these are stated at two classes so the geometry survives. Paint stays
   `currentColor`, which is how the supplied files were integrated. */
.po-pc .po-pc__nav svg {
	display: block;
	width: 100%;
	height: 100%;
	max-width: none;
	max-height: none;
	fill: none;
	stroke: currentColor;
	vertical-align: middle;
}

/* The heart, held against Elementor for the same reason the arrows are.

   The favourite is a `<button>`, and its base rule was a lone
   `.po-pc__favourite` at (0,1,0) — `.elementor button` is (0,1,1) and won, so
   inside Elementor the heart arrived sitting on a filled blue rectangle. The
   approved control is the outline heart on the white card body and nothing
   else, so every state Elementor paints is stated here at two classes.

   No appearance change: this is the design the card already asked for, said
   loudly enough to survive. */
.po-pc .po-pc__favourite,
.po-pc .po-pc__favourite:hover,
.po-pc .po-pc__favourite:focus,
.po-pc .po-pc__favourite:focus-visible,
.po-pc .po-pc__favourite:active {
	padding: 0;
	border: 0;
	border-radius: 0;
	background: none;
	background-color: transparent;
	background-image: none;
	box-shadow: none;
	text-decoration: none;
	-webkit-appearance: none;
	appearance: none;
}

.po-pc__nav--prev {
	left: 12px;
}

.po-pc__nav--next {
	right: 12px;
}

.po-pc__nav:hover,
.po-pc__nav:focus-visible {
	background: rgba(15, 23, 42, 0.48);
}

/* The supplied chevron artwork is drawn on a 16x32 grid and carries its own
   white stroke; these dimensions keep that 1:2 proportion at the asset's own
   intrinsic size. No stroke or colour here — CSS would override the artwork's
   presentation attributes, which is exactly what must not happen. */
.po-pc__nav-icon {
	width: 24px;
	height: 48px;
}

/* ──────────────────────────────────────────────────────────────── overlays */

/* ONE grouped panel — location marker, hairline separator, camera + count —
   not a row of separate chips. */
.po-pc__overlay {
	position: absolute;
	right: 12px;
	bottom: 12px;
	z-index: 3;
	display: inline-flex;
	align-items: stretch;
	height: 36px;
	background: rgba(15, 23, 42, 0.6);
	border-radius: 3px;
	color: #ffffff;
	font-size: 15px;
	font-weight: 600;
	line-height: 1;
}

.po-pc__overlay-pin,
.po-pc__overlay-count {
	display: inline-flex;
	gap: 8px;
	align-items: center;
	padding: 0 12px;
}

/* The divider inside the group, inset from the panel's top and bottom edges. */
.po-pc__overlay-sep {
	width: 1px;
	margin: 8px 0;
	background: rgba(255, 255, 255, 0.32);
}

.po-pc__overlay-icon {
	width: 19px;
	height: 19px;
}

/* ──────────────────────────────────────────────────────────────────── body */

/* One continuous white information area: price, description, specifications.
   No strip, no divider, no inner container. */
.po-pc__body {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	padding: 30px 22px 32px;
}

.po-pc__headline {
	display: flex;
	gap: 12px;
	align-items: center;
	justify-content: space-between;
}

.po-pc__price {
	margin: 0;
	color: #16233c;
	font-size: 27px;
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -0.015em;
}

/* A bare outline heart in blue-grey — no background, no border, no button
   chrome of any kind. Themes and the workspace shell both style <button>
   aggressively, so every inherited surface is reset explicitly. */
.po-pc__favourite {
	/* Held at the end of the headline even when it is the only thing in it.
	   `justify-content: space-between` sends a LONE child to the START, so a
	   property with no price - "price on application" is a real listing state,
	   not an error - put the heart at the far left of the card while every
	   priced card kept it at the right. With both present this changes nothing,
	   because the price already fills the space between them.

	   Modern positions its heart absolutely in the media corner and restates
	   `margin: 0` at higher specificity, so it is unaffected. */
	position: relative;
	/* Above the stretched title link, so the heart is clickable even though the
	   whole card is a link target. */
	z-index: 3;
	display: flex;
	flex: none;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	/* `0 0 0 auto`, not `0`: the left auto holds the heart at the END of the
	   headline even when it is the only child. `justify-content: space-between`
	   sends a lone child to the START, so a property with no price - "price on
	   application" is a real listing state - put the heart at the far left
	   while every priced card kept it at the right. With a price present this
	   changes nothing, because the price already fills the space between them.

	   It must live in the SHORTHAND: a `margin-left` declared above this line
	   is overwritten by it, which is exactly how the first attempt failed. */
	margin: 0 0 0 auto;
	padding: 0;
	background: none;
	background-color: transparent;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	color: #9aa8bd;
	cursor: pointer;
	transition: color 160ms ease;
}

.po-pc__favourite:hover,
.po-pc__favourite:focus,
.po-pc__favourite:active {
	background: none;
	background-color: transparent;
	box-shadow: none;
	color: #64748b;
}

.po-pc__favourite.is-active {
	color: var(--po-bad, #ef4444);
}

/* Active is the one state that deliberately overrides the supplied artwork's
   paint — a saved property has to read as saved. At rest the heart keeps the
   colours it was delivered with.

   The SHAPES are targeted, not the <svg>: the supplied heart carries its stroke
   as a presentation attribute on the path, and an attribute beats a value
   inherited from the parent. Only a rule matching the path itself wins. */
.po-pc__favourite.is-active .po-pc__favourite-icon > * {
	fill: currentColor;
	stroke: currentColor;
}

.po-pc__favourite-icon {
	width: 27px;
	height: 27px;
}

/* The listing description. The canonical title already reads as a sentence and
   carries the location ("… For Sale in Del Judor Ext 4"), so this is the only
   text block between price and specifications — there is no separate location
   line. */
/*
 * 🚨 SCOPED UNDER THE COMPONENT ROOT, TO SURVIVE A GLOBAL HEADING RULE.
 *
 * This is drawn on a real heading element, and both Elementor's global font kit
 * and most themes style headings by TAG: `.elementor-kit-10 h3` is (0,1,1) and
 * outranks a lone `.po-pc__title` at (0,1,0).
 *
 * MEASURED on the live Kava install at 1440x720, that kit rule had taken over
 * 8 of the 11 PropOffice headings on a property page. The Features category
 * label is the clearest case: declared 12px/600 uppercase with 0.06em tracking,
 * it drew at 24px/400 with tracking `normal` — LARGER than the 22px section
 * heading it sits under, so the hierarchy read backwards and the label had
 * stopped looking like a label at all.
 *
 * One extra class is the whole fix, and it is not a guess: on that same page
 * `.po-finance .po-finance__heading` and `.po-overview .po-overview__title`
 * already carried it and rendered exactly what they declare. Both at (0,2,0),
 * both winning. That is why Finance looked right and this did not — never a
 * difference of design, only of specificity.
 */
.po-pc .po-pc__title {
	margin: 24px 0 0;
	color: #2b3a52;
	font-size: 17px;
	font-weight: 400;
	line-height: 1.5;
}

.po-pc__link {
	color: inherit;
	text-decoration: none;
	box-shadow: none;
}

/* The whole-card click target. One real link, stretched — see
   ClassicPropertyCardRenderer for why the card is not wrapped in an anchor. */
.po-pc__link::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 2;
}

.po-pc__link:hover {
	color: var(--po-ds-color-primary);
}

/* The blue ring after a click.

   A pointer click focuses the element it lands on, and the browser's default
   focus ring — plus Elementor's own `:focus` treatment on buttons and links —
   then paints a blue outline that stays until something else takes focus. It
   reads as a selected card, which is not a state this design has.

   `:focus:not(:focus-visible)` is the whole fix: it withdraws the ring for the
   pointer only. `:focus-visible` is exactly the case the browser judges to
   need an indicator — Tab, and pointer focus on a text field — so keyboard
   users are untouched. Nothing here is `outline: none` unconditionally. */
.po-pc :focus:not(:focus-visible) {
	outline: none;
	box-shadow: none;
}

/* And the keyboard ring is the card's own rather than the browser's or
   Elementor's, so it reads as part of this design. */
.po-pc :focus-visible {
	outline: 2px solid var(--po-ds-color-primary);
	outline-offset: 2px;
}

/* ──────────────────────────────────────────────────────── specification row */

/* NO DIVIDER. The specifications flow directly out of the description as part
   of one continuous information area — there is deliberately no border-top,
   no rule, and no decorative separator of any kind here. */
/* EVERY specification is an equal member of one flowing row — floor area
   included. Area used to be given `flex-basis: 100%` so it took a line of its
   own; that made one statistic's layout a special case of its key, which is
   exactly what a wrapping flex row already handles. The chips sit inline while
   the card is wide enough and wrap together when it is not, so a narrow card
   loses a row rather than a statistic. */
.po-pc__specs {
	display: flex;
	flex-wrap: wrap;
	gap: 22px 20px;
	margin: 34px 0 0;
	padding: 0;
	list-style: none;
}

.po-pc__spec {
	display: inline-flex;
	gap: 8px;
	align-items: center;
	margin: 0;
	color: #3d4d66;
	font-size: 15px;
	font-weight: 400;
	line-height: 1.2;
	white-space: nowrap;
}

/* Size only. The supplied specification icons are solid #CBD5E1 and carry that
   fill themselves; a colour or stroke-width here would override the delivered
   artwork, because CSS beats SVG presentation attributes. */
.po-pc__spec-icon {
	width: 20px;
	height: 20px;
}

.po-pc__spec-value,
.po-pc__spec-label {
	color: inherit;
	font-weight: inherit;
}


/* ═══════════════════════════════════════════════════ MODERN (Template 2) ═══
 *
 * The Modern design — stored under the id `listing`, which existing Elementor
 * content still carries. Everything below is scoped to `.po-pc--listing` so it
 * cannot reach a Classic card: the two designs share a data model and a script,
 * never a presentation rule.
 */

/* `hidden` has to WIN, and only Modern needs it told.
   The attribute's own UA rule is `display: none` at the weakest specificity there
   is, so Modern giving its menu `display: flex` silently overrode it and every
   card rendered with the menu already open. Scoped to Modern because Modern is
   the design that has panels; Classic renders none and is left alone. */
.po-pc--listing [hidden],
.po-pc--listing .po-pc__menu[hidden],
.po-pc--listing .po-pc__share[hidden],
.po-pc--listing .po-pc__confirm[hidden] {
	display: none;
}

/* The spare listings Modern asked the collection to keep in the page (see
   PropertyCardReserveInterface). The script moves one INTO the grid when a
   visitor hides a listing. The attribute already does this — the rule is here
   because a theme that restyles [hidden] would otherwise drop three unasked-for
   cards onto the page. */
.po-pc-grid__reserve,
.po-pc-grid__reserve[hidden] {
	display: none;
}

/* ═══════════════════════════════════════════════════ LISTING (Template 2) ═══
 *
 * The portal-style feed card: a full-bleed photo carrying every control, then an
 * unruled body of price, key details, address and credit.
 *
 * PALETTE. The reference pins five colours (#111827 ink, #6B7280 secondary,
 * #9CA3AF icon, #FFFFFF surface, #2563EB accent). They are declared here the way
 * the whole stylesheet declares colour — as a design-system token with the
 * REFERENCE value as its fallback — so a card on a public theme page renders in
 * exactly the specified palette, while a card inside the PropOffice workspace
 * takes the live design system and does not sit in the shell wearing a second,
 * nearly-identical set of greys.
 */

.po-pc--listing {
	--po-pc-ink: var(--po-ds-color-text);
	--po-pc-secondary: var(--po-ds-color-muted);
	--po-pc-icon: var(--po-ds-color-muted);
	--po-pc-surface: var(--po-ds-color-surface);
	--po-pc-accent: var(--po-ds-color-primary);
	--po-pc-hover: var(--po-ds-color-background);
	--po-pc-radius: 12px;

	/* The three-dot control's own blue, declared here and not only consumed:
	   it is the colour that came WITH the supplied artwork rather than one this
	   design chose, so it is a value a site may want to retint without the icon
	   being redrawn — and a custom property that is only ever referenced reads
	   as an undeclared reference to anything auditing the stylesheet, the design
	   language's orphan check included. */
	--po-pc-more: #0041d9;

	/* The status dot's colour. Declared HERE, in Modern's scope, because the
	   restored Classic pins its badge to a fixed colour and no longer declares
	   this — leaving Modern's dot referencing a custom property nothing defines,
	   which is an orphan to the design language's usage check. The renderer
	   inlines the canonical status colour onto the dot (winning over this); this
	   is the fallback for a vocabulary that names no colour. */
	--po-pc-badge: var(--po-ds-color-text);

	/* WHY THE CARD IS ITS OWN CONTAINER.
	   The reference is drawn at 660px, and its proportions — a price a twentieth
	   of the card wide, an address two-thirds of that — are the design. A card
	   dropped into a three-across grid is ~380px and into a narrow Elementor
	   column narrower still, so fixed type sizes would reproduce the reference at
	   exactly one width and misreport it everywhere else. Sizing from the
	   CONTAINER reproduces the reference at the reference's width and scales the
	   same drawing down, rather than switching to a different one.

	   Every such rule states a plain px value first: `cqi` is ignored wholesale by
	   a browser that does not know it, and the fallback is what that browser gets. */
	container-type: inline-size;

	/* The menu, the share sheet and the confirmation are raised ABOVE the card,
	   so this design cannot clip its own overflow the way Classic does. The photo
	   keeps its rounded corners by clipping the frame instead. */
	overflow: visible;
	background: var(--po-pc-surface);
	border: 0;
	border-radius: var(--po-pc-radius);
	box-shadow: 0 1px 2px rgba(17, 23, 39, 0.06), 0 2px 10px rgba(17, 23, 39, 0.07);
	color: var(--po-pc-ink);
}

.po-pc--listing:hover {
	box-shadow: 0 2px 4px rgba(17, 23, 39, 0.08), 0 10px 24px rgba(17, 23, 39, 0.12);
}

.po-pc--listing:focus-within {
	box-shadow: 0 0 0 2px var(--po-pc-accent), 0 10px 24px rgba(17, 23, 39, 0.12);
}

/* ------------------------------------------------------------------- media */

/* The reference's own ratio, measured from it: the photo is exactly twice as
   wide as it is tall, at every size, on every breakpoint. */
.po-pc--listing .po-pc__frame {
	aspect-ratio: 2 / 1;
	border-radius: var(--po-pc-radius) var(--po-pc-radius) 0 0;
}

/* The photo fills the media area and crops rather than distorts, holding the
   reference's composition as the card narrows. */
.po-pc--listing .po-pc__image {
	object-fit: cover;
	object-position: center center;
}

/* WHICH photograph is showing — Modern's half of a contract Classic already has.

   The two designs mark the same state on different elements. Classic wraps each
   photograph in a `.po-pc__track` and the track carries `data-po-pc-slide`,
   `is-active` and `is-leaving`; Modern has no track, so the `<img>` carries them
   itself. The script does not care which — it moves whatever the slide element
   is — and that is why the index, the dot rail and the counter were always
   right here.

   What was missing was the other half: nothing told Modern's photographs to stop
   being drawn. `.po-pc__frame .po-pc__image` sets `display: block` for every
   image, which is correct for Classic (the track decides, and an image hidden
   inside a travelling track would send an empty rectangle across the frame) and
   fatal for Modern, where the image IS the slide. All five were painted, stacked
   at `inset: 0`, and the LAST one in document order was the only one ever seen —
   so the gallery reported image 3 and showed image 5, and appeared to work only
   at whichever end happened to agree.

   Stated at three classes so it outranks that rule, and at four for the states,
   so the active photograph and — for the length of one transition — the one
   leaving are the only two drawn. Exactly the contract Classic's tracks have,
   which is what makes this one model rather than two. */
.po-pc--listing .po-pc__frame .po-pc__image {
	display: none;
}

.po-pc--listing .po-pc__frame .po-pc__image.is-active,
.po-pc--listing .po-pc__frame .po-pc__image.is-leaving {
	display: block;
}

/* ------------------------------------------------------------------- pill */

.po-pc--listing .po-pc__pill {
	position: absolute;
	top: 12px;
	top: clamp(10px, 3.4cqi, 26px);
	left: 12px;
	left: clamp(10px, 3.4cqi, 26px);
	z-index: 3;
	display: inline-flex;
	gap: 7px;
	align-items: center;
	max-width: calc(100% - 76px);
	padding: 7px 14px;
	padding: clamp(5px, 1.4cqi, 10px) clamp(10px, 2.8cqi, 20px);
	overflow: hidden;
	background: var(--po-pc-surface);
	border-radius: 999px;
	box-shadow: 0 1px 3px rgba(17, 23, 39, 0.22);
	color: var(--po-pc-ink);
	font-size: 14px;
	font-size: clamp(12px, 2.85cqi, 21px);
	font-weight: 500;
	line-height: 1.3;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* The canonical status colour, kept as a mark rather than a fill: this pill is
   white by design, and a vocabulary that defines no colour simply has no dot. */
.po-pc--listing .po-pc__pill-dot {
	flex: none;
	width: 8px;
	height: 8px;
	background: var(--po-pc-badge);
	border-radius: 50%;
}

/* --------------------------------------------------------------- favourite */

.po-pc--listing .po-pc__favourite {
	position: absolute;
	top: 10px;
	top: clamp(8px, 2.8cqi, 22px);
	right: 10px;
	right: clamp(8px, 2.8cqi, 22px);
	z-index: 3;
	display: flex;
	align-items: flex-start;
	justify-content: flex-end;
	box-sizing: content-box;
	width: auto;
	height: auto;
	margin: 0;
	/* The target grows DOWN and LEFT, into the photograph, and the offsets are
	   untouched - so the artwork keeps the exact corner the approved design
	   gives it and the box never leaves the card.

	   Growing outward instead (pulling the offsets back by half) was tried and
	   measured: at small card widths the offset clamps to 8px, so a centred
	   44px box hangs 2px past the card's right edge. The heart is the one
	   control that must stay inside a rounded photo corner.

	   24x22 of artwork + 18px = a 42x40 target, up from 24x22 - which was under
	   the 24px minimum, on a control an arm's length from the gallery
	   chevron. */
	/* `content-box` above is load-bearing, not decoration: Elementor resets
	   `.elementor *` to `border-box`, and under that this padding would eat
	   the artwork instead of extending the target around it - shrinking the
	   heart on exactly the surface this card is drawn on. It also keeps the
	   "Favourite button size" Style control meaning the ARTWORK's size,
	   which is what an editor adjusting it is looking at. */
	padding: 0 0 18px 18px;
	background: transparent;
	border: 0;
	box-shadow: none;
	cursor: pointer;
}

.po-pc--listing .po-pc__favourite:hover {
	background: transparent;
	transform: scale(1.06);
}

/* The supplied save artwork carries its own two colours — a translucent dark
   body that keeps the mark readable over a bright sky, and a white outline over
   it. Neither is overridden here; the unsaved state IS the artwork as given. */
.po-pc--listing .po-pc__favourite-icon {
	width: 28px;
	width: clamp(24px, 6.2cqi, 44px);
	height: auto;
	filter: drop-shadow(0 1px 2px rgba(17, 23, 39, 0.3));
}

/* Saved: the body fills solid white, so a glance at a row of cards separates
   the saved from the unsaved without reading anything. */
.po-pc--listing .po-pc__favourite.is-active .po-pc__heart-fill {
	fill: #ffffff;
	fill-opacity: 1;
}

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

/* Bare white chevrons floated directly on the photograph — no discs, no
   circular buttons — over a soft darkening of the image side, which is both the
   reference's treatment and what keeps a white chevron readable against a pale
   sky. Always visible: the reference shows them at rest, and a control that only
   appears on hover does not exist at all on a touch screen. */
.po-pc--listing .po-pc__nav {
	top: 0;
	width: 46px;
	width: clamp(38px, 9cqi, 76px);
	height: 100%;
	border-radius: 0;
	opacity: 1;
	transform: none;
	filter: drop-shadow(0 1px 3px rgba(17, 23, 39, 0.55));
	/* The strip is SCENERY. See the band below: what the visitor can press is a
	   bounded target inside it, not the whole edge of the photograph. */
	pointer-events: none;
}

/* What the arrow actually offers to a pointer.

   The scrim runs the full height of the photograph because that is how the
   design keeps a white chevron readable against a pale sky — but a full-height
   interactive edge is a different claim, and it was a false one: the save
   control sits at `right: clamp(8px, 2.8cqi, 22px)`, INSIDE the next arrow's
   strip, and the arrow is rendered after it, so every press meant for the heart
   paged the gallery instead.

   So the two are separated. The button keeps its box, its gradient and its
   chevron exactly as drawn and takes no pointer events; this band does, and it
   is inset far enough from both ends to clear the furniture the design puts in
   the corners — the save control above, the counter and the dot rail below. One
   symmetric inset rather than a value per corner, in the same `cqi` the rest of
   this design is measured in, so the target grows with the card.

   The hit is attributed to the button, so the delegated click handler, the
   `:hover` treatment and the keyboard path are all unchanged — a `<button>` is
   still focusable and still fires on Enter, because `pointer-events` is about
   pointers and nothing else.

   These numbers are load-bearing, and PropertyCardInteractionTest measures the
   two boxes at five card widths rather than trusting them. */
.po-pc--listing .po-pc__nav::before {
	content: '';
	position: absolute;
	/* Held off the ends by a fixed clearance OR by a share of the button,
	   whichever is smaller — and the second term is the one that matters.

	   The clearance is measured against the design's own arrow, which runs the
	   full height of the photograph. Elementor's Gallery Navigation controls can
	   make that arrow any height they like, and a 36px inset on a 64px button
	   leaves a band of MINUS eight pixels: the target collapses and the arrow
	   stops being clickable at all. Capping the inset at a proportion keeps a real
	   band under every height an editor can choose.

	   At the design's own sizes the cap never bites — 30% of a 2:1 frame is always
	   at least the clearance from a 240px card upward — so the default geometry is
	   exactly what it was. */
	inset: min(clamp(36px, 9.2cqi, 72px), 30%) 0;
	pointer-events: auto;
}

.po-pc--listing .po-pc__nav--prev {
	left: 0;
	background: linear-gradient(to right, rgba(17, 23, 39, 0.3), rgba(17, 23, 39, 0));
}

.po-pc--listing .po-pc__nav--next {
	right: 0;
	background: linear-gradient(to left, rgba(17, 23, 39, 0.3), rgba(17, 23, 39, 0));
}

.po-pc--listing .po-pc__nav--prev:hover {
	background: linear-gradient(to right, rgba(17, 23, 39, 0.44), rgba(17, 23, 39, 0));
}

.po-pc--listing .po-pc__nav--next:hover {
	background: linear-gradient(to left, rgba(17, 23, 39, 0.44), rgba(17, 23, 39, 0));
}

.po-pc--listing .po-pc__nav-icon {
	width: 28px;
	width: clamp(22px, 5.6cqi, 44px);
	height: auto;
	stroke-width: 2.4;
}

/* -------------------------------------------------------------------- dots */

.po-pc--listing .po-pc__dots {
	position: absolute;
	bottom: 10px;
	bottom: clamp(8px, 2.4cqi, 20px);
	left: 50%;
	z-index: 3;
	display: flex;
	gap: 7px;
	gap: clamp(6px, 1.5cqi, 11px);
	align-items: center;
	padding: 6px 10px;
	padding: clamp(5px, 1.2cqi, 9px) clamp(9px, 2.2cqi, 16px);
	transform: translateX(-50%);
	background: rgba(17, 23, 39, 0.42);
	border-radius: 999px;
}

.po-pc--listing .po-pc__dot {
	width: 7px;
	width: clamp(6px, 1.5cqi, 11px);
	height: 7px;
	height: clamp(6px, 1.5cqi, 11px);
	margin: 0;
	padding: 0;
	background: rgba(255, 255, 255, 0.55);
	border: 0;
	border-radius: 50%;
	box-shadow: none;
	cursor: pointer;
	transition: background-color 160ms ease, transform 160ms ease;
}

.po-pc--listing .po-pc__dot.is-active {
	background: #ffffff;
	transform: scale(1.15);
}

/* ---------------------------------------------------------------- overlay */

/* One pill, not two: a marker, a hairline, a photo count — the reference groups
   them, and two separate pills in the same corner read as two separate facts. */
.po-pc--listing .po-pc__overlays {
	right: 10px;
	right: clamp(8px, 2.4cqi, 20px);
	bottom: 10px;
	bottom: clamp(8px, 2.4cqi, 20px);
}

.po-pc--listing .po-pc__overlay {
	gap: 9px;
	gap: clamp(7px, 1.9cqi, 14px);
	height: 28px;
	height: clamp(24px, 6.2cqi, 44px);
	padding: 0 11px;
	padding: 0 clamp(9px, 2.4cqi, 17px);
	background: rgba(17, 23, 39, 0.62);
	border-radius: 6px;
	font-size: 13px;
	font-size: clamp(11px, 2.6cqi, 19px);
	font-weight: 500;
}

.po-pc--listing .po-pc__overlay-item {
	display: inline-flex;
	gap: 5px;
	gap: clamp(4px, 1.1cqi, 8px);
	align-items: center;
}

.po-pc--listing .po-pc__overlay-icon {
	width: 16px;
	width: clamp(14px, 3.1cqi, 22px);
	height: auto;
}

.po-pc--listing .po-pc__overlay-divider {
	width: 1px;
	height: 14px;
	height: clamp(12px, 3cqi, 22px);
	background: rgba(255, 255, 255, 0.42);
}

/* -------------------------------------------------------------------- body */

.po-pc--listing .po-pc__body {
	/* No rules between sections, per the reference: the rhythm is spacing. */
	gap: 4px;
	gap: clamp(3px, 0.9cqi, 7px);
	/* The reference is airier below the credit line than above the price — the
	   bottom of the card is deliberately open space, which is what stops a dense
	   four-line body from reading as cramped. Measured off the reference and its
	   own unannotated "next listing" card. */
	padding: 18px 16px 34px;
	padding: clamp(14px, 4.4cqi, 32px) clamp(14px, 2.6cqi, 20px) clamp(24px, 8.2cqi, 58px);
}

.po-pc--listing .po-pc__headline {
	gap: 8px;
	align-items: center;
}

/* The colour is restated, not inherited: `.po-pc__price` above sets it from the
   Classic palette at the same specificity, so leaving it out here would leave the
   headline of this design wearing the other one's ink. */
.po-pc--listing .po-pc__price {
	color: var(--po-pc-ink);
	font-size: 26px;
	font-size: clamp(20px, 5.2cqi, 38px);
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -0.02em;
}

.po-pc--listing .po-pc__details {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	margin: 0;
	color: var(--po-pc-ink);
	font-size: 15px;
	font-size: clamp(13px, 3.3cqi, 24px);
	font-weight: 400;
	line-height: 1.45;
}

/* The bar is PRINTED between values, as the reference draws it, and hidden from
   assistive technology by the renderer — it is punctuation, not a value. Because
   the renderer emits it BETWEEN details rather than after each one, a property
   missing its last detail cannot leave one dangling. */
.po-pc--listing .po-pc__detail-sep {
	margin: 0 0.42em;
	color: var(--po-pc-icon);
	font-weight: 300;
}

.po-pc--listing .po-pc__address {
	margin: 3px 0 0;
	margin-top: clamp(2px, 0.7cqi, 6px);
	color: var(--po-pc-secondary);
	font-size: 15px;
	font-size: clamp(13px, 3cqi, 22px);
	font-weight: 400;
	line-height: 1.4;
}

.po-pc--listing .po-pc__link:hover {
	color: var(--po-pc-ink);
	text-decoration: underline;
}

.po-pc--listing .po-pc__listed-by {
	margin: 5px 0 0;
	margin-top: clamp(4px, 1.2cqi, 10px);
	color: var(--po-pc-secondary);
	font-size: 11px;
	font-size: clamp(10px, 2.2cqi, 16px);
	font-weight: 400;
	line-height: 1.35;
	letter-spacing: 0.02em;
	text-transform: uppercase;
}

/* ----------------------------------------------------------------- actions */

.po-pc--listing .po-pc__actions {
	position: relative;
	/* Above the stretched address link, so the menu is reachable even though the
	   whole card is a click target. */
	z-index: 4;
	flex: none;
	margin-left: auto;
}

/* A compact light disc carrying the supplied three-dot artwork. The dots take
   the artwork's own blue — it is part of what was supplied, not a colour this
   design chose — through a custom property, so a site can retint the control
   without the icon being redrawn. */
.po-pc--listing .po-pc__more {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	width: clamp(28px, 6.4cqi, 46px);
	height: 32px;
	height: clamp(28px, 6.4cqi, 46px);
	margin: 0;
	padding: 0;
	background: var(--po-ds-color-background);
	border: 0;
	border-radius: 50%;
	box-shadow: none;
	color: var(--po-ds-color-primary);
	cursor: pointer;
	transition: background-color 160ms ease;
}

.po-pc--listing .po-pc__more:hover, .po-pc--listing .po-pc__more[aria-expanded='true'] {
	background: var(--po-ds-color-border);
}

.po-pc--listing .po-pc__more-icon {
	width: 20px;
	width: clamp(18px, 4.2cqi, 30px);
	height: auto;
}

/* ------------------------------------------------------- menu, share, hide */

.po-pc--listing .po-pc__menu {
	position: absolute;
	top: calc(100% + 6px);
	right: 0;
	z-index: 6;
	display: flex;
	flex-direction: column;
	min-width: 150px;
	padding: 6px;
	background: var(--po-ds-color-surface);
	border-radius: 10px;
	box-shadow: 0 4px 6px rgba(17, 23, 39, 0.06), 0 12px 28px rgba(17, 23, 39, 0.16);
}

.po-pc--listing .po-pc__menu-item {
	display: flex;
	gap: 10px;
	align-items: center;
	width: 100%;
	margin: 0;
	padding: 9px 10px;
	background: transparent;
	border: 0;
	border-radius: 7px;
	box-shadow: none;
	color: var(--po-ds-color-text);
	font-size: 14px;
	line-height: 1.3;
	text-align: left;
	cursor: pointer;
}

.po-pc--listing .po-pc__menu-item:hover {
	background: var(--po-ds-color-background);
}

.po-pc--listing .po-pc__menu-icon {
	width: 18px;
	height: 18px;
	color: var(--po-ds-color-muted);
}

/* The share sheet and confirmation stay viewport-centered regardless of which
   property card opened them. */
.po-pc--listing .po-pc__share, .po-pc--listing .po-pc__confirm {
	position: fixed;
	top: 50%;
	left: 50%;
	z-index: 7;
	width: calc(100% - 24px);
	max-width: 320px;
	transform: translate(-50%, -50%);
	background: var(--po-ds-color-surface);
	border-radius: 12px;
	box-shadow: 0 6px 10px rgba(17, 23, 39, 0.08), 0 20px 44px rgba(17, 23, 39, 0.22);
	color: var(--po-ds-color-text);
	text-align: left;
}

.po-pc--listing .po-pc__share-head {
	display: flex;
	gap: 12px;
	align-items: center;
	justify-content: space-between;
	padding: 14px 16px 10px;
}

.po-pc--listing .po-pc__share-title {
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.3;
}

.po-pc--listing .po-pc__share-close {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	margin: 0;
	padding: 0;
	background: transparent;
	border: 0;
	border-radius: 50%;
	box-shadow: none;
	color: var(--po-ds-color-muted);
	cursor: pointer;
}

.po-pc--listing .po-pc__share-close:hover {
	background: var(--po-ds-color-background);
}

.po-pc--listing .po-pc__share-close-icon {
	width: 15px;
	height: 15px;
}

/* Every channel the reference lists is visible at once — a share sheet that
   hides half its options behind a scroll is a shorter share sheet. The cap is
   only there so the panel can never grow taller than the window it is centred
   in. */
.po-pc--listing .po-pc__share-list {
	max-height: min(62vh, 460px);
	margin: 0;
	padding: 0 8px 10px;
	overflow-y: auto;
	list-style: none;
}

.po-pc--listing .po-pc__share-row {
	margin: 0;
}

.po-pc--listing .po-pc__share-item {
	display: flex;
	gap: 12px;
	align-items: center;
	width: 100%;
	margin: 0;
	padding: 6px 10px;
	background: transparent;
	border: 0;
	border-radius: 8px;
	box-shadow: none;
	color: var(--po-ds-color-primary);
	font-size: 14px;
	line-height: 1.3;
	text-align: left;
	text-decoration: none;
	cursor: pointer;
}

.po-pc--listing .po-pc__share-item:hover {
	background: var(--po-ds-color-background);
	color: var(--po-ds-color-primary);
}

.po-pc--listing .po-pc__share-icon {
	width: 21px;
	height: 21px;
}

/* Each channel wears its own colour, as the reference draws them. The marks
   themselves stay PropOffice's own single-colour drawings — colour identifies
   the channel, the label beside it names it, and no third-party brand asset is
   shipped to do either. */
.po-pc--listing .po-pc__share-icon--whatsapp {
	color: #25d366;
}

.po-pc--listing .po-pc__share-icon--facebook {
	color: #1877f2;
}

.po-pc--listing .po-pc__share-icon--x {
	color: #000000;
}

.po-pc--listing .po-pc__share-icon--linkedin {
	color: #0a66c2;
}

.po-pc--listing .po-pc__share-icon--mail, .po-pc--listing .po-pc__share-icon--link {
	color: var(--po-ds-color-primary);
}

.po-pc--listing .po-pc__share-icon--message {
	color: #22c55e;
}

.po-pc--listing .po-pc__share-icon--more {
	color: var(--po-ds-color-muted);
}

.po-pc--listing .po-pc__share-note {
	margin-left: auto;
	color: var(--po-ds-color-muted);
	font-size: 12px;
}

.po-pc--listing .po-pc__confirm {
	padding: 18px;
}

.po-pc--listing .po-pc__confirm-title {
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.3;
}

.po-pc--listing .po-pc__confirm-text {
	margin: 6px 0 0;
	color: var(--po-ds-color-muted);
	font-size: 13px;
	line-height: 1.45;
}

.po-pc--listing .po-pc__confirm-actions {
	display: flex;
	gap: 8px;
	justify-content: flex-end;
	margin-top: 16px;
}

.po-pc--listing .po-pc__button {
	margin: 0;
	padding: 8px 16px;
	background: var(--po-ds-color-surface);
	border: 1px solid var(--po-ds-color-border);
	border-radius: 8px;
	box-shadow: none;
	color: var(--po-ds-color-text);
	font-size: 14px;
	font-weight: 500;
	line-height: 1.3;
	cursor: pointer;
}

.po-pc--listing .po-pc__button:hover {
	background: var(--po-ds-color-background);
}

.po-pc--listing .po-pc__button--primary {
	background: var(--po-ds-color-primary);
	border-color: var(--po-ds-color-primary);
	color: #ffffff;
}

.po-pc--listing .po-pc__button--primary:hover {
	filter: brightness(0.94);
	background: var(--po-ds-color-primary);
}

/* The listing that arrives when another is dismissed.
   An ANIMATION rather than a transition, deliberately: a transition needs a
   second frame and a timer to drive it, and tying the DOM removal to a timer is
   how a card ends up half-removed when a visitor dismisses two in quick
   succession. The class is added as the card enters and never has to come off. */
@keyframes po-pc-enter {
	from {
		opacity: 0;
		transform: translateY(6px);
	}

to {
		opacity: 1;
		transform: none;
	}
}

.po-pc--listing.is-entering {
	animation: po-pc-enter 220ms ease-out;
}


/* ──────────────────────────────────────────────────── accessible-only text */

.po-pc .screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* ────────────────────────────────────────────────────────────── responsive */

/* Tablet (768px–1023px): a fixed column request drops to two — unless the
   placement authored its own tablet count (`--po-pc-columns-tablet`, written by
   the Property Search widget's "Result Columns (Tablet)" control). Unset, the
   fallback is exactly the rule this always was. */
@media (max-width: 1023px) {
	.po-pc-grid--fixed {
		grid-template-columns: repeat(
			var(--po-pc-columns-tablet, min(var(--po-pc-columns, 2), 2)),
			minmax(0, 1fr)
		);
	}
}

/* Mobile (< 768px): one card per row.
 *
 * Only the COLLECTION changes here. The card itself keeps its type, spacing and
 * control sizes at every width, because the reference design is a ~414px-wide
 * rendering — tightening the body below 768px would compress the very width the
 * design was drawn at. A narrow viewport gets fewer cards per row, not a
 * smaller card. */
@media (max-width: 767px) {
	.po-pc-grid,
	.po-pc-grid--fixed {
		/* One per row unless the placement authored its own mobile count
		   (`--po-pc-columns-mobile`). Unset, this is the single column it has
		   always been. */
		grid-template-columns: repeat(var(--po-pc-columns-mobile, 1), minmax(0, 1fr));
		--po-pc-gap: 16px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.po-pc,
	.po-pc__nav,
	.po-pc__favourite {
		transition: none;
	}

	/* Qualified by the frame to match the rule it is switching off. The
	   geometry rule is two classes, so a lone `.po-pc__image` here would lose
	   to it and the transition would survive a reduced-motion request. */
	.po-pc__frame .po-pc__image {
		transition: none;
	}

	/* The gallery still pages — the arrows work, the photograph changes, the
	   count follows. It simply does not travel to get there.

	   The script asks the same question of matchMedia and swaps the tracks
	   outright, so nothing is left waiting on a transitionend that a
	   `transition: none` element will never fire. This rule is the belt to
	   that braces: a browser that reports reduced motion to CSS but not to
	   JavaScript still gets an instant change rather than a 300ms slide. */
	.po-pc__frame .po-pc__track {
		transition: none;
	}

	.po-pc:hover {
		transform: none;
	}

	/* The zoom is motion, which is the thing being asked for less of. The
	   photograph still changes on hover in every other respect; it simply does
	   not travel. */
	.po-pc__frame:hover .po-pc__image,
	.po-pc__frame:focus-within .po-pc__image {
		transform: none;
	}
}
