/*
Theme Name: Dexter Yard 2026
Description: Custom Theme for BioMed Realty
Theme URI: https://localwebhub.com
Version: 1.2.57
Author: Dmitriy Agadzhanov
Author URI: https://localwebhub.com
*/

/* 
add font family to reset.min.css
*/

:root {
	--theme-menu-height: 51px;
	--theme-content-padding: 15px;
	--theme-bullet-text: #736c55; /* taupe copy on the "Bullets" cream pills (Figma) */
	--dy-columns-gap-mobile: 80px; /* vertical gap between STACKED core/columns (client, 2026-07-29) */

	/* Responsive spacing - override WordPress preset spacing */
	--wp--preset--spacing--small: 15px;
	--wp--preset--spacing--medium: 30px;
	--wp--preset--spacing--large: 60px;
	--wp--preset--spacing--x-large: 90px;
}

@media (min-width: 992px) {
	:root {
		--theme-content-padding: 30px;
		
		/* Desktop spacing - override WordPress preset spacing */
		--wp--preset--spacing--small: 30px;
		--wp--preset--spacing--medium: 60px;
		--wp--preset--spacing--large: 90px;
		--wp--preset--spacing--x-large: 120px;		
	}
}
@media (min-width: 1200px) {
	:root {
		--theme-menu-height: 113px;	
	}
}

html {
	scroll-behavior: smooth;
}

/* ---------------------------------------------------------------------------
   Typography — Dexter Yard type scale (from Figma "Type Hierarchy")
   Font: instrument-sans-variable throughout (client confirmed Instrument Sans
   everywhere; the Roboto styles in the Figma file are intentionally NOT used).
   Sizes/weights per Figma. Sizes reference the theme.json fluid presets; these classes mirror
   styles.elements.h1–h6 + base Body so the type styles are usable both on
   native heading tags (via theme.json) and as utility classes on any element.
   The .has-*-font-size preset classes (added when an editor picks a size in
   the UI) are paired in too — WP only generates their font-size, so without
   this they'd keep the body line-height/tracking, far too loose at H1 scale.
--------------------------------------------------------------------------- */

.acf-block-preview,
body:not(.wp-admin) {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--body, 18px);
	font-weight: 400;
	line-height: 1.28;
	letter-spacing: 0.03em;
}

.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
.subhead {
	margin: 0;
}

.h1,
.has-h-1-font-size {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--h-1, 83px);
	font-weight: 500;
	line-height: 0.87;
	letter-spacing: -0.06em;
}

.h2,
.has-h-2-font-size {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--h-2, 63px);
	font-weight: 400;
	line-height: 1.05;
	letter-spacing: -0.05em;
}

.h3,
.has-h-3-font-size {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--h-3, 42px);
	font-weight: 700;
	line-height: 1.02;
	letter-spacing: -0.02em;
}

.h4,
.has-h-4-font-size {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--h-4, 33px);
	font-weight: 400;
	line-height: 1.06;
	letter-spacing: 0.01em;
}

.h5,
.has-h-5-font-size {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--h-5, 28px);
	font-weight: 500;
	line-height: 1.21;
	letter-spacing: 0.01em;
}

.h6,
.has-h-6-font-size {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--h-6, 24px);
	font-weight: 500;
	line-height: 1.42;
	letter-spacing: 0.01em;
}

.subhead,
.has-subhead-font-size {
	font-family: "instrument-sans-variable", sans-serif;
	font-size: var(--wp--preset--font-size--subhead, 21px);
	font-weight: 400;
	line-height: 1.33;
	letter-spacing: 0.03em;
}

/* Body-size preset picked on a heading: restore body metrics too */
.has-body-font-size {
	font-weight: 400;
	line-height: 1.28;
	letter-spacing: 0.03em;
}

/* Label paragraph style (DY-Bento offset layouts: plus icon + short label) */
p.is-style-label {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 15px;
	line-height: 1.15;
	letter-spacing: 0.038em;
}
p.is-style-label::before {
	content: "";
	flex: none;
	width: 21px;
	height: 21px;
	background: url(images/plus-icon.svg) no-repeat center / contain;
}

