/*
Theme Name: Rebar Child Theme
Theme URI: https://rebar.themerex.net/
Description: Rebar is a Premium WordPress theme that has built-in support for popular Page Builders, slider with swipe gestures, and is SEO- and Retina-ready. The unique system of inheritance and override options allows setting up individual parameters for different sections of your site and supported plugins.
Author: ThemeREX
Author URI: https://themerex.net/
Version: 1.16.0
Tested up to: 6.9
Requires at least: 5.5
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Tags: blog, e-commerce, portfolio, grid-layout, one-column, two-columns, three-columns, four-columns, left-sidebar, right-sidebar, custom-background, custom-colors, custom-header, custom-logo, custom-menu, editor-style, featured-image-header, featured-images, flexible-header, footer-widgets, full-width-template, microformats, post-formats, sticky-post, theme-options, threaded-comments, translation-ready, block-styles, wide-blocks
Text Domain: rebar
Template: rebar
*/


/* =Child-Theme customization starts here
------------------------------------------------------------ */
/* ---------------------------------------------------------------------------
   Duranco customisations
   ------------------------------------------------------------------------- */

/* Mobile header: the phone stays OUTSIDE the burger, but the number itself does
   not fit next to logo + CTA + burger at 375px (it squeezed the CTA from 106px
   down to 49px), so under 768px only the tappable handset icon remains. Tablets
   are wide enough and keep the full number. */
@media (max-width: 767px) {
	.duranco-header-phone--mobile .trx-addons-icon-list-text {
		display: none;
	}
	.duranco-header-phone--mobile .trx-addons-icon-list-item > a {
		display: flex;
		align-items: center;
		justify-content: center;
		min-width: 44px;
		min-height: 44px;
	}
}

/* Sticky mobile action bar — markup in functions.php.
   z-index sits above the theme's scroll-to-top (7999) and below the fixed
   header (100002), so a sticky header never ends up behind it. */
.duranco-mobile-bar {
	display: none;
}