body:not(.wp-admin) [id] {
	scroll-margin-top: var(--theme-menu-height);
}

/* Section-level ACF blocks own their vertical spacing — cancel the blockGap
   margin that constrained layouts (post-content) add between top-level
   siblings. :root matches core's `:root :where(.is-layout-constrained) > *`
   specificity floor, then the class outranks it. */
:root .wp-block-acf-section,
:root .wp-block-acf-hero,
:root .wp-block-acf-copy,
:root .wp-block-acf-spacer,
:root .wp-block-acf-bento,
:root .wp-block-acf-parallax,
:root .wp-block-acf-toggle,
:root .wp-block-acf-floorplans,
:root .wp-block-acf-accordion,
:root .wp-block-acf-marquee,
:root .wp-block-acf-leasing-form {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* Stacked core/columns get a wider vertical gap on mobile (client: 80px).
   The gap normally comes from global styles' `:root :where(.is-layout-flex)
   {gap:24px}` (0,1,0) — do NOT override .is-layout-flex itself, that class is
   on every flex group site-wide (header pill, nav, groups). `.wp-block-columns
   :not(.is-not-stacked-on-mobile)` is (0,2,0), so it wins on specificity
   regardless of load order (no !important).
   Breakpoint is CORE's 781px stacking boundary, not the theme's 756px — at
   756–781px columns are already stacked and would otherwise keep 24px.
   row-gap only: once stacked each column is flex-basis:100%, so column-gap is
   moot and stays on the 24px blockGap for the wrap boundary.
   Tune per context by re-setting the var on a wrapper (see floorplans below). */
@media (max-width: 781.98px) {
	.wp-block-columns:not(.is-not-stacked-on-mobile) {
		row-gap: var(--dy-columns-gap-mobile);
	}
}

/* 4+ column core/columns rows keep STACKING until 992px (client, 2026-08-10).
   Core flips its columns block straight from fully stacked to all-across at
   exactly 782px — `@media (min-width:782px){.wp-block-columns{flex-wrap:nowrap
   !important}}` plus `flex-basis:0;flex-grow:1` on the columns. Current core
   has NO intermediate 2-up step (older versions did), so 782px is the only
   boundary there is. Fine for 2 or 3 columns; a 4-column stat row lands at
   163px per column at 850px (measured on Home's "380K SF" row), so the
   boundary moves to 992px for rows of 4+ only. Client picked the full stack
   over a 2×2 for this band (2026-08-10).
   `:has(> .wp-block-column:nth-child(4))` IS the column count — unlike a Grid
   group (see bento.css, which needs a PHP + editor-JS class stamp because core
   hides N inside a generated container class) a columns block's children are
   literally its columns, so plain CSS can count them and the editor canvas
   gets it for free.
   `!important` is required to beat core's own `!important` (sanctioned vendor
   override). row-gap keeps the 80px stacked spacing continuous across the
   782px line instead of dropping back to blockGap's 24px mid-stack. */
@media (min-width: 782px) and (max-width: 991.98px) {
	.wp-block-columns:not(.is-not-stacked-on-mobile):has(> .wp-block-column:nth-child(4)) {
		flex-wrap: wrap !important;
		row-gap: var(--dy-columns-gap-mobile);
	}
	.wp-block-columns:not(.is-not-stacked-on-mobile):has(> .wp-block-column:nth-child(4)) > .wp-block-column {
		flex-basis: 100% !important;
	}
}

/* The floorplans stat columns are a tight stat row — keep them on blockGap. */
.wp-block-acf-floorplans {
	--dy-columns-gap-mobile: 24px;
}

/* "Stat Row" block style for core/columns (client, 2026-08-10) — the 80px
   stacked gap is right for content columns but far too airy between short
   stats ("380K SF / Total Space", "1 Block"), so those rows opt down to
   blockGap, same value the floorplans stat row already uses.
   NOTE this deliberately does NOT try to out-specify the two rules above, and
   nothing here needs to match their long `:has()` selectors: the gap travels
   through a CUSTOM PROPERTY, so the winner is whoever sets `--dy-columns-gap-
   mobile` on (or above) the element, and a single class does that at (0,2,0).
   That is also why a direct `row-gap: 24px` override is the hard way round —
   it has to beat (0,5,0) inside the right media query, twice. Set the var
   instead and both bands follow automatically.
   Registered in functions.php so editors click it in the Styles panel rather
   than typing a CSS class; the whole knob is the one value below. */
.wp-block-columns.is-style-stat-row {
	--dy-columns-gap-mobile: 24px;
}

.theme-container {
	max-width: var(--wp--style--global--wide-size, 1400px);
	margin-left: auto;
	margin-right: auto;
	padding-left: var(--theme-content-padding);
	padding-right: var(--theme-content-padding);
}

.theme-row {
	display: flex;
	flex-wrap: wrap;
	margin-right: calc(-1 * var(--theme-content-padding));
	margin-left: calc(-1 * var(--theme-content-padding));
}

.theme-row > * {
	flex-shrink: 0;
	width: 100%;
	max-width: 100%;
	padding-right: var(--theme-content-padding);
	padding-left: var(--theme-content-padding);
	gap: var(--theme-content-padding);
}

@media (min-width: 992px) {
	.theme-col-lg {
		flex: 1 0 0%;
	}

	.theme-col-lg-auto {
		flex: 0 0 auto;
		width: auto;
	}

    .theme-lg-two-col {
		flex: 1 1 calc((100% - (calc(var(--theme-content-padding)) * 1)) / 2);
    }

	.theme-lg-three-col {
       flex: 1 1 calc((100% - (calc(var(--theme-content-padding)) * 2)) / 3);
    }

    .theme-lg-four-col {
       flex: 1 1 calc((100% - (calc(var(--theme-content-padding)) * 3)) / 4);
    }
	
}

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

.object-fit-contain > img {
	height: 100%;
	width: 100%;	
	object-fit: contain;
}

.alignleft,
.alignright,
.aligncenter {
	display: block;
	margin: 0 auto 30px auto;
}

@media (min-width:768px) {
	.alignleft {
		margin: 0 20px 20px 0;
		float: left;
	}

	.alignright {
		float: right;
		margin: 0 0 20px 20px;
	}
}

.article img {
	max-width: 100%;
	height: auto;
}

.screen-reader-text {
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: polygon(0px 0px, 0px 0px,0px 0px, 0px 0px);
	position: absolute !important;
	white-space: nowrap;
	height: 1px;
	width: 1px;
	overflow: hidden;
}

@media (max-width: 575px) {
	.sm-ignore-br br {
		display: none;
	}
}

@media (max-width: 767px) {
	.md-ignore-br br {
		display: none;
	}
}

@media (max-width: 991px) {
	.lg-ignore-br br {
		display: none;
	}
}

.acf-block-preview * {
	box-sizing: border-box;
}

.acf-block-preview > section,
.acf-block-preview > div {
	font-size: 12px;
	border: 1px solid #ccc;
	min-height: 50px;
	margin-top: 0 !important;
	margin-bottom: 0 !important;
}

.acf-block-preview > div:before,
.acf-block-preview > section:before {
	content: attr(data-blockname);
	position: absolute;
	top: 0;
	left: 0;
	padding: 2px;
	font-size: .875rem;
	background: #ccc;
	color: #000;
	z-index: 4;
}

.slick-slide img.lazyloading {
	opacity: 1;
}

/* .slick-track{
	display: flex !important;
}

.slick-slide {
	display: flex !important;
	height: auto;
} */

.slick-track {
	display: flex;
}

.slick-initialized .slick-slide {
    display: flex;
	flex-direction: column;
	height: auto;
}

.slick-initialized .slick-slide > div {
	flex-grow: 1;
}

.vimeo--video[data-fill="1"] {
	overflow: hidden;
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;	
}

.vimeo--video[data-fill="1"] iframe {
	position: absolute;
}

.has-fixed-background {
	background-attachment: fixed;
}

@supports (-webkit-overflow-scrolling: touch) {
	.has-fixed-background {
		background-attachment: scroll; 
	}
}

@media (prefers-reduced-motion: reduce) {
	.has-fixed-background {
		background-attachment: scroll; 
	}
}

.grecaptcha-badge{
	visibility: collapse !important;
}

.recaptcha--terms {
	font-size: .6rem;
}

/* ---------------------------------------------------------------------------
   CookieYes banner vs AudioEye launcher
   Both are fixed bottom-right. AudioEye's launcher (#ae_launcher, 44x48px,
   24px in from the right) paints OVER the banner and swallows clicks on the
   right end of "Accept All" (desktop) or "Reject All" (phones). Neither vendor
   has a setting for it, so the banner's buttons keep a gutter clear of the
   launcher. AudioEye loads on every page (Site Settings → Integration →
   Footer Scripts), so this is not scoped to its presence. docs/cookieyes.md
   --------------------------------------------------------------------------- */
:root {
	--dy-ae-launcher-clearance: 56px; /* 44px launcher + air, past the banner's own 24px inset */
}

.cky-consent-container .cky-notice-btn-wrapper {
	margin-right: var(--dy-ae-launcher-clearance);
}

/* The preference centre only reaches the launcher while it fills the screen.
   Padding, not margin, so the wrapper's border-top still runs edge to edge;
   24px is CookieYes's own inset ("prefrence" is its spelling). */
.cky-modal .cky-prefrence-btn-wrapper {
	padding-right: calc(24px + var(--dy-ae-launcher-clearance));
}

/* 577px deliberately tracks CookieYes, not the Bootstrap ladder: its
   preference centre goes full-screen at (max-width: 576px) OR
   (max-height: 576px), so the gutter is needed until BOTH exceed 576px. */
@media (min-width: 577px) and (min-height: 577px) {
	.cky-modal .cky-prefrence-btn-wrapper {
		padding-right: 24px;
	}
}

.single-post--content-width > [data-blockname], 
.single-post--content-width > .alignfull {
	margin-left: -15px;
	margin-right: -15px;
}

@media (min-width: 780px) {
	.single-post--content-width > [data-blockname],
	.single-post--content-width > .alignfull {
		margin-left: calc(50% - 50vw);
		margin-right: calc(50% - 50vw);
		margin-left: calc(50% - ((100vw - var(--scrollbar-width)) / 2));
		margin-right: calc(50% - ((100vw - var(--scrollbar-width)) / 2));
	}
}

.main--section > p,
.main--section > .wp-block-heading,
.main--section > .wp-block-list {
	max-width: var(--wp--style--global--content-size, 900px);
	margin: 0 auto 1em;
	padding: 0 30px;
}

.main--section > .wp-block-list {
	padding-left: calc(30px + 1em);
}

/* ---------------------------------------------------------------------------
   Scroll reveal
   Both classes are added by js/reveal.js — NOTHING here applies at first paint.
   That is deliberate: Chrome excludes opacity:0 elements from LCP, so hiding
   content in CSS and un-hiding it in JS delays LCP at every viewport height.
   reveal.js arms only units that are already below the fold, so anything a
   visitor can see on load was never hidden. See the header of that file.

   It also means there are no editor-canvas guards to write (trap 10): the
   front-end script never runs in the block editor, so nothing there is armed.

   No will-change — a page here carries ~200 units, and promoting that many
   compositor layers at once costs real mobile memory for no gain; opacity and
   transform already composite without the hint.
---------------------------------------------------------------------------- */

.is-reveal-armed {
	opacity: 0;
	transform: translateY(24px);
	transition: opacity 0.8s ease, transform 0.8s ease;
}

.is-reveal-armed.is-revealed {
	opacity: 1;
	transform: none;
}

/* Belt and braces. reveal.js already bails before arming anything under
   reduced motion, so this only matters if the setting is changed after load. */
@media (prefers-reduced-motion: reduce) {
	.is-reveal-armed {
		opacity: 1;
		transform: none;
		transition: none;
	}
}