@media (max-width: 767px) {
	.duranco-mobile-bar {
		display: flex;
		position: fixed;
		z-index: 8000;
		left: 0;
		right: 0;
		bottom: 0;
		gap: 8px;
		padding: 7px 12px calc(7px + env(safe-area-inset-bottom, 0px));
		background: #FFFFFF;
		box-shadow: 0 -2px 14px rgba(0, 0, 0, .14);
	}

	.duranco-mobile-bar__btn {
		flex: 1 1 50%;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 8px;
		min-height: 48px;          /* comfortable tap target */
		padding: 0 10px;
		border-radius: 4px;
		font-size: 15px;
		font-weight: 600;
		line-height: 1.2;
		text-decoration: none;
		white-space: nowrap;
	}

	.duranco-mobile-bar__btn--call {
		color: #121414;
		color: var(--theme-color-title, #121414);
		border: 1px solid rgba(18, 20, 20, .25);
		background: #FFFFFF;
	}

	.duranco-mobile-bar__btn--cta,
	.duranco-mobile-bar__btn--cta:hover,
	.duranco-mobile-bar__btn--cta:focus {
		color: #FFFFFF;
		background: #B55329;
		background: var(--theme-color-link, #B55329);
	}

	.duranco-mobile-bar__btn--call:hover,
	.duranco-mobile-bar__btn--call:focus {
		color: #B55329;
		color: var(--theme-color-link, #B55329);
	}

	.duranco-mobile-bar__icon {
		width: 18px;
		height: 18px;
		flex: 0 0 auto;
	}

	/* Keep the bar from covering the end of the page. !important because Rebar
	   enqueues this child stylesheet BEFORE its own, so an equal-specificity
	   theme rule would otherwise win the cascade. */
	body.duranco-has-mobile-bar {
		padding-bottom: calc(62px + env(safe-area-inset-bottom, 0px)) !important;
	}

	/* The scroll-to-top button lives in the same corner as the bar. It hides
	   itself by animating `bottom` to -45px (just off-screen) from JS, so ANY
	   constant offset -- transform or margin -- drags the hidden state back into
	   view and drops it on top of the bar. On phones the bar owns that corner,
	   so the button stands down -- but only while the bar is actually on the page.
	   With the bar unhooked nothing is in that corner and the button comes back.
	   Untouched on tablet and desktop either way. */
	body.duranco-has-mobile-bar .trx_addons_scroll_to_top {
		display: none !important;
	}
}

/* The mobile bar is logo | phone | CTA | burger on 375px, and Elementor lets all
   four shrink: the CTA collapsed to 70px and wrapped "Free Estimate" onto two
   lines while the burger lost a third of its width. Pin the right-hand group to
   its content and let the logo (116px of text in a 157px slot) absorb the slack.

   Elementor writes per-element rules (.elementor-4387 .elementor-element.<id>)
   whose specificity beats any class hook, so these mobile-only corrections are
   marked !important instead of being restated at silly specificity. The row is
   addressed by element id because container-level "CSS Classes" are not rendered
   by this Elementor version -- the widget-level ones are, hence the mix. */
@media (max-width: 767px) {
	.elementor-element-ce5ccfe {
		width: auto !important;
		flex-shrink: 0 !important;
	}
	.elementor-element-ce5ccfe > * {
		flex-shrink: 0 !important;
	}
	/* with the right-hand group pinned, the logo slot narrows to 97px and
	   "Duranco Inc" broke onto two lines; 82px of text plus the 188px group
	   still fits 335px, so it only needed permission not to wrap */
	.elementor-element-539ed6c .logo_text {
		white-space: nowrap;
	}

	.duranco-header-cta--mobile .elementor-button {
		padding: 10px 12px !important;
		font-size: 12px !important;
		white-space: nowrap;
	}
}

/* Related-projects card: make the whole photo the link.
   The theme renders an empty <a class="icons"> over the thumbnail -- position
   static, 0px wide, so nothing was clickable except the title. Stretch it over
   the image and keep it above the hover mask. It stays aria-hidden, which is
   correct: the linked title next to it is the one screen readers announce. */
.sc_portfolio_item_thumb {
	position: relative;
}

.sc_portfolio_item_thumb a.icons {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2;
}

.sc_portfolio_item_header {
	margin-top: 14px;
}

.sc_portfolio_item_city {
	margin-top: 4px;
	opacity: .75;
	font-size: 15px;
}

/* Only one <h1> per page. Section headings and counter numbers were demoted to
   <h2>/<div> for that, so this restores the h1 metrics they used to inherit.
   Both values are the theme's own vars, so all three breakpoints follow. */
.dc-h1-size .elementor-heading-title,
.dc-h1-size .trx-addons-counter-number-wrap {
	font-size: var(--theme-font-h1_font-size);
	line-height: var(--theme-font-h1_line-height);
}

/* The 404's own title was an <h3> and the view had no <h1> at all; it is now
   the <h1>, and this holds the size it had so the page looks unchanged. */
.dc-h3-size .elementor-heading-title {
	font-size: var(--theme-font-h3_font-size);
	line-height: var(--theme-font-h3_line-height);
}

/* Archive/search/404 titles injected by duranco_archive_h1() in functions.php.
   Rebar's own title panel never runs here because the header is a custom layout. */
.dc-archive-title {
	margin: 0 0 1.4em;
}

/* ---------------------------------------------------------------------------
 * Testimonials: one card height per widget, the rest opens in a dialog.
 *
 * The six real reviews differ by roughly 2x in length (337 to 797 characters),
 * so the cards rendered at wildly different heights and the slider jumped as it
 * advanced. duranco_testimonials_quotes() in functions.php measures every quote
 * in a widget and cuts them all to the SHORTEST one; what does not fit is faded
 * out and a "Read full review" button opens the whole text in a modal.
 *
 * Until 2026-08-26 the overflow scrolled inside the card instead. The client
 * asked for that to go: a nested vertical scroller inside a horizontal swipe
 * area fights the swipe on touch and looks like a bug on desktop.
 *
 * The cut height itself is written inline by the script because it can only be
 * known at runtime -- it depends on the rendered text. Everything that does not
 * depend on measurement lives here.
 * ------------------------------------------------------------------------- */

.dc-tst-quote {
	position: relative;
	overflow: hidden;
}

.dc-tst-quote.is-clipped {
	cursor: pointer;
}

/* Both gradient stops come from the script as custom properties: the card's
   background is an Elementor global, and fading to the keyword `transparent`
   would interpolate through transparent BLACK and smear the last line grey. */
.dc-tst-quote.is-clipped::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 4.5em;
	pointer-events: none;
	background: linear-gradient( to bottom,
		var( --dc-tst-fade-from, rgba( 255, 255, 255, 0 ) ) 0%,
		var( --dc-tst-fade-to, #FFFFFF ) 92% );
	/* ⚠ NO transition here. A transition on a pseudo-element styled in the same
	   pass that creates it sticks at its start value for good, and transition
	   declarations sit ABOVE author !important in the cascade. */
}

/* order:4 puts the button straight after the quote. The content wrapper is a
   flex column and the plugin hands out 2/3/4/5 -- heading / rating / text /
   author, once `rating_position: before` is in play. A button left at the
   default order 0 would jump to the very top of the card. */
.dc-tst-more {
	order: 4;
	align-self: flex-start;
	/* 10px below so the reviewer's name is not sitting on the link. */
	margin: 16px 0 10px;
	padding: 0 0 2px;
	border: 0;
	border-bottom: 1px solid rgba( 181, 83, 41, 0.35 );
	border-radius: 0;
	background: none;
	-webkit-appearance: none;
	appearance: none;
	cursor: pointer;
	font-family: inherit;
	font-size: 14px;
	font-weight: 500;
	line-height: 1.4;
	color: #B55329;
	transition: color .2s ease, border-color .2s ease;
}

/* ⚠ `background` has to be NAMED here, not just left at the base rule's `none`.
   The theme paints every button on the site from
   `button:where( :not(…):not(…):not(…) ):hover { background-color: … }` and
   `:where()` adds no specificity, so that selector weighs exactly `button:hover`
   = (0,1,1) and beats the (0,1,0) `.dc-tst-more`. This rule is (0,2,0) and wins
   -- but only over the properties it actually declares. */
.dc-tst-more:hover,
.dc-tst-more:focus,
.dc-tst-more:focus-visible,
.dc-tst-more:active {
	background: none;
	box-shadow: none;
}

.dc-tst-more:hover,
.dc-tst-more:focus-visible {
	color: #121414;
	border-bottom-color: #121414;
}

.dc-tst-more:focus-visible {
	outline: 2px solid #B55329;
	outline-offset: 3px;
}

/* --- Quote typography -------------------------------------------------------
 * The reviews used to be rendered as <h6> and inherited the heading face at
 * heading weight. `content_size` is `div` now, so they fall back to body copy
 * on their own; these values pin that down so nothing drifts on a theme update,
 * and drop the weight to Regular as the client asked. (0,2,0) -- the same
 * specificity as the plugin's own rule for this element, and the child sheet
 * loads after it. */
.trx-addons-testimonials-content-wrapper .trx-addons-testimonials-text-wrapper {
	font-size: 16px;
	font-weight: 400;
	line-height: 1.7;
	letter-spacing: 0;
	text-transform: none;
}

.trx-addons-testimonials-content-wrapper .trx-addons-testimonials-text-wrapper p {
	margin: 0 0 0.85em;
	font-size: inherit;
	font-weight: inherit;
	line-height: inherit;
	color: inherit;
}

.trx-addons-testimonials-content-wrapper .trx-addons-testimonials-text-wrapper p:last-child {
	margin-bottom: 0;
}

/* Only reachable while a widget is still on a heading tag -- see paragraphs()
   in duranco_testimonials_quotes(). */
.dc-tst-p {
	display: block;
}

.dc-tst-p + .dc-tst-p {
	margin-top: 0.75em;
}

/* ---------------------------------------------------------------------------
 * Service cards (info boxes in a grid): one photo height for the whole grid.
 *
 * The images are library files with whatever proportions they shipped with —
 * on Additional Services they ran from 490x490 (1:1) to 850x570 (1.49:1) — so
 * each photo ended at its own height and the row of cards looked ragged.
 * Nothing here is JS: unlike the testimonial clamp, "same height" for images IS
 * expressible in CSS, because we pick the height instead of deriving it from a
 * sibling. Lock the frame to 4:3 and let the photo cover it.
 *
 * object-fit:cover crops instead of squashing, so faces and edges stay in
 * proportion. object-position stays centre — these are wide scene shots with
 * the subject in the middle, and per-image tuning would be unmaintainable.
 *
 * Opted in per container, not by widget type: info boxes are used elsewhere
 * for ICONS, and an icon must never be stretched into a 4:3 photo frame.
 * .elementor-element-5816752 is the Additional Services grid — Elementor emits
 * that class from the element id, so it is stable across edits. dc-svc-grid is
 * the portable hook: add it to a container's "CSS Classes" in the builder and
 * the same treatment applies without touching this file.
 * ------------------------------------------------------------------------- */
.dc-svc-grid .trx-addons-info-box-icon-type-image .trx-addons-info-box-icon,
.dc-svc-grid .trx-addons-info-box-icon-type-image .trx-addons-info-box-icon-inner,
.elementor-element-5816752 .trx-addons-info-box-icon-type-image .trx-addons-info-box-icon,
.elementor-element-5816752 .trx-addons-info-box-icon-type-image .trx-addons-info-box-icon-inner {
	display: block;
	width: 100%;
}

.dc-svc-grid .trx-addons-info-box-icon-type-image img,
.elementor-element-5816752 .trx-addons-info-box-icon-type-image img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	object-position: center;
}

/* Keep each card's contents packed to the top, otherwise a short one spreads
   its text down the spare space and stops lining up with its neighbours.

   Note the descendant selector rather than a child one: a "boxed" Elementor
   container is not the grid itself — it puts an .e-con-inner in between, and
   the cards are grandchildren. A > selector here matches nothing. */
.dc-svc-grid .e-con,
.elementor-element-5816752 .e-con {
	align-content: flex-start;
}

/* A grid sizes each row to its own tallest card, so three rows of cards came
   out 500 / 448 / 474 px. 1fr on the implicit rows makes every row take the
   height of the tallest card on the page instead, so all twelve match.

   Only from tablet up: at the mobile breakpoint the grid collapses to one
   column, where no two cards are ever side by side, and equalising there would
   only pad every short card with dead space. */
@media (min-width: 768px) {
	.dc-svc-grid .e-con-inner,
	.dc-svc-grid.e-grid,
	.elementor-element-5816752 .e-con-inner,
	.elementor-element-5816752.e-grid {
		grid-auto-rows: 1fr;
	}
}

/* --- Before & After sliders (Kitchens 2521, Bathrooms 5815) ----------------
 *
 * trx_addons' "Images Compare" stacks the two photos absolutely on top of each
 * other and reveals them with clip: rect(), so both have to occupy exactly the
 * same box. These pairs do not: several "before" frames are portrait phone
 * shots (1440x1920) while every "after" is landscape. Left alone the seam tears
 * and the handle slides across empty space below the shorter image. Pin the box
 * to 4/3 and let the photos crop into it — the same thing MFR's own CSS does.
 */
.dc-ba .sc_icompare_content {
	aspect-ratio: 4 / 3;
}

.dc-ba .sc_icompare_image {
	height: 100%;
	object-fit: cover;
	object-position: center;
}

/* The widget's own Before/After labels are plain white text with no backdrop,
 * and these photos are mostly pale tile and white cabinets — the words vanish.
 * A dark pill keeps them legible on every frame without touching the theme's
 * typography.
 */
.dc-ba .sc_icompare_text_before,
.dc-ba .sc_icompare_text_after {
	padding: 6px 14px;
	background-color: rgba(0, 0, 0, 0.55);
	border-radius: 3px;
	color: #fff;
	font-size: 13px;
	line-height: 1.2;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

/* City + ZIP under each slider. */
.dc-ba-caption p {
	margin: 0;
	font-size: 15px;
}

/* Container-level "CSS Classes" do not render in this Elementor version, so the
 * grid is reached by its element id instead of a class. Keep both selectors:
 * the class is there for whenever that changes.
 */
.dc-ba-grid,
.elementor-element-dcbagrd {
	align-items: start;
}

/* --- Portfolio grid ([duranco_portfolio]) ----------------------------------
 *
 * Colours and fonts are the theme's own tokens (#121414 title, #86898C text,
 * #E5E7DE rule, #B55329 accent, Inter / Inter Tight) so the grid reads as part
 * of the site rather than as a bolted-on block. The literals are repeated
 * because the theme only exposes a few of them as CSS variables, and the ones
 * we need most (the accent) is not among them.
 */
.dcpf__filters {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-bottom: 36px;
	padding-bottom: 24px;
	border-bottom: 1px solid #E5E7DE;
	border-bottom-color: var(--theme-color-bd_color, #E5E7DE);
}

.dcpf__chip {
	padding: 9px 16px;
	border: 1px solid #E5E7DE;
	border-color: var(--theme-color-bd_color, #E5E7DE);
	border-radius: 10px;
	background: transparent;
	color: #86898C;
	color: var(--theme-color-text, #86898C);
	font-family: "Inter Tight", Inter, sans-serif;
	font-size: 15px;
	font-weight: 500;
	line-height: 1.2;
	cursor: pointer;
	transition: color .2s ease, border-color .2s ease, background-color .2s ease;
}

/* dcpf-chip-hover-white
 *
 * ⚠ `background` has to be NAMED here. The theme repaints every bare <button>
 * on hover from
 *   button:where(:not(.components-button):not(...)):hover,
 *   ... { color: ...; border-color: ...; background-color: var(--theme-color-hover) }
 * and `:where()` adds no specificity, so that selector weighs exactly
 * `button:hover` = (0,1,1). This rule is (0,2,0) and outranks it -- but only
 * over the properties it actually declares, which is why colour, border and
 * background are all spelled out rather than left to the base rule.
 *
 * The active chip keeps its solid dark fill: `.dcpf__chip.is-active` is the
 * same (0,2,0) weight and is declared after this block, so it wins the tie. */
.dcpf__chip:hover,
.dcpf__chip:focus-visible {
	background: #FFFFFF;
	border-color: #121414;
	border-color: var(--theme-color-title, #121414);
	color: #121414;
	color: var(--theme-color-title, #121414);
	box-shadow: none;
}

.dcpf__chip.is-active {
	background: #121414;
	background: var(--theme-color-title, #121414);
	border-color: #121414;
	border-color: var(--theme-color-title, #121414);
	color: #FFFFFF;
}

/* The count is the quiet half of the chip, so it keeps the accent colour and
 * lets the label carry the contrast. */
.dcpf__count {
	margin-left: 7px;
	color: #B55329;
	font-weight: 600;
}

.dcpf__chip.is-active .dcpf__count {
	color: #E0A588;
}

.dcpf__grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 34px 28px;
}

.dcpf__card {
	display: flex;
	flex-direction: column;
	/* Air under the card. It goes INSIDE the card rather than on the grid gap
	 * on purpose: cards in a row stretch to a common height and .dcpf__meta is
	 * pushed down by `margin-top: auto`, so padding here keeps every meta row
	 * on the same line as its neighbours' and still leaves the 40px below. */
	padding-bottom: 40px;
	cursor: pointer;
}

/* Filtering hides cards outright: the grid should reflow, not leave gaps. */
.dcpf__card.is-hidden {
	display: none;
}

.dcpf__link {
	display: block;
	text-decoration: none;
}

/* Fixed 4/3 box. The source photos are a mix of landscape and two portrait
 * phone shots, and without this the rows would step up and down. */
.dcpf__media {
	position: relative;
	display: block;
	overflow: hidden;
	aspect-ratio: 4 / 3;
	border-radius: 10px;
	background: #F3F2EE;
}

/* Fill the 4/3 box completely. `height: 100%` alone is NOT enough here: the
 * box's height comes from `aspect-ratio`, and a percentage height against a
 * height the engine treats as indefinite silently falls back to `auto` -- a
 * 768x525 photo then rendered 390x266 inside a 390x292 box and left a strip
 * of the placeholder colour whose size varied with each photo's own ratio.
 * Absolute positioning drops the percentage dependency; .dcpf__media is
 * already position:relative, and object-fit still does the cropping. */
/* ⚠ Elementor's custom-frontend.min.css has `.elementor img { height: auto }`
 * at (0,1,1), and every card lives inside `.elementor`. A bare `.dcpf__img`
 * is (0,1,0) and loses to it whatever the load order, so the photo kept its
 * intrinsic height. Scoped to the parent, this is (0,2,0) and wins. */
.dcpf__media .dcpf__img {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
	transition: transform .7s ease;
}

.dcpf__card:hover .dcpf__img {
	transform: scale(1.04);
}

.dcpf__type {
	position: absolute;
	top: 14px;
	left: 14px;
	padding: 6px 11px;
	border-radius: 6px;
	background: rgba(18, 20, 20, .78);
	color: #FFFFFF;
	font-family: "Inter Tight", Inter, sans-serif;
	font-size: 11px;
	font-weight: 600;
	line-height: 1;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.dcpf__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 8px;
	padding-top: 18px;
}

/* The tag row under the photo: City · Area · Status.
 *
 * The middle dot is a ::before on `tag + tag` and not a character in the
 * markup, so a project that has no area or no status simply prints fewer
 * tags instead of a dot with nothing after it. It is also not read out by
 * screen readers, which is the right call for pure punctuation.
 *
 * This replaces the old .dcpf__neigh block. The `text-transform: uppercase`
 * that lived there is gone on purpose -- but dropping it was not enough by
 * itself, because most of the excerpts are stored shouting ("SOUTH PALM
 * BEACH"); duranco_portfolio_city() fixes the data side at render time.
 */
.dcpf__tags {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	/* Spread edge to edge, the same gesture as .dcpf__meta below it, so the
	 * row reads as part of the card rather than as a caption floating at the
	 * left. This is what turns the separator into a real element: see
	 * duranco_portfolio_grid(). */
	justify-content: space-between;
	width: 100%;
	margin: 0;
	color: #86898C;
	color: var(--theme-color-text, #86898C);
	font-size: 13px;
	font-weight: 500;
	line-height: 1.35;
}

.dcpf__sep {
	flex: 0 0 auto;
	/* .3em is a floor, not the spacing: `space-between` supplies the rest.
	 * Without it the dot would touch the text on a card narrow enough that
	 * the distributed gap collapses to nothing. */
	margin: 0 .3em;
	color: #B55329;
	/* Scaled up so the accent colour is actually visible: at 13px the glyph
	 * is a couple of pixels wide and reads as grey next to grey text.
	 * `line-height: 0` keeps the bigger character from opening the line box
	 * and shifting the tag row against the photo above it. */
	font-size: 1.35em;
	font-weight: 700;
	line-height: 0;
	vertical-align: -0.02em;
}

.dcpf__name {
	margin: 0;
	color: #121414;
	color: var(--theme-color-title, #121414);
	font-family: "Inter Tight", Inter, sans-serif;
	font-size: 21px;
	font-weight: 500;
	line-height: 1.24;
}

.dcpf__meta {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 12px;
	margin-top: auto;
	padding-top: 14px;
	border-top: 1px solid #E5E7DE;
	border-top-color: var(--theme-color-bd_color, #E5E7DE);
	color: #86898C;
	color: var(--theme-color-text, #86898C);
	font-size: 14px;
	line-height: 1.3;
}

.dcpf__cta {
	color: #B55329;
	font-weight: 600;
	white-space: nowrap;
}

/* The gallery's 2nd..Nth photos are anchors that exist only so Elementor's
 * lightbox can collect them into one slideshow. They must stay in the DOM and
 * stay findable, so they are clipped rather than display:none.
 */
.dcpf__slides {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
}

@media (max-width: 1279px) {
	.dcpf__grid {
		grid-template-columns: repeat(2, 1fr);
		gap: 30px 24px;
	}
}

@media (max-width: 767px) {
	.dcpf__grid {
		grid-template-columns: 1fr;
		gap: 28px;
	}

	.dcpf__filters {
		margin-bottom: 28px;
		padding-bottom: 18px;
	}

	.dcpf__name {
		font-size: 19px;
	}
}

/* DC-BEGIN testimonials-mobile */
/* --- Testimonials: horizontal swipe on mobile (grid layout only) ------------
 *
 * The home page prints the widget as a Swiper slider (`slider: yes`), so it is
 * already swipeable. /kitchens/, /bathrooms/ and /additions-conversions/ print
 * it as `layout: grid` with only 2 reviews: on mobile `columns_mobile: 1` just
 * stacks the two cards and there is no paging affordance at all.
 *
 * Turn that grid into a scroll-snap carousel below 768px. Scoped to
 * `.trx-addons-testimonials-grid`, the class the GRID variant carries -- the
 * slider variant is `.trx-addons-testimonials-carousel` inside
 * `.swiper-container`, so nothing here can reach the home page.
 */
/* Dots for the grid variant, injected by duranco_testimonials_dots() (child
 * functions.php, wp_footer:23). Hidden by default: above 767px the grid is a
 * normal wrapped row with every card on screen, so there is nothing to page. */
.dc-tst-dots {
	display: none;
}

@media (max-width: 767px) {

	/* Same look as the home-page slider's own .swiper-pagination: 7px circles,
	 * #E5E7DE idle / #B55329 active, 15px apart, centred, 30px below the cards.
	 * The button is 22x24 so the tap target is usable while the visible gap
	 * between the circles still measures the same 15px. */
	/* `.trx-addons-testimonials-box` is itself `display:flex; flex-wrap:wrap`, so
	 * the dots arrive as a second flex ITEM and would otherwise shrink to the
	 * width of the two circles and sit hard left. Claim the whole line. */
	.dc-tst-dots {
		display: flex;
		flex: 0 0 100%;
		width: 100%;
		justify-content: center;
		align-items: center;
		margin-top: 30px;
	}

	.dc-tst-dot {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 22px;
		height: 24px;
		padding: 0;
		margin: 0;
		border: 0;
		border-radius: 0;
		background: none;
		-webkit-appearance: none;
		appearance: none;
		cursor: pointer;
		line-height: 0;
	}

	.dc-tst-dot::before {
		content: '';
		display: block;
		width: 7px;
		height: 7px;
		border-radius: 50%;
		background-color: #E5E7DE;
		/* ⚠ NO transition here. The dots are created and immediately painted, and
		 * Chrome then starts a background-color transition on a pseudo-element
		 * that has never had a resolved style -- it sticks at the start colour
		 * for good. Transition declarations sit ABOVE author !important in the
		 * cascade, so nothing can override the stuck value; the active dot simply
		 * stays grey. Instant colour change, no quirk. */
	}

	.dc-tst-dot.is-active::before {
		background-color: #B55329;
	}

	.dc-tst-dot:focus-visible {
		outline: 2px solid #B55329;
		outline-offset: 2px;
	}

	.trx-addons-testimonials-grid {
		display: flex;
		flex-wrap: nowrap;
		overflow-x: auto;
		overflow-y: hidden;
		gap: 14px;
		/* The theme hangs a -20px bottom margin on the grid to swallow the row
		 * gap of a wrapped multi-row layout. One row now, so drop it or the box
		 * eats 20px of whatever follows. */
		margin: 0;
		scroll-snap-type: x mandatory;
		-webkit-overflow-scrolling: touch;
		/* No visible track: the peeking next card is the affordance. */
		scrollbar-width: none;
	}

	.trx-addons-testimonials-grid::-webkit-scrollbar {
		display: none;
	}

	/* 88% leaves ~12% of the next card in frame, which is what tells the visitor
	 * there IS a next card. Fixed basis + no shrink, or flex would squeeze both
	 * cards into the viewport and there would be nothing to scroll. */
	.trx-addons-testimonials-grid > .trx-addons-testimonials-container {
		flex: 0 0 88%;
		width: 88%;
		max-width: 88%;
		margin: 0;
		scroll-snap-align: start;
	}

	/* ⚠ The cards are ThemeREX "animated-item"s: they ship with
	 * `elementor-invisible trx_addons_invisible` (visibility:hidden) and are
	 * un-hidden by a waypoint when they scroll into the VERTICAL viewport. A
	 * card parked outside a HORIZONTAL scroller may never trip that waypoint and
	 * would then stay hidden for ever -- exactly the failure mode that hid the
	 * before/after sliders. Inside this scroller the animation is not worth the
	 * risk, so force them visible. */
	.trx-addons-testimonials-grid > .trx-addons-testimonials-container.trx_addons_invisible,
	.trx-addons-testimonials-grid > .trx-addons-testimonials-container.elementor-invisible {
		visibility: visible;
		opacity: 1;
	}

	.trx-addons-testimonials-grid .trx-addons-testimonials-content-wrapper {
		height: 100%;
	}

	/* `duranco_testimonials_quotes()` (child functions.php, wp_footer:21) cuts
	 * every quote in a widget down to the height of the SHORTEST one, so the
	 * columns of a desktop row line up. On a 1-up mobile card there is no row to
	 * line up with, so the script skips the clamp below 768px -- these rules are
	 * the belt to that braces, in case a resize leaves an inline height behind.
	 * The "Read full review" button goes with it: nothing is hidden here, so
	 * there is nothing to expand. */
	.trx-addons-testimonials-text-wrapper.dc-tst-quote {
		height: auto !important;
		max-height: none !important;
		overflow: visible;
		padding-right: 0;
	}

	.dc-tst-quote.is-clipped::after {
		display: none;
	}

	.dc-tst-more {
		display: none;
	}

	/* Swipe is the affordance on a touch screen, and a 46px circle parked over a
	 * 88%-wide card covers the text. */
	.elementor-widget-trx_elm_testimonials .trx-addons-slider-arrow {
		display: none;
	}
}
/* DC-END testimonials-mobile */

/* --- Elementor lightbox: make prev/next читаемыми ---------------------------
 *
 * Elementor ships each slideshow arrow as a 20%-wide transparent flex box
 * holding a bare near-white chevron (rgba(237,237,237,.9), 25px). Over the
 * bright kitchen and bathroom photos in the portfolio galleries it disappears
 * into the picture, so nothing tells the visitor the gallery can be paged.
 *
 * Give it a real control: dark disc, brand accent on hover, pinned inside the
 * frame. Scoped to .elementor-lightbox so it cannot leak into the page swipers.
 *
 * ⚠ SPECIFICITY. Two Elementor sheets fight us here, and a plain
 * `.elementor-lightbox .elementor-swiper-button` (0,2,0) loses to BOTH:
 *   - e-swiper.min.css      `.elementor-lightbox .swiper ~ .elementor-swiper-button`
 *                           (0,3,0) -> colour rgba(237,237,237,.9), font-size 25px
 *   - custom-lightbox.min.css `.elementor-lightbox .elementor-swiper-button-next,
 *                              .elementor-lightbox .elementor-swiper-button-prev`
 *                           (0,2,0) but loaded AFTER the child theme -> width 20%
 * That is why the first attempt produced a 254x938 ellipse: background and
 * border-radius landed (nobody else sets them) while width/height/colour did
 * not. Doubling the class names lifts every rule below to (0,4,0), which beats
 * both regardless of load order. Ugly, deliberate, commented.
 */
.elementor-lightbox.elementor-lightbox .elementor-swiper-button.elementor-swiper-button {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 58px;
	height: 58px;
	min-width: 58px;
	max-width: 58px;
	font-size: 22px;
	line-height: 1;
	color: #FFFFFF;
	background: rgba(18, 20, 20, .62);
	border-radius: 50%;
	transition: background-color .2s ease, transform .2s ease;
}

/* The chevron is an inline SVG, and custom-lightbox.min.css paints it with
 * `fill: var(--lightbox-ui-color)`. Follow the button's own colour instead so
 * the hover state below carries the glyph with it. */
.elementor-lightbox.elementor-lightbox .elementor-swiper-button.elementor-swiper-button svg {
	width: 1em;
	height: 1em;
	fill: currentColor;
}

.elementor-lightbox.elementor-lightbox .elementor-swiper-button.elementor-swiper-button:hover,
.elementor-lightbox.elementor-lightbox .elementor-swiper-button.elementor-swiper-button:focus-visible {
	color: #FFFFFF;
	background: #B55329;
	/* translateY(-50%) comes from e-swiper.css and must be repeated here, or the
	 * button jumps half its height on hover. */
	transform: translateY(-50%) scale(1.06);
}

/* Both insets are set by Elementor (left:0 / right:0 from custom-lightbox), so
 * release the opposite one or the box is anchored on two sides at once. */
.elementor-lightbox.elementor-lightbox .elementor-swiper-button-prev.elementor-swiper-button-prev {
	left: 26px;
	right: auto;
}

.elementor-lightbox.elementor-lightbox .elementor-swiper-button-next.elementor-swiper-button-next {
	right: 26px;
	left: auto;
}

/* A single-photo project (all six room additions) has nothing to page to.
 * Swiper marks the arrows locked; hide them rather than offer a dead control. */
.elementor-lightbox.elementor-lightbox .elementor-swiper-button.swiper-button-lock {
	display: none;
}

.elementor-lightbox.elementor-lightbox .elementor-swiper-button.swiper-button-disabled {
	opacity: .3;
}

@media (max-width: 767px) {
	.elementor-lightbox.elementor-lightbox .elementor-swiper-button.elementor-swiper-button {
		width: 44px;
		height: 44px;
		min-width: 44px;
		max-width: 44px;
		font-size: 18px;
	}

	.elementor-lightbox.elementor-lightbox .elementor-swiper-button-prev.elementor-swiper-button-prev {
		left: 10px;
	}

	.elementor-lightbox.elementor-lightbox .elementor-swiper-button-next.elementor-swiper-button-next {
		right: 10px;
	}
}

/* DC-BEGIN testimonials-arrows */
/* --- Testimonials slider arrows ---------------------------------------------
 *
 * `arrows: yes` is set on the two carousel widgets (Home 2515, About 2529); the
 * three service pages print the widget as a 2-up grid, where render_arrows() is
 * never even called. The plugin's own look is the ThemeREX demo: a 40px white
 * circle with a black glyph and no border. Repaint it in the Duranco palette.
 *
 * The circles have to sit INSIDE the slider: `.trx-addons-testimonials-box` is
 * overflow:hidden, so anything hung outside the track is clipped away.
 */
.elementor-widget-trx_elm_testimonials .trx-addons-slider-arrow {
	width: 46px;
	height: 46px;
	font-size: 17px;
	line-height: 1;
	color: #121414;
	background-color: #FFFFFF;
	border: 1px solid #E5E7DE;
	box-shadow: 0 6px 18px rgba( 18, 20, 20, 0.10 );
}

.elementor-widget-trx_elm_testimonials .trx-addons-arrow-prev {
	left: 12px;
}

.elementor-widget-trx_elm_testimonials .trx-addons-arrow-next {
	right: 12px;
}

.elementor-widget-trx_elm_testimonials .trx-addons-slider-arrow:hover,
.elementor-widget-trx_elm_testimonials .trx-addons-slider-arrow:focus-visible {
	color: #FFFFFF;
	background-color: #B55329;
	border-color: #B55329;
}

.elementor-widget-trx_elm_testimonials .trx-addons-slider-arrow:focus-visible {
	outline: 2px solid #B55329;
	outline-offset: 2px;
}
/* The circles are absolutely positioned inside `.trx-addons-testimonials-box`,
 * which is overflow:hidden -- they cannot be hung outside it, so the track has
 * to move in instead. 58px = the 46px circle + a 12px gap, minus the 15px the
 * carousel already pulls back with its negative margins... which is why the
 * carousel needs its own clip: `overflow` cuts at the PADDING box, so without
 * the second rule the next slide still shows through the padding we just made.
 */
@media (min-width: 768px) {

	.elementor-widget-trx_elm_testimonials .trx-addons-testimonials-box.swiper-container-wrap {
		padding-left: 58px;
		padding-right: 58px;
	}

	.elementor-widget-trx_elm_testimonials .trx-addons-testimonials-carousel {
		overflow: hidden;
	}
}
/* DC-END testimonials-arrows */

/* DC-BEGIN testimonials-modal */
/* --- Full review dialog -----------------------------------------------------
 *
 * Built once, lazily, by duranco_testimonials_quotes() and parked on <body>.
 *
 * ⚠ Being outside the widget means Elementor's per-element CSS no longer
 * reaches the stars: `fill` and `empty` are written as
 * `.elementor-element-<id> .trx-addons-fb-rev-star-... svg { fill: ... }`, so a
 * copy of that markup anywhere else renders in the browser default. The two
 * colours are therefore repeated here -- accent #B55329, idle #E5E7DE, the same
 * pair the widgets carry.
 */
html.dc-tst-modal-open {
	overflow: hidden;
}

.dc-tst-modal[hidden] {
	display: none;
}

.dc-tst-modal {
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 100000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 24px;
}

.dc-tst-modal__backdrop {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	background: rgba( 18, 20, 20, 0.55 );
}

.dc-tst-modal__dialog {
	position: relative;
	z-index: 1;
	width: 100%;
	max-width: 640px;
	max-height: 82vh;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	padding: 42px 42px 34px;
	background: #FFFFFF;
	border-radius: 10px;
	box-shadow: 0 24px 60px rgba( 18, 20, 20, 0.28 );
}

.dc-tst-modal__close {
	position: absolute;
	top: 8px;
	right: 8px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: none;
	-webkit-appearance: none;
	appearance: none;
	cursor: pointer;
	font-size: 30px;
	line-height: 1;
	color: #86898C;
}

/* A plain grey cross that does not react to the pointer. Same (0,1,1) button
   rule as above: with `border-radius: 50%` on this one the theme's hover fill
   came out as a brown DISC behind the ✕. The colour is restated rather than
   simply dropped -- take the rule away entirely and the theme decides it. */
.dc-tst-modal__close:hover,
.dc-tst-modal__close:focus,
.dc-tst-modal__close:active {
	color: #86898C;
	background: none;
	box-shadow: none;
}

.dc-tst-modal__close:focus-visible {
	outline: 2px solid #B55329;
	outline-offset: 2px;
}

.dc-tst-modal__rating {
	margin: 0 0 18px;
	line-height: 0;
}

.dc-tst-modal__rating:empty {
	display: none;
}

.dc-tst-modal__rating .trx-addons-fb-rev-stars {
	font-size: 17px;
}

.dc-tst-modal__rating .trx-addons-fb-rev-star-filled svg,
.dc-tst-modal__rating .trx-addons-fb-rev-star-half svg {
	fill: #B55329;
}

.dc-tst-modal__rating .trx-addons-fb-rev-star-empty svg {
	fill: #E5E7DE;
}

.dc-tst-modal__text {
	font-size: 16px;
	font-weight: 400;
	line-height: 1.75;
	color: #86898C;
}

.dc-tst-modal__text p {
	margin: 0 0 0.9em;
}

.dc-tst-modal__text p:last-child {
	margin-bottom: 0;
}

.dc-tst-modal__author {
	margin-top: 26px;
	padding-top: 18px;
	border-top: 1px solid #E5E7DE;
}

.dc-tst-modal__name {
	display: block;
	font-size: 18px;
	font-weight: 600;
	line-height: 1.3;
	color: #121414;
}

.dc-tst-modal__company {
	display: block;
	margin-top: 4px;
	font-size: 14px;
	line-height: 1.4;
	color: #86898C;
}

.dc-tst-modal__company:empty {
	display: none;
}

@media (max-width: 767px) {

	.dc-tst-modal {
		padding: 14px;
	}

	.dc-tst-modal__dialog {
		max-height: 88vh;
		padding: 36px 20px 24px;
	}
}
/* DC-END testimonials-modal */

/* DC-BEGIN tablet-stack */
/* --- Tablet and below: stack the split sections ---------------------------
 *
 * Elementor's breakpoints on this site are the defaults -- the
 * `elementor_breakpoints` option is unset, mobile_extra / tablet_extra /
 * laptop / widescreen are all disabled -- so "tablet" means 1279px, NOT 1024.
 * `max-width: 1279px` therefore covers tablet AND phone in one go, which is
 * what we want: the 50% columns have no mobile override either, so without
 * this the heading would sit in a 160px-wide well on a phone.
 *
 * See the header of apply-tablet-stack.php for why this is CSS keyed to
 * element ids rather than a `flex_direction_tablet` edit in _elementor_data.
 */
@media (max-width: 1279px) {

	/* The row that holds heading-column + text-column.
	 * 9b75232 kitchens 2521 | 15e3902 bathrooms 5815 | a0e695a additions 5816
	 * The four --container-widget-* lines are not decoration: they are what
	 * Elementor's own `flex_direction: column` preset sets alongside the
	 * direction, and dropping them leaves any direct widget child sized as
	 * if the container were still a row. */
	body .elementor .elementor-element.elementor-element-9b75232,
	body .elementor .elementor-element.elementor-element-15e3902,
	body .elementor .elementor-element.elementor-element-a0e695a {
		--flex-direction: column;
		--container-widget-width: 100%;
		--container-widget-height: initial;
		--container-widget-flex-grow: 0;
		--container-widget-align-self: initial;
	}

	/* Its two 50% children, heading then text, in that order.
	 * 4d9b2ab/eebf997 kitchens | f668edb/9f96e15 bathrooms | 3a78659/ba7bb56 additions */
	body .elementor .elementor-element.elementor-element-4d9b2ab,
	body .elementor .elementor-element.elementor-element-eebf997,
	body .elementor .elementor-element.elementor-element-f668edb,
	body .elementor .elementor-element.elementor-element-9f96e15,
	body .elementor .elementor-element.elementor-element-3a78659,
	body .elementor .elementor-element.elementor-element-ba7bb56 {
		--width: 100%;
	}

	/* Before & After: the four sliders in one column instead of 2x2.
	 * The ROWS have to be restated as well. The grid carries an explicit
	 * `repeat(2, 1fr)`; leave it alone and sliders 3 and 4 drop into
	 * implicit auto rows while 1 and 2 stay locked to an equal fraction,
	 * so the column comes out with two different row heights. `auto` also
	 * removes the dead space `1fr` leaves under the shorter captions --
	 * which is the same complaint that started this task. */
	body .elementor .elementor-element.elementor-element-dcbagrd {
		--e-con-grid-template-columns: repeat(1, 1fr);
		--e-con-grid-template-rows: repeat(4, auto);
	}
}
/* DC-END tablet-stack */

/* DC-BEGIN form-two-up */
/* --- Request form: Name and Email side by side from the desktop edge up -----
 *
 * MetForm 2192 is one template reused on every page that shows the form, so
 * this is keyed to the three element ids inside it rather than to a page.
 *
 * The parent container is flex-COLUMN, which is why 50% widths on their own
 * would still stack -- the direction has to flip too. `flex-wrap: wrap` is
 * what keeps the rest of the form (phone, message, consent, button, all of
 * them inside child container 6b1e4ea2) on its own full-width row underneath.
 *
 * Elementor prints `--flex-direction: column` for this container at (0,3,0)
 * with no media query at all, so the override has to weigh more: leading each
 * selector with `body` makes it (0,3,1) and it wins on weight, not on the
 * accident of which stylesheet loads last.
 *
 * 1280 is Elementor's own desktop edge on this site (tablet ends at 1279), so
 * this is the same boundary every other responsive rule here uses. Below it
 * the fields stay stacked at full width on purpose: at an 834px viewport the
 * form column is only 365px wide, and half of that is the "too small" the
 * client flagged.
 */
@media (min-width: 1280px) {
	body .elementor .elementor-element.elementor-element-7b16fa98 {
		--flex-direction: row;
		--flex-wrap: wrap;
		--column-gap: 20px;
	}

	body .elementor .elementor-element.elementor-element-19d034f0,
	body .elementor .elementor-element.elementor-element-208b034b {
		width: calc(50% - 10px);
		max-width: calc(50% - 10px);
		flex: 0 0 auto;
	}

	body .elementor .elementor-element.elementor-element-6b1e4ea2 {
		--width: 100%;
		width: 100%;
		max-width: 100%;
		flex: 0 0 auto;
	}
}
/* DC-END form-two-up */

/* DC-BEGIN card-tags-narrow */
/* --- The spread tag row needs the whole line to itself ----------------------
 *
 * The widest row on the page is "West Delray Beach · 580 sq ft · In progress"
 * at 275px, and a card is roughly the viewport minus 55px once the grid drops
 * to one column. Measured on the live page: at a 360px viewport all 24 rows
 * are a single line, at 320px one of them is two -- and a wrapped
 * `space-between` line reads as broken rather than as spread, because the
 * orphan on the second line gets pushed to the left edge on its own.
 *
 * So below the narrowest width that still fits, the row goes back to being
 * left-packed and the dot carries the spacing itself.
 */
@media (max-width: 359px) {
	.dcpf__tags {
		justify-content: flex-start;
	}

	.dcpf__sep {
		margin: 0 .5em;
	}
}
/* DC-END card-tags-narrow */

/* DC-BEGIN form-band-equal-height */
@media (min-width: 768px) {
	body .elementor .elementor-element.elementor-element-e6c8680 {
		/* Elementor applies its floor as `min-height: var(--min-height)`, so the
		 * variable is what has to go; `min-height` is restated as well so the
		 * rule still holds if that plumbing ever changes. */
		--min-height: 0px;
		min-height: 0;
		/* Required, not decorative: the container is empty -- the photo is a
		 * background -- so without stretch it would collapse to nothing once
		 * the floor is removed. */
		align-self: stretch;
	}
}
/* DC-END form-band-equal-height */

/* DC-BEGIN home-service-card-links */
/* The <a> is injected by duranco_service_card_link() in functions.php and wraps
 * the whole card, so all that is left here is to undo the theme's link styling
 * and give the card a hover affordance. Nothing is positioned, so there is no
 * overlay geometry to keep in step with Elementor's layout.
 *
 * Every selector leads with `body .elementor` and names the element
 * (`a.dc-servicelink`) because the theme colours links from rules that weigh
 * (0,2,0); a bare `.dc-servicelink` at (0,1,0) would lose to them. */
body .elementor a.dc-servicelink,
body .elementor a.dc-servicelink:hover,
body .elementor a.dc-servicelink:focus {
	display: block;
	color: inherit;
	text-decoration: none;
	box-shadow: none;
}

/* The transition sits on the title, which is markup that already existed, and
 * not on the injected link -- a property that is first declared and first
 * transitioned in the same breath can latch at its start value. */
body .elementor a.dc-servicelink .elementor-icon-box-title,
body .elementor a.dc-servicelink .elementor-icon-box-title span {
	transition: color .25s ease;
}

body .elementor a.dc-servicelink:hover .elementor-icon-box-title,
body .elementor a.dc-servicelink:hover .elementor-icon-box-title span,
body .elementor a.dc-servicelink:focus-visible .elementor-icon-box-title,
body .elementor a.dc-servicelink:focus-visible .elementor-icon-box-title span {
	color: #B55329;
	/* The same variable Elementor already uses for this card's icon, so the
	 * title and the icon cannot end up on two different accents. */
	color: var( --e-global-color-theme_color_link, #B55329 );
}

/* Pointer hover is colour only; a keyboard user gets the whole card outlined. */
body .elementor a.dc-servicelink:focus-visible {
	outline: 2px solid #121414;
	outline: 2px solid var( --theme-color-title, #121414 );
	outline-offset: 8px;
	border-radius: 10px;
}
/* DC-END home-service-card-links */
