/*
 * Styles for the hand-built patterns — the Elementor widgets with no core block
 * equivalent. Everything a core block can already express lives in theme.json
 * instead; nothing here overrides a core block's own styling.
 *
 * Tokens only. Every colour, size and space is a theme.json preset var, so a
 * change to the palette or the scale reaches these patterns without an edit.
 */

/* ------------------------------------------------------------------ reset */
/*
 * The theme never set box-sizing, so it was the CSS default: content-box. That
 * is not a cosmetic default — it is what broke the 50/50 grid on every split
 * section on the site.
 *
 * A media/text band is two core/column blocks at `flex-basis: 50%`. Under
 * content-box the text column's `--adsbox-col-pad` (5vw = 73.5px a side at
 * 1470px) sits OUTSIDE its basis, so the row's items sum to 100% + 147px, the
 * row overflows, and flex-shrink takes the overflow off both columns equally.
 * Measured on /our-services/ at 1470px: 808.5 / 661.5 — a 55/45 split where
 * production is exactly 735 / 735. Every padded box on the site was off by its
 * own padding in the same way.
 *
 * border-box is also what core's own block CSS is written against, so this is
 * a correction rather than an opinion.
 */
*,
*::before,
*::after {
	box-sizing: border-box;
}

/*
 * Every <img> the assembler emits now carries its INTRINSIC width and height,
 * read out of the file in uploads-old/ (scripts/lib/imgdims.py). That is what
 * lets the browser reserve the box before the bytes arrive, which is the CLS
 * half of `Lighthouse mobile >= 90`.
 *
 * It is also a trap. An HTML `height` is a real presentational hint: with a CSS
 * `width: 100%` and no `height: auto`, the image renders at its CSS width and
 * its HTML height, and every photograph on the site squashes. The wrapper rules
 * further down set `height: auto` on the images they own — this is the floor
 * under the ones nobody owns.
 *
 * `aspect-ratio: auto <w> / <h>` is what the browser derives from the two
 * attributes; stating `height: auto` keeps that ratio in charge instead of the
 * literal pixel height. `object-fit` wrappers (.adsbox-col-media, the flip
 * photos) override this on purpose and are unaffected — they set their own
 * height and this rule loses to their specificity.
 */
img {
	max-width: 100%;
	height: auto;
}

/* ---------------------------------------------------------------- eyebrow */

.adsbox-eyebrow {
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 500;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--wp--preset--color--accent);
	margin-bottom: var(--wp--preset--spacing--20);
}

/* -------------------------------------------------------------- flip card */
/*
 * Replaces Elementor's flip-box, 66 instances — the single largest non-trivial
 * widget on the site, and the whole of the About page's team grid.
 *
 * It is NOT a flip, and calling it one is what produced fifteen empty cream
 * boxes with a name in them. Measured from the widget's own settings:
 * `flip_effect` is `fade`, sides A and B carry the SAME photograph, and side B
 * adds `background_overlay_b: #181818CC`. So the card is a portrait that
 * darkens on hover to show who it is.
 *
 * The overlay text is in the DOM at all times — indexable, and reachable by a
 * screen reader — and the card reveals on :focus-within as well as :hover, so a
 * keyboard reaches the name and the LinkedIn link. The Elementor widget did
 * neither.
 */

.adsbox-flip {
	position: relative;
	overflow: hidden;
	border-radius: 1.25rem;
	background: var(--wp--preset--color--cream);
	isolation: isolate;
}

.adsbox-flip:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 4px;
}

.adsbox-flip__photo,
.adsbox-flip__photo img {
	display: block;
	width: 100%;
	margin: 0;
	height: 100%;
}

.adsbox-flip__photo {
	aspect-ratio: 3 / 4;
}

.adsbox-flip__photo img {
	object-fit: cover;
	transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.adsbox-flip:hover .adsbox-flip__photo img,
.adsbox-flip:focus-within .adsbox-flip__photo img {
	transform: scale(1.04);
}

/* #181818CC — production's own overlay, alpha and all. */
.adsbox-flip__overlay {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	gap: var(--wp--preset--spacing--10);
	margin: 0;
	padding: var(--wp--preset--spacing--40);
	background: rgba(24, 24, 24, 0.8);
	color: var(--wp--preset--color--base);
	opacity: 0;
	transition: opacity 0.35s ease;
}

.adsbox-flip:hover .adsbox-flip__overlay,
.adsbox-flip:focus-within .adsbox-flip__overlay {
	opacity: 1;
}

.adsbox-flip__overlay :is(h1, h2, h3, h4, h5, h6, p, a) {
	color: inherit;
	margin: 0;
}

.adsbox-flip__overlay :is(h1, h2, h3, h4, h5, h6) {
	font-size: var(--wp--preset--font-size--large);
	letter-spacing: -0.02em;
}

.adsbox-flip__overlay .wp-block-buttons {
	margin-top: var(--wp--preset--spacing--20);
}

.adsbox-flip__link .wp-block-button__link {
	background: transparent;
	border: 1px solid currentColor;
	color: inherit;
	padding: 0.4rem 1rem;
	font-size: var(--wp--preset--font-size--x-small);
}

.adsbox-flip__link .wp-block-button__link:hover,
.adsbox-flip__link .wp-block-button__link:focus-visible {
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
}

/*
 * A card whose source carries no photograph — 4 of the 66, all in library
 * templates. Hiding its only content behind a hover on an empty box would be a
 * regression, so the panel is always on and sits on the cream ground.
 */
.adsbox-flip--nophoto {
	min-height: 18rem;
}

.adsbox-flip--nophoto .adsbox-flip__overlay {
	position: static;
	opacity: 1;
	background: transparent;
	color: var(--wp--preset--color--contrast);
	justify-content: center;
	min-height: inherit;
}

/*
 * A fade is not a rotation, but it is still motion under the pointer. With
 * reduced motion requested the reveal is instant and the photograph does not
 * scale; no content is lost either way, because the overlay text is always in
 * the DOM.
 */
@media (prefers-reduced-motion: reduce) {
	.adsbox-flip__overlay,
	.adsbox-flip__photo img {
		transition: none;
	}

	.adsbox-flip:hover .adsbox-flip__photo img,
	.adsbox-flip:focus-within .adsbox-flip__photo img {
		transform: none;
	}
}

/* ------------------------------------------------------ before/after slider */
/*
 * Replaces exad-image-comparison, 29 instances. A range input drives one CSS
 * custom property; the overlay is clipped to it. The range input IS the control,
 * so keyboard and screen-reader support come from the platform rather than from
 * a script.
 */

/*
 * `margin: 0` on every child is load-bearing, not tidying. The group renders as
 * `is-layout-flow`, which gives every sibling after the first
 * `margin-block-start: var(--wp--style--block-gap)` — 24px — and an absolutely
 * positioned element still honours its margin. So the divider sat 24px BELOW
 * the line the clip-path cut, and a 24px band of the "after" image showed above
 * the handle. Computed `top` said 475.2 and `offsetTop` said 499: the gap
 * between those two numbers is the whole bug.
 */
.adsbox-compare > * {
	margin: 0;
}

.adsbox-compare {
	--adsbox-compare-pos: 50%;
	position: relative;
	overflow: hidden;
	border-radius: 1.25rem;
	background: var(--wp--preset--color--cream);
	touch-action: none;
}

.adsbox-compare img {
	display: block;
	width: 100%;
	height: auto;
}

/*
 * Both halves must be the SAME box, or the clip reveals the wrong pixels near
 * the divider. The "after" layer is a core/group wrapping a core/image, and
 * every one of those three elements has to be told to fill — a figure is a
 * shrink-to-fit box and .wp-block-image carries a bottom margin, which is how
 * the overlay ended up 480px tall inside a 504px frame and the join stopped
 * lining up.
 */
.adsbox-compare__after {
	position: absolute;
	inset: 0;
	margin: 0;
	clip-path: inset(0 0 0 var(--adsbox-compare-pos));
}

.adsbox-compare > .wp-block-image,
.adsbox-compare__after > .wp-block-image {
	inline-size: 100%;
	block-size: 100%;
	margin: 0;
}

.adsbox-compare__after img {
	block-size: 100%;
	inline-size: 100%;
	object-fit: cover;
}

.adsbox-compare__handle {
	position: absolute;
	inset-block: 0;
	left: var(--adsbox-compare-pos);
	width: 2px;
	background: var(--wp--preset--color--base);
	pointer-events: none;
	box-shadow: 0 0 0 1px rgba(24, 24, 24, 0.25);
}

/* The ring is the widget's own geometry: `exad_image_comparison_handler_width`
   and `_height` are 35px and `_border` is 4px on every instance. */
.adsbox-compare__handle::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 35px;
	height: 35px;
	transform: translate(-50%, -50%);
	border: 4px solid var(--wp--preset--color--base);
	border-radius: 50%;
	background: rgba(24, 24, 24, 0.35);
}

/*
 * `pointer-events: none` is load-bearing. The input covers the whole slider at
 * `opacity: 0` to stay the accessible control, and while it took pointer events
 * it ALSO ran its own native drag — which reads X. On a vertical slider that
 * meant two handlers writing the same custom property from two different axes
 * on every move, and the divider stuttered and trailed the cursor. compare.js
 * owns the pointer now; the input keeps the keyboard, which pointer-events does
 * not touch.
 */
.adsbox-compare__range {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	margin: 0;
	opacity: 0;
	pointer-events: none;
	appearance: none;
	-webkit-appearance: none;
	background: transparent;
}

.adsbox-compare {
	cursor: ew-resize;
}

.adsbox-compare--vertical {
	cursor: ns-resize;
}

.adsbox-compare__range:focus-visible + .adsbox-compare__handle::after {
	outline: 3px solid var(--wp--preset--color--accent);
	outline-offset: 3px;
}

/*
 * Vertical: the divider is horizontal and moves up and down. This is what
 * production does on ALL 29 instances — `data-exad-oriantation="vertical"`,
 * measured from the archived HTML, not from the exad setting, which is absent.
 * The first rebuild took the widget's left/right default, so every slider on
 * the site read as a different component from the one it replaced.
 *
 * Only the axis changes: same custom property, same range input, same handle.
 */
.adsbox-compare--vertical .adsbox-compare__after {
	clip-path: inset(var(--adsbox-compare-pos) 0 0 0);
}

.adsbox-compare--vertical .adsbox-compare__handle {
	inset-block: auto;
	inset-inline: 0;
	top: var(--adsbox-compare-pos);
	width: auto;
	height: 2px;
}

/* Production's handle (exad `handle-style-2`) is a ring carrying an up/down
   chevron pair. On a vertical slider that arrow is the only thing telling a
   visitor which way to drag, so it is not decoration. An inline SVG mask rather
   than an image file: it is 200 bytes, it inherits the handle's colour, and it
   is not a request. */
.adsbox-compare--vertical .adsbox-compare__handle::after {
	z-index: 0;
	background: rgba(24, 24, 24, 0.28);
}

.adsbox-compare--vertical .adsbox-compare__handle::before {
	content: "";
	position: absolute;
	z-index: 1;
	top: 50%;
	left: 50%;
	width: 0.85rem;
	height: 1.15rem;
	transform: translate(-50%, -50%);
	background: var(--wp--preset--color--base);
	-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 16"><path d="M6 0 11 5H1z"/><path d="M6 16 1 11h10z"/></svg>') center / contain no-repeat;
	mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 16"><path d="M6 0 11 5H1z"/><path d="M6 16 1 11h10z"/></svg>') center / contain no-repeat;
}

.adsbox-compare--vertical .adsbox-compare__label--a {
	inset: 0.75rem auto auto 0.75rem;
}

.adsbox-compare--vertical .adsbox-compare__label--b {
	inset: auto 0.75rem 0.75rem auto;
}

/* ------------------------------------------------------- animated card video */
/*
 * The 27 animated service-card GIFs are 595 MiB; the same frames as H.264 are
 * 23.8 MiB. `muted` and `playsinline` are not optional — iOS refuses to autoplay
 * without both. H.264 has no alpha channel, so the card paints a solid ground
 * behind the video rather than relying on the GIF's transparency.
 */

.adsbox-card-video {
	position: relative;
	overflow: hidden;
	border-radius: 1.25rem;
	background: var(--wp--preset--color--cream);
}

.adsbox-card-video video,
.adsbox-card-video img {
	display: block;
	width: 100%;
	height: auto;
	background: var(--wp--preset--color--cream);
}

/*
 * With reduced motion requested the video is replaced by its own poster frame.
 * `autoplay` is still in the markup for browsers that ignore the query, so the
 * rule also pauses via `animation-play-state`-free means: the poster image is
 * shown on top and the video is hidden from the accessibility tree.
 */
@media (prefers-reduced-motion: reduce) {
	.adsbox-card-video video {
		display: none;
	}

	.adsbox-card-video .adsbox-card-video__poster {
		display: block;
	}
}

.adsbox-card-video .adsbox-card-video__poster {
	display: none;
}

/* ------------------------------------------------------------ logo marquee */
/*
 * Replaces the 14-logo client carousel, which on production is a complete
 * pasted <!DOCTYPE html> tutorial document nested inside the homepage, linking
 * a stylesheet that 404s on every load (defect 13).
 *
 * The track is duplicated once and translated by exactly -50%, which is what
 * makes the loop seamless. The duplicate is aria-hidden so a screen reader
 * reads each logo once.
 */

.adsbox-marquee {
	overflow: hidden;
	mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.adsbox-marquee__track {
	display: flex;
	width: max-content;
	align-items: center;
	gap: var(--wp--preset--spacing--70);
	animation: adsbox-marquee 40s linear infinite;
}

.adsbox-marquee:hover .adsbox-marquee__track {
	animation-play-state: paused;
}

/* Production's logos sit directly on the band at roughly 3.5rem — the pasted
   tutorial's own stylesheet 404s there, so they render at a size nothing
   constrains. 2.5rem on a cream ground under an eyebrow read as a footnote. */
.adsbox-marquee img {
	height: 3.5rem;
	width: auto;
	flex: 0 0 auto;
	filter: grayscale(1);
	opacity: 0.65;
	transition: filter 0.2s, opacity 0.2s;
}

.adsbox-marquee img:hover {
	filter: none;
	opacity: 1;
}

@keyframes adsbox-marquee {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
	.adsbox-marquee {
		overflow-x: auto;
		scroll-snap-type: x mandatory;
	}

	.adsbox-marquee__track {
		animation: none;
	}
}

/* ---------------------------------------------------------------- stats row */
/*
 * Production floats three white cards ON the hero photograph: the row carries
 * margin-top -180px under a 400px-tall background image, and each card is a
 * shadowed white panel with an accent rule, a large INK numeral and an
 * uppercase caption. The first rebuild made it a flat cream band of pink
 * numbers, which loses the one composition on the page anybody would recognise.
 *
 * The numerals are ink (#181818), not accent: the accent is the rule.
 */

.adsbox-stats {
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--30);
}

/*
 * core's grid layout emits `repeat(auto-fill, minmax(16rem, 1fr))`, and
 * auto-FILL keeps the empty tracks: three cards in a 1392px band filled the
 * first three of five and huddled against the left edge with a third of the
 * band empty beside them. auto-fit collapses the empty tracks so the three
 * stretch across the band, which is what production does.
 */
.adsbox-stats > .wp-block-group.is-layout-grid {
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

/* The rule inside a card is the card's own accent, not a band separator: it
   sits tight against the numeral rather than on the section's rhythm. */
.adsbox-stat .adsbox-rule {
	width: 100px;
	max-width: 50%;
	margin-block: 0;
}

.adsbox-stat {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--wp--preset--spacing--20);
	min-height: 15rem;
	padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--30);
	background: var(--wp--preset--color--base);
	border-radius: 1.5rem;
	box-shadow: 0 1.5rem 3rem rgba(24, 24, 24, 0.12);
}

.adsbox-stat > * {
	margin-block: 0;
}

.adsbox-stat__value {
	font-size: clamp(3.5rem, 6vw, 6rem);
	font-weight: 700;
	line-height: 1;
	letter-spacing: -0.03em;
	text-transform: uppercase;
	color: var(--wp--preset--color--contrast);
	font-variant-numeric: tabular-nums;
}

.adsbox-stat__label {
	font-size: var(--wp--preset--font-size--small);
	text-transform: uppercase;
	color: var(--wp--preset--color--contrast);
}

/* ---------------------------------------------------------------- the rule */
/*
 * Elementor's divider widget, 87 instances: the short thick bar under every
 * heading and over every statistic on the site. Colour and geometry arrive as
 * class modifiers from layout._rule(), because core/separator serialises its
 * own colour and a hand-written style attribute would flag every one of them as
 * invalid content the first time the page was opened in the editor.
 *
 * Measured across the 74 documents: 42 bark, 37 accent, 4 cream, 7 one-offs;
 * 46 at 13x26px (a stub used as a bullet), 21 at 163x12px, 16 at 25% x 12px.
 */

.adsbox-rule {
	width: 163px;
	max-width: 60%;
	height: 12px;
	border: 0;
	margin-inline: auto;
	margin-block: var(--wp--preset--spacing--20);
	background: currentColor;
	opacity: 1;
}

.adsbox-rule--accent   { background: var(--wp--preset--color--accent); }
.adsbox-rule--bark     { background: var(--wp--preset--color--bark); }
.adsbox-rule--cream    { background: var(--wp--preset--color--cream); }
.adsbox-rule--contrast { background: var(--wp--preset--color--contrast); }
.adsbox-rule--base     { background: var(--wp--preset--color--base); }

.adsbox-rule--quarter { width: 25%; max-width: none; }
.adsbox-rule--stub    { width: 13px; height: 26px; max-width: none; }

.adsbox-rule--left  { margin-inline: 0 auto; }
.adsbox-rule--right { margin-inline: auto 0; }

/*
 * Elementor's divider `gap` — the space above and below the bar. 68 of the 91
 * dividers on the site declare 0 and 20 declare 2, so the rule is drawn tight
 * against what follows it. The rebuild gave every one 16px of its own margin
 * and then core's flow layout added another 24px of blockGap to the heading
 * underneath, which is most of why the /contact/ information band stood 262px
 * tall against production's ~158.
 */
.adsbox-rule--tight {
	margin-block: 2px;
}

.adsbox-rule--tight + * {
	margin-block-start: 0.5rem;
}

@media (max-width: 781px) {
	.adsbox-rule { width: 93px; }
}

/*
 * The band-level "loose content gets the measure and a gutter" rule would give
 * the rule a 1200px box and centre it inside that; it is a 163px bar and needs
 * neither. Kept narrow enough that it cannot swallow a real separator.
 */
.adsbox-section > .adsbox-rule {
	padding-inline: 0;
}

/* ----------------------------------------------------------------- card box */

.adsbox-card {
	border-radius: 1.25rem;
	background: var(--wp--preset--color--shell);
	padding: var(--wp--preset--spacing--40);
}

.adsbox-card--outline {
	background: transparent;
	border: 1px solid var(--wp--preset--color--hairline);
}

/* ---------------------------------------------------------------- accordion */

.adsbox-faq .wp-block-details {
	border-bottom: 1px solid var(--wp--preset--color--hairline);
	padding-block: var(--wp--preset--spacing--30);
}

.adsbox-faq .wp-block-details summary {
	cursor: pointer;
	font-size: var(--wp--preset--font-size--large);
	font-weight: 500;
	letter-spacing: -0.02em;
	list-style: none;
}

.adsbox-faq .wp-block-details summary::-webkit-details-marker {
	display: none;
}

.adsbox-faq .wp-block-details summary::after {
	content: "+";
	float: right;
	font-weight: 400;
	line-height: 1;
}

.adsbox-faq .wp-block-details[open] summary::after {
	content: "\2013";
}

/* ------------------------------------------------------------------- header */
/*
 * Production's header (elementor_library 4085): sticky to the top on every
 * breakpoint, white, a soft shadow, min-height 74px, boxed at 1860px with 2%
 * inline padding, and a row of four things — logo, menu, booking button,
 * language flag. The first rebuild had none of the sticking, a 110px logo and
 * no switcher at all.
 */

/*
 * The sticky goes on the template part's own <header>, not on the group inside
 * it. A sticky element only travels within its containing block, and that
 * wrapper's height IS the header — so `position: sticky` on .adsbox-header
 * computed correctly, reported `sticky`, and stuck to nothing. The bar simply
 * scrolled away like a static one.
 */
.wp-site-blocks > header.wp-block-template-part {
	position: sticky;
	top: 0;
	z-index: 99;
	background: var(--wp--preset--color--base);
	box-shadow: 0 1px 0 rgba(24, 24, 24, 0.08), 0 6px 18px rgba(24, 24, 24, 0.04);
}

/*
 * `add_theme_support('wp-block-styles')` gives any group with a background
 * `padding: 1.25em 2.375em` — the same trap `.adsbox-section.has-background`
 * already opts out of. Here it added 24px above and below an 80px bar and made
 * the header 128px against production's ~78. Nothing in the markup says 128.
 */
.adsbox-header,
.adsbox-header.has-background {
	padding: 0;
}

/*
 * The root flow layout gives every child of .wp-site-blocks a 24px
 * `margin-block-start` from theme.json's blockGap, and <main> is one of those
 * children — so a 24px white stripe sat between the sticky header and the top
 * of every page. On /contact/ that is a band of white above the hero
 * photograph, which butts against the header on production.
 *
 * Zeroed on <main> and the footer only: the gap is right *inside* a page and
 * wrong between the page and its chrome.
 */
.wp-site-blocks > main,
.wp-site-blocks > footer.wp-block-template-part {
	margin-block-start: 0;
}

.adsbox-header__bar {
	max-width: 1860px;
	margin-inline: auto;
	min-height: 74px;
	padding-inline: 2%;
	gap: 2%;
}

/* 153x80, object-fit: contain — the logo file is 660x649, so a width-only rule
   makes it a tall square that pushes the bar to twice its height. */
.adsbox-header .wp-block-site-logo img {
	width: 153px;
	height: 80px;
	object-fit: contain;
	object-position: left center;
}

/* The menu is the flexible middle: it takes the space the logo and the
   right-hand cluster do not, and centres inside it — production's
   "stretch + centre". */
.adsbox-header .adsbox-nav {
	flex: 1 1 auto;
	justify-content: center;
}

.adsbox-header .adsbox-nav .wp-block-navigation__container {
	gap: clamp(1.5rem, 3.6vw, 70px);
}

.adsbox-header__end {
	flex: 0 0 auto;
	gap: var(--wp--preset--spacing--20);
}

.adsbox-header__end .wp-block-buttons {
	margin: 0;
}

/*
 * The header button keeps the size it had before the page buttons were raised
 * to production's 18px, and that is a deliberate exception rather than an
 * oversight. theme.json sets one button style for the whole site; 18px is right
 * for a call to action a visitor is meant to land on, and too heavy for a chip
 * sitting in a 72px bar next to the nav and the language switcher, where it
 * starts crowding the logo.
 *
 * Scoped to .adsbox-header__end, so every button on every page keeps the new
 * scale. The mobile override further down this file still wins below 782px.
 */
.adsbox-header__end .wp-block-button__link {
	font-size: var(--wp--preset--font-size--small);
	padding: 0.7rem 1.5rem;
}

/* Site nav. The underline theme.json puts on every link is right for body copy
   and wrong here; production draws a 1px pointer under the active item. */
.adsbox-nav .wp-block-navigation-item__content {
	text-decoration: none;
	padding-block: 3px;
	border-block-end: 1px solid transparent;
}

.adsbox-nav .wp-block-navigation-item__content:hover,
.adsbox-nav .wp-block-navigation-item__content:focus-visible {
	color: var(--wp--preset--color--accent);
	border-block-end-color: currentColor;
}

.adsbox-nav .current-menu-item > .wp-block-navigation-item__content {
	color: var(--wp--preset--color--accent);
	border-block-end-color: currentColor;
}

/* Submenu. Production's Bulgarian header nests the UGC page under Услуги —
   an arrow beside the parent, a panel on hover. core/navigation ships the
   behaviour and the arrow; core's default panel is a white box with a hairline
   border and 0.5em padding, which reads as a browser widget rather than as
   this site. Restyled to the header's own type and ink. */
.adsbox-nav .wp-block-navigation__submenu-container {
	min-width: max-content;
	padding: 0.75rem 0;
	border: 0;
	border-radius: 4px;
	background-color: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	box-shadow: 0 12px 32px rgba(24, 24, 24, 0.14);
}

.adsbox-nav .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	padding: 0.4rem 1.25rem;
	border-block-end: 0;
	white-space: nowrap;
}

/* The parent's own underline is the header's hover affordance and belongs to
   the label, not to the label plus its arrow. */
.adsbox-nav .wp-block-navigation__submenu-icon {
	padding: 0 0 0 0.4rem;
	background: transparent;
	border: 0;
	color: inherit;
}

/* The overlay is a stacked list, so the panel is inline there rather than a
   floating box — core handles the position; this stops it inheriting the
   desktop panel's shadow and ground. */
.adsbox-nav .wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__submenu-container {
	padding: 0;
	background: transparent;
	box-shadow: none;
}

/* --------------------------------------------------------- language switch */
/*
 * Production's polylang widget with `hide_current: yes` and no language name:
 * one flag, 4:3, linking to the translation of the page you are on. Rendered by
 * [adsbox_lang_switcher] in inc/chrome.php, which is Polylang-first and falls
 * back to the pairs the WXR already carries.
 */

.adsbox-langs__link {
	display: inline-flex;
	align-items: center;
	line-height: 0;
	text-decoration: none;
	border-radius: 2px;
	box-shadow: 0 0 0 1px rgba(24, 24, 24, 0.15);
	transition: transform 0.2s ease;
}

.adsbox-langs__link:hover,
.adsbox-langs__link:focus-visible {
	transform: scale(1.08);
}

.adsbox-langs__flag svg {
	display: block;
	width: 24px;
	height: auto;
	border-radius: 2px;
}

/* -------------------------------------------------------------- footer */
/*
 * Production's footer (elementor_library 690) is a single row, aligned to the
 * top, space-between: the mailing-list form at 48%, the INFORMATION menu, then
 * "Follow us:" with five outlined circular icons. It carries no rule and no
 * copyright line — the first rebuild invented both, stacked the form full width
 * and put a pink submit button under it.
 */

.adsbox-footer {
	padding-block: var(--wp--preset--spacing--60);
}

.adsbox-footer__bar {
	max-width: 1860px;
	margin-inline: auto;
	padding-inline: 4%;
	gap: var(--wp--preset--spacing--50);
	align-items: flex-start;
}

/* 48% on production, and it must not grow into the link columns: the form is
   the widest thing in the row and flex-grow lets it eat the space the two
   menus need. */
.adsbox-footer__form {
	flex: 0 1 48%;
	min-width: min(100%, 20rem);
	max-width: 40rem;
}

.adsbox-footer__col {
	flex: 0 1 auto;
}

.adsbox-footer__col > h2 {
	font-weight: 500;
	text-transform: uppercase;
	letter-spacing: -0.03em;
	margin-block-end: var(--wp--preset--spacing--30);
}

/* Production stacks this column: the heading sits above the icon row, like
   INFORMATION above its links. Laying it out inline made the column ~600px
   wide, which wrapped the three-column bar onto two lines. */
.adsbox-footer__social .wp-block-social-links {
	gap: 7px;
}

/* Production draws filled ink discs with a white glyph (Elementor's
   shape-circle social-icons widget), ~26px across — the icon row is about as
   wide as the "Follow us:" heading above it, which is how production scales it. Core's icon-background-color
   paints the disc; only the geometry and the hover are here. */
.adsbox-social .wp-block-social-link {
	border-radius: 50%;
	transition: background-color 0.2s ease;
}

.adsbox-social .wp-block-social-link a {
	padding: 5px;
}

.adsbox-social .wp-block-social-link:hover,
.adsbox-social .wp-block-social-link:focus-within {
	background-color: var(--wp--preset--color--accent);
}

.adsbox-footer-nav {
	font-weight: 500;
}

.adsbox-footer-nav .wp-block-navigation__container {
	gap: var(--wp--preset--spacing--30);
}

.adsbox-footer-nav .wp-block-navigation-item__content {
	text-decoration: none;
	text-transform: uppercase;
	letter-spacing: -0.03em;
}

.adsbox-footer-nav .wp-block-navigation-item__content:hover,
.adsbox-footer-nav .wp-block-navigation-item__content:focus-visible {
	color: var(--wp--preset--color--accent);
}

/* ------------------------------------------------------------- skip to main */

.adsbox-skip-link {
	position: absolute;
	left: -9999px;
	z-index: 999;
	padding: 0.75rem 1.25rem;
	background: var(--wp--preset--color--contrast);
	color: var(--wp--preset--color--base);
}

.adsbox-skip-link:focus {
	left: 1rem;
	top: 1rem;
}

/* ------------------------------------------------------------ blog surface */
/*
 * Phase 4b. The article surface is a deliverable, not a detail: the whole
 * project exists to enable an SEO content operation, and an article that reads
 * badly is a page nobody finishes.
 */

.adsbox-lead {
	color: var(--wp--preset--color--contrast);
	line-height: 1.4;
	letter-spacing: -0.02em;
}

.adsbox-byline {
	color: var(--wp--preset--color--bark);
	align-items: baseline;
}

.adsbox-byline .wp-block-post-author-name a {
	text-decoration: none;
}

/* Separates author from date without an extra element in the markup. */
.adsbox-byline > * + *::before {
	content: "·";
	margin-inline-end: 0.5em;
	opacity: 0.5;
}

/*
 * Measure. Article body copy is capped by single.html's 720px contentSize; this
 * only relaxes the heading rhythm so an H2 is not glued to the paragraph above.
 */
.adsbox-article-head + * {
	margin-top: var(--wp--preset--spacing--40);
}

.wp-block-post-content > :is(h2, h3) {
	margin-top: var(--wp--preset--spacing--50);
}

.adsbox-pullquote {
	border-left: 3px solid var(--wp--preset--color--accent);
	padding-left: var(--wp--preset--spacing--30);
	margin-block: var(--wp--preset--spacing--50);
	font-size: var(--wp--preset--font-size--large);
	line-height: 1.35;
}

.adsbox-pullquote p {
	font-size: inherit;
}

/* ------------------------------------------------------------ post teasers */

.adsbox-teaser .wp-block-post-title a {
	text-decoration: none;
}

.adsbox-teaser .wp-block-post-title a:hover,
.adsbox-teaser .wp-block-post-title a:focus-visible {
	text-decoration: underline;
	text-decoration-color: var(--wp--preset--color--accent);
}

.adsbox-teaser__date,
.adsbox-teaser .wp-block-post-date {
	color: var(--wp--preset--color--bark);
}

/* ---------------------------------------------------------- prev/next links */

.wp-block-post-navigation-link {
	color: var(--wp--preset--color--bark);
}

.wp-block-post-navigation-link a {
	text-decoration: none;
}

.wp-block-post-navigation-link a:hover,
.wp-block-post-navigation-link a:focus-visible {
	text-decoration: underline;
}

/* ------------------------------------------------------------------ search */

.wp-block-search__input {
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 9999px;
	padding: 0.8rem 1.25rem;
	font-family: inherit;
	font-size: var(--wp--preset--font-size--small);
}

.wp-block-search__button {
	border: 0;
	border-radius: 9999px;
	background: var(--wp--preset--color--contrast);
	color: var(--wp--preset--color--base);
	padding: 0.8rem 1.75rem;
	font-family: inherit;
	font-size: var(--wp--preset--font-size--small);
	font-weight: 500;
	cursor: pointer;
}

.wp-block-search__button:hover,
.wp-block-search__button:focus-visible {
	background: var(--wp--preset--color--accent);
}

/* ------------------------------------------------ Phase 6 additions
 *
 * Two things the assembled pages need that no pattern file declares.
 * Everything else Phase 6 emits reuses the class names above, deliberately:
 * the first version of the assembler invented its own, so 26 of the 31 classes
 * it wrote had no rule anywhere. The markup was valid, every structural check
 * passed, and the sections rendered as unstyled body text. Structure checks do
 * not see CSS.
 */

/* The comparison slider's labels. patterns/image-compare.php carries them only
 * as an aria-label, which leaves a sighted visitor with no idea which half is
 * which — and left the words out of post_content entirely, which check 3b
 * reported as lost content. It was right on both counts. */
/*
 * The labels are a hover affordance, not a caption. Production's server HTML
 * carries them only as `data-exad-before_label` / `-after_label`; exad's script
 * paints them into an overlay that appears when the pointer is on the slider
 * (`exad_overlay_enable: on`, `exad_overlay_color: rgba(0,0,0,0.37)`), so a
 * visitor looking at the page sees two photographs and a divider and nothing
 * else. They stay real text in post_content — a label only a data attribute
 * knows about is not a label — and they are revealed on hover and on keyboard
 * focus rather than being hidden from everyone.
 */
.adsbox-compare__label {
	position: absolute;
	bottom: 0.75rem;
	z-index: 2;
	opacity: 0;
	transition: opacity 150ms ease;
	padding: 0.25rem 0.6rem;
	border-radius: 999px;
	background: color-mix(in srgb, var(--wp--preset--color--contrast) 72%, transparent);
	color: var(--wp--preset--color--base);
	font-size: var(--wp--preset--font-size--x-small, 0.78rem);
	letter-spacing: 0.02em;
	pointer-events: none;
}

.adsbox-compare__label--a { left: 0.75rem; }
.adsbox-compare__label--b { right: 0.75rem; }

.adsbox-compare:hover .adsbox-compare__label,
.adsbox-compare:focus-within .adsbox-compare__label {
	opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
	.adsbox-compare__label {
		transition: none;
	}
}

/* --------------------------------------------------------------------- forms
 *
 * Fluent Forms ships its own stylesheet; these rules only make it inherit the
 * theme's type, colour and spacing so a form does not read as a bolted-on
 * plugin. Everything is scoped under .adsbox-form, which is the group the
 * assembler wraps every [fluentform] shortcode in.
 */

/*
 * The geometry below is production's own, read from the Elementor form widget
 * on /contact/ (page-698) rather than invented: fields are `width: 50` two-up,
 * `column_gap`/`row_gap` are 35px, the control is transparent with a single
 * 1px #C8C8C8 bottom border and no radius, the label is 20px/400 uppercase at
 * 1px tracking, and the submit is a black 25px pill centred under the form
 * that turns accent-pink on hover. The first rebuild shipped boxed cream
 * inputs in one stacked column with a pink left-aligned button — every one of
 * those was a default, not a reading of the source.
 *
 * The section container is `padding: 0 140px`, so the form is 1160px wide at
 * the 1440px desktop, not the 44rem the first cut capped it at.
 */
.adsbox-form {
	margin-block: 50px;
	max-width: 72.5rem;
	margin-inline: auto;
	padding-block-end: var(--wp--preset--spacing--60);
}

.adsbox-form .fluentform .ff-el-group {
	margin-block-end: 35px;
}

/* Fluent Forms' own column container. The cells carry an inline `flex-basis: 50%`
   which a stylesheet cannot outrank, so the 35px `column_gap` the widget declares
   has to come from padding inside the cell, not from `gap`: Fluent Forms' own
   15px gap made two 50% cells sum to 1127px inside a 1112px row, they wrapped,
   and each then grew back to the full width — a two-column form that rendered as
   one stacked column with every check still green. */
.adsbox-form .fluentform .ff-t-container {
	display: flex;
	flex-wrap: wrap;
	gap: 0;
	width: auto;
	margin-inline: -17.5px;
}

.adsbox-form .fluentform .ff-t-container > .ff-t-cell {
	padding-inline: 17.5px;
	min-width: min(100%, 14rem);
	flex-grow: 1;
}

.adsbox-form .fluentform .ff-el-input--label label {
	font-family: inherit;
	font-size: 20px;
	font-weight: 400;
	line-height: 1;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--wp--preset--color--contrast);
	margin-block-end: 0.6rem;
	display: inline-block;
}

/* `mark_required: yes` with `mark_required_color: #D57F9C` on the widget. */
.adsbox-form .fluentform .ff-el-is-required.asterisk-right label:after {
	color: var(--wp--preset--color--accent);
}

.adsbox-form .fluentform .ff-el-form-control {
	border: 0;
	border-bottom: 1px solid #c8c8c8;
	border-radius: 0;
	background: transparent;
	padding: 0.55rem 0;
	font-family: inherit;
	font-size: 16px;
	font-weight: 300;
	line-height: 1.5;
	color: var(--wp--preset--color--contrast);
	width: 100%;
	box-shadow: none;
}

.adsbox-form .fluentform textarea.ff-el-form-control {
	resize: vertical;
}

/* `field_text_color: #7E7E7E`, `text-transform: capitalize` — production styles
   the placeholder, not the typed value, which stays ink. */
.adsbox-form .fluentform .ff-el-form-control::placeholder {
	color: #7e7e7e;
	text-transform: capitalize;
	opacity: 1;
}

.adsbox-form .fluentform .ff-el-form-control:focus,
.adsbox-form .fluentform .ff-el-form-control:focus-visible {
	outline: 0;
	border-bottom-color: var(--wp--preset--color--accent);
	box-shadow: none;
}

/* The consent checkbox is the most-repeated broken link on the old site
 * (defect 6: 83 http://theadsbox.com/privacy-policy-2/ across all 46 pages).
 * It is rebuilt root-relative and must stay legible, not shrunk to fine print. */
.adsbox-form .fluentform .ff-el-form-check.ff-el-tc label {
	display: flex;
	gap: 0.6rem;
	align-items: center;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.5;
}

/* Fluent Forms leaves the box at the browser's default 13px, which next to
   16px type reads as an afterthought — on the one control that records consent.
   The span is the wrapper Fluent Forms puts around the input. */
.adsbox-form .fluentform .ff-el-form-check.ff-el-tc .ff_tc_checkbox {
	display: inline-flex;
	flex: 0 0 auto;
}

.adsbox-form .fluentform .ff-el-form-check.ff-el-tc input[type="checkbox"] {
	width: 1.1rem;
	height: 1.1rem;
	margin: 0;
	accent-color: var(--wp--preset--color--accent);
}

/* `button_align: center` on every page form in the source. Fluent Forms writes
   the alignment as a class on the wrapper; the rule is here because the wrapper
   is a plain block and `ff-text-center` only sets text-align. */
.adsbox-form .fluentform .ff_submit_btn_wrapper {
	text-align: center;
}

.adsbox-form .fluentform .ff-btn-submit {
	border: 0;
	border-radius: 25px;
	background: #000000;
	color: #ffffff;
	font-family: inherit;
	font-size: 16px;
	font-weight: 400;
	line-height: 1;
	letter-spacing: 1px;
	text-transform: uppercase;
	padding: 1.15rem 3.5rem;
	cursor: pointer;
	transition: background-color 0.2s ease, transform 0.2s ease;
}

.adsbox-form .fluentform .ff-btn-submit:hover {
	background: var(--wp--preset--color--accent);
	color: #ffffff;
	filter: none;
}

@media (prefers-reduced-motion: no-preference) {
	/* `button_hover_animation: grow` on the widget. */
	.adsbox-form .fluentform .ff-btn-submit:hover {
		transform: scale(1.05);
	}
}

/* ------------------------------------------------------------------ the map
 *
 * Production's /contact/ ends on a full-bleed maps.google.com iframe, 408px
 * tall. The height comes from the widget's own `height` setting, carried in as
 * --adsbox-map-h so a second map at a different size needs no new CSS.
 */
.adsbox-map {
	position: relative;
	margin: 0;
	height: var(--adsbox-map-h, 408px);
	background: var(--wp--preset--color--mist);
	overflow: hidden;
}

.adsbox-map__frame {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

/*
 * The office address as text. The iframe renders it as pixels inside a
 * cross-origin document, so this is the only place it exists on the page for a
 * screen reader or a crawler. Visually hidden rather than removed — production
 * shows the map alone, and inventing a caption under it would be a design
 * change nobody asked for. Focusable, so a keyboard user can still reach
 * directions.
 */
.adsbox-map__addr {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.adsbox-map__addr a:focus {
	position: fixed;
	top: 1rem;
	left: 1rem;
	z-index: 100;
	width: auto;
	height: auto;
	padding: 0.75rem 1.25rem;
	clip-path: none;
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	box-shadow: 0 10px 30px rgba(24, 24, 24, 0.2);
}

/* ------------------------------------------------------------- popup dialog
 *
 * Elementor popup 673, rebuilt as a native <dialog>. `showModal()` gives focus
 * trapping, Escape-to-close and inertness of the rest of the page for free —
 * all of which the Elementor popup needed a JavaScript bundle for.
 *
 * .adsbox-open-booking and .adsbox-open-subscribe are on the core/button
 * wrapper, not on the <a>, so they carry no appearance of their own. They exist
 * so theme/adsbox/assets/js/dialogs.js can find the triggers, and they are
 * declared here because check-structure.py requires every adsbox-* class in the
 * markup to have a rule — a class with no rule is how Phase 6 shipped 26
 * invented ones that rendered unstyled while every check stayed green.
 */

.adsbox-open-booking,
.adsbox-open-subscribe {
	cursor: pointer;
}

.adsbox-dialog {
	border: 0;
	border-radius: 1.5rem;
	padding: 0;
	max-width: min(40rem, calc(100vw - 2rem));
	width: 100%;
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
}

.adsbox-dialog::backdrop {
	background: rgba(24, 24, 24, 0.55);
}

.adsbox-dialog__body {
	padding: var(--wp--preset--spacing--50);
	position: relative;
}

.adsbox-dialog__close {
	position: absolute;
	inset-block-start: 0.75rem;
	inset-inline-end: 1rem;
	border: 0;
	background: none;
	color: inherit;
	font-size: 1.75rem;
	line-height: 1;
	cursor: pointer;
}

.adsbox-dialog__title {
	margin-block: 0 0.5rem;
	font-size: var(--wp--preset--font-size--x-large);
	letter-spacing: -0.02em;
}

.adsbox-dialog__intro {
	margin-block-start: 0;
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--bark);
}

@media (prefers-reduced-motion: no-preference) {
	.adsbox-dialog[open] {
		animation: adsbox-dialog-in 200ms ease-out;
	}
}

@keyframes adsbox-dialog-in {
	from { opacity: 0; transform: translateY(0.75rem); }
	to   { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------- carousel
 *
 * CSS scroll-snap, no library. Decided 2026-08-28: native scrolling brings
 * keyboard, touch, momentum, focus and screen-reader behaviour with it, where a
 * carousel library has to reimplement all of it — and adding a ~30 KB dependency
 * to a project whose whole premise is that a page builder died under this client
 * is a poor trade for a fade transition.
 *
 * With CSS off this is a list of images. With JavaScript off it is a scrollable
 * strip. The prev/next buttons are added by carousel.js, so they are never
 * present as dead controls.
 */

.adsbox-carousel {
	position: relative;
}

.adsbox-carousel__track {
	display: flex;
	gap: var(--wp--preset--spacing--30);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-padding-inline: var(--wp--preset--spacing--30);
	scrollbar-width: thin;
	padding-block-end: 0.75rem;
	/* The flex children below are the block wrappers core emits, which is why
	 * the rule targets the child rather than a class of ours. */
}

.adsbox-carousel__track > * {
	flex: 0 0 min(22rem, 82%);
	scroll-snap-align: start;
	margin-block: 0;
}

.adsbox-carousel--cards .adsbox-carousel__track > * {
	flex-basis: min(28rem, 88%);
}

.adsbox-carousel__slide img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: 1.25rem;
}

.adsbox-carousel__track:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 4px;
}

/* Added by carousel.js only when the track actually overflows. */
.adsbox-carousel__nav {
	display: flex;
	gap: 0.5rem;
	justify-content: flex-end;
	margin-block-start: 0.75rem;
}

.adsbox-carousel__btn {
	width: 2.75rem;
	height: 2.75rem;
	border-radius: 999px;
	border: 1px solid var(--wp--preset--color--hairline);
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	font-size: 1.1rem;
	line-height: 1;
	cursor: pointer;
}

.adsbox-carousel__btn[disabled] {
	opacity: 0.35;
	cursor: default;
}

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

@media (prefers-reduced-motion: no-preference) {
	.adsbox-carousel__track {
		scroll-behavior: smooth;
	}
}

/* =========================================================================
 * Sections, rows and columns — the Elementor container layout
 * =========================================================================
 *
 * Phase 6 originally assembled every page as one linear stack of full-width
 * blocks, because the Phase 2 document model had discarded the container tree.
 * scripts/lib/layout.py recovers it; this is where it becomes a design.
 *
 * The geometry is production's own, read from Elementor kit 671: container
 * width 1660px, container padding 0, space_between_widgets 0. That is why the
 * bands run edge to edge and a split section's photograph bleeds off the side
 * of the screen instead of sitting inside a gutter.
 */

.adsbox-section {
	overflow: clip;                 /* a bled photo must not widen the page */
}

/*
 * theme.json's blockGap is spacing--30 (24px), and core's flow layout turns that
 * into `margin-block-start` on every sibling inside .entry-content. On a page of
 * bands that is a 24px white stripe between each one — measured on
 * /our-services/, where production's five colour fields butt directly against
 * each other and the rebuild had a gap under all four.
 *
 * The gap is right for loose content and wrong between bands, so it is removed
 * here rather than in theme.json, where zeroing it would also collapse the
 * spacing inside every column. Elementor kit 671's space_between_widgets is 0,
 * which is why production looks like one continuous field.
 */
.entry-content > .adsbox-section,
.wp-site-blocks > .adsbox-section {
	margin-block-start: 0;
	margin-block-end: 0;
}

/*
 * add_theme_support( 'wp-block-styles' ) loads wp-block-library-theme.css, which
 * gives any group with a background `padding: 1.25em 2.375em`. On a band that is
 * ~45px of inset on each side, and it is what stopped a split section's
 * photograph from reaching the edge of the screen — the section looked full
 * width because its background was, while its contents were not.
 *
 * The bands set their own spacing below, so the opt-out is total rather than a
 * per-side correction.
 */
.adsbox-section.has-background {
	padding: 0;
}

/*
 * A band whose Elementor container declared its own vertical padding. The
 * source value is on the section as a real spacing.padding attribute
 * (layout._box_style -> blocks.section), so all this rule has to do is stop the
 * columns inside adding `--adsbox-col-pad` on top of it — 72px at the 1440px
 * desktop, where /contact/'s band says 3%, i.e. 43px.
 *
 * Horizontal padding is deliberately left to the column: the source's 8% is an
 * inset on the text, and moving it to the section would change where every
 * column edge falls on nine other pages for no gain here.
 */
.adsbox-section--pad > .adsbox-row > .wp-block-column {
	padding-block: 0;
}

/* A band of plain content — no columns — still needs the site's rhythm and
   must not run the full 1660px as a line of text. */
.adsbox-section > :not(.adsbox-row):not(.wp-block-group):not(.adsbox-form):not(.adsbox-col-media):not(.adsbox-rule):not(.adsbox-map):not(.adsbox-section__bg) {
	max-width: var(--wp--style--global--content-size);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--30);
}

/*
 * A group band — the FAQ accordion, the location card, a run of loose text.
 * It needs the max-width as well as the padding: without it the "Find us"
 * address card on /contact/ rendered as a full-bleed 1440px stripe with its
 * heading pinned 24px from the left edge of the screen, which is not a card.
 */
.adsbox-section > .wp-block-group:not(.adsbox-row):not(.adsbox-card):not(.adsbox-form):not(.adsbox-stats):not(.adsbox-carousel--quotes) {
	max-width: var(--wp--style--global--content-size);
	margin-inline: auto;
	padding-block: var(--wp--preset--spacing--60);
	padding-inline: var(--wp--preset--spacing--30);
}

/* The logo band is the exception: it is a full-bleed stripe on production, and
   the rule above capped it at the 1200px measure and centred it — so a white
   band appeared inset inside the section's own colour, with the section showing
   through on both sides. */
.adsbox-section > .wp-block-group:not(.adsbox-row):not(.adsbox-card):not(.adsbox-form):not(.adsbox-stats):has(> .adsbox-marquee) {
	max-width: none;
	padding-inline: 0;
}

/*
 * Loose copy sitting straight on a band. contentSize is 1200px, which is the
 * right width for a layout and much too wide for a line of text — the privacy
 * policy is 1,600 words set to a 1200px measure. Capped by characters, not by
 * pixels, so it holds when the type scale changes.
 *
 * The :not() chain is NOT tidiness — it is specificity. This rule used to end
 * at `:not(.adsbox-card)`, four classes, and the band rule above it carries
 * seven; `:not()` takes its argument's specificity, so (0,8,0) beat (0,5,1) and
 * the measure NEVER APPLIED on any page. Measured 2026-09-03 on
 * /social-media/: the hero's 13px intro sentence ran the full 1200px as one
 * hairline of type across the band, and so did /ppc-pay-per-click/'s and
 * /audit-strategy/'s. Same family as the `:has()`/`:not()` traps in CLAUDE.md,
 * on the rule that sets the page's measure. The exclusions added here are all
 * real — they are the same elements the band rule exempts — so the chain is
 * honest as well as heavy.
 */
.adsbox-section > :is(p, ul, ol, dl, h2, h3, h4, h5, h6):not(.adsbox-row):not(.wp-block-group):not(.adsbox-form):not(.adsbox-card):not(.adsbox-col-media):not(.adsbox-rule):not(.adsbox-map):not(.adsbox-section__bg):not(.adsbox-marquee) {
	max-width: min(var(--wp--style--global--content-size), 68ch);
}

/*
 * A measure narrower than the band has to start where the band's other content
 * starts, or it reads as a stray centred paragraph under a left-aligned
 * heading. The band rule above centres every loose child inside contentSize, so
 * a left-aligned one is offset by the same amount the 1200px measure is — and
 * by nothing at all once the viewport is under 1200px, where `max()` floors it.
 */
.adsbox-section {
	--adsbox-measure-inset: max(0px, calc((100% - var(--wp--style--global--content-size)) / 2));
}

.adsbox-section > :is(p, ul, ol, dl, h2, h3, h4, h5, h6).has-text-align-left:not(.adsbox-row):not(.wp-block-group):not(.adsbox-form):not(.adsbox-card):not(.adsbox-col-media):not(.adsbox-rule):not(.adsbox-map):not(.adsbox-section__bg) {
	margin-inline: var(--adsbox-measure-inset) auto;
}

/*
 * A band that opens with a heading and then a grid: the grid carries its own
 * padding-block and the heading carries none, so the heading butted straight
 * against the band above it — on /about/ "ANONYMOUS SOURCES SAY OUR TEAM IS
 * COOL." sat 15px under the edge of the black welcome field.
 */
.adsbox-section > :is(h1, h2, h3):first-child {
	padding-top: var(--wp--preset--spacing--60);
}

/*
 * A band that is nothing but a call to action. On /about/ the "Schedule a
 * meeting" band sat with its pill touching the black welcome field below it —
 * zero gap, measured — because the band carried no vertical padding of its own
 * and the button block brought only its own 2rem top margin. Production gives
 * that button air on both sides.
 */
.adsbox-section:has(> .wp-block-buttons:only-child) {
	/*
	 * The air goes INSIDE the band, and that is not a style choice either. It
	 * used to be `margin-bottom`, and a margin is unpainted: on
	 * /user-generated-content-ugs/ the band and both its neighbours are shell,
	 * and the 48px below the pill — plus the 32px the button's own top margin
	 * collapsed out through `overflow: clip`, which does NOT open a block
	 * formatting context — rendered as two WHITE seams across a cream page.
	 *
	 * `display: flow-root` keeps the button's own margins in the band, where
	 * the band's ground paints them, and the space below is stated on the
	 * BUTTON rather than as the band's padding-bottom: the assembler carries
	 * /about/'s Elementor container padding as an inline `padding-bottom: 20px`
	 * that no stylesheet rule can outrank. A child's margin is not in that
	 * fight, and with flow-root it can no longer escape.
	 */
	display: flow-root;
}

.adsbox-section:has(> .wp-block-buttons:only-child) > .wp-block-buttons {
	margin-bottom: var(--wp--preset--spacing--50);
}

/*
 * A buttons block sitting straight on a band, not inside a column: same problem
 * as in .adsbox-col--text below. On /case-studies/ the heading and the intro are
 * centred and "Schedule a meeting" sat at the far left of the band under them.
 */
.adsbox-section > .wp-block-buttons:not(.is-content-justification-left):not(.is-content-justification-right):not(.is-content-justification-space-between) {
	justify-content: center;
}

/*
 * ...unless the band it sits on is set left. A hero states its heading and its
 * intro with `has-text-align-left` — the source's own alignment, carried by
 * layout._typography — and the rule above then centred the one element that had
 * no alignment of its own, so /social-media/, /ppc-pay-per-click/ and
 * /audit-strategy/ all read left, left, centre down the same 1200px measure.
 * The band decides, and it decides once for everything on it.
 */
.adsbox-section:has(> :is(h1, h2, h3, p).has-text-align-left) > .wp-block-buttons:not(.is-content-justification-left):not(.is-content-justification-right):not(.is-content-justification-space-between) {
	justify-content: flex-start;
}

/* A grid holding one item leaves it in the first track and the rest of the
   measure empty — /case-studies/ has exactly one case study. Centring the track
   is the honest fix; it needs no knowledge of how many items there will be. */
.adsbox-section .wp-block-group.is-layout-grid:has(> :only-child) {
	justify-content: center;
}

/* A card is already a box with its own ground and inset; the band padding above
   would double it and the max-width above would stretch it to the full measure. */
.adsbox-section > .wp-block-group.adsbox-card {
	max-width: 46rem;
	margin-inline: auto;
	padding: var(--wp--preset--spacing--40);
	margin-block: var(--wp--preset--spacing--60);
}

/*
 * A media-only container that IS the section — every full-bleed hero on the
 * site. Left to the generic band rule above it rendered as a boxed picture
 * inside a 1200px measure with white on both sides; production crops it to a
 * band. Elementor's own min-height on those containers is 400px.
 */
.adsbox-section > .adsbox-col-media {
	max-width: none;
	margin-inline: 0;
	padding-inline: 0;
	min-height: 0;
}

.adsbox-section > .adsbox-col-media img {
	width: 100%;
	height: clamp(20rem, 34vw, 30rem);
	object-fit: cover;
}

/*
 * A section production pulls up over the one above it — on /about/ the three
 * statistics sit ON the hero photograph. The negative margin is carried as a
 * real core spacing attribute by blocks.section(); this only has to make sure
 * the pulled band paints above its neighbour and does not clip.
 */
.adsbox-section--pull {
	position: relative;
	z-index: 1;
	overflow: visible;
	/* Measured against production: the gap from the cards to the button below
	   them is ~55px, of which the buttons block already brings 32px of its own
	   top margin. */
	padding-bottom: var(--wp--preset--spacing--30);
}

/*
 * The pull is an inline style, because core serialises spacing.margin into one
 * — so overriding it needs !important, which is the price of the attribute
 * being a real block attribute rather than a class. On a phone the cards stack
 * and -180px would drag the first one off the top of the photograph entirely.
 */
@media (max-width: 781px) {
	.adsbox-section--pull {
		margin-top: -3rem !important;
	}
}

/* ------------------------------------------------------------------ rows */

.adsbox-row {
	--adsbox-col-pad: clamp(1.5rem, 5vw, 5.5rem);
	gap: 0;
	align-items: stretch;
}

/* core/columns stacks below 782px on its own. Above it a 50/50 split is a
   50/50 split — those two columns sum to 100% and never wrap.
 *
 * The `min-width: 0` this rule used to carry is what let a column collapse to
 * nothing. On /user-generated-content-ugs/ a row pairs four labels with four
 * 25%-basis photographs: the photographs take the whole 100% and the four
 * label columns, which carry no basis of their own, were squeezed to about one
 * character — "1. Unboxing videos" rendered as a vertical stack of letters, the
 * same failure as the nested row below. A floor plus wrapping means a column
 * that cannot fit moves down instead of disappearing.
 *
 * `!important` and the doubled class are load-bearing. core/columns ships
 *   @media (min-width: 782px) { .wp-block-columns { flex-wrap: nowrap!important } }
 * so the wrap half of "a floor plus wrapping" NEVER APPLIED — only the floor
 * did, which turns a row that cannot fit into a row that OVERLAPS instead of
 * one that collapses. Measured 2026-09-03 on /social-media/: the nested row
 * inside the 48% copy column laid three 240px columns (the min-width floor,
 * exactly) across a 547px content box and ran 101px past its parent, under the
 * photograph beside it — "ORGANIC ENGAGEMENT" rendered as ORGA/NIC/ENGA/GEME/NT.
 * A tie on specificity is broken by source order, and nothing guarantees this
 * sheet loads after wp-block-library, so the selector is (0,2,0) on purpose. */
/*
 * Three columns or more, and only then. A two-column split is the one shape
 * that must NOT wrap: the closing CTA carries production's own
 * `margin-left: 150px` on its copy column, so 34% + 57% + 150px is 1461px in a
 * 1440px row and the slider dropped to a line of its own on
 * /ppc-pay-per-click/ and /audit-strategy/ the moment wrapping worked.
 * Under nowrap those two shrink by 21px between them and look exactly right,
 * which is what every measurement in this file was taken against. Nothing was
 * ever wrong with a two-column row — the collapse and the overlap are both
 * failures of a row with more columns than fit.
 */
.wp-block-columns.adsbox-row:has(> .wp-block-column:nth-child(3)) {
	flex-wrap: wrap !important;
}

.adsbox-row > .wp-block-column {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-width: min(100%, 12rem);
}

/*
 * How the source row distributes its columns. 41 of the 124 row containers
 * across the 28 pages declare a justification, and EVERY row whose column
 * widths sum below 100 declares space-between — the eight closing CTAs (34% +
 * 45%), the three-up and four-up service rows. A row that sums to 100 is
 * unaffected by this; a row that does not now shows production's gap instead of
 * stretching one column over it.
 */
.adsbox-row-between {
	justify-content: space-between;
}

.adsbox-row-around {
	justify-content: space-around;
}

.adsbox-row-evenly {
	justify-content: space-evenly;
}

.adsbox-row-mid {
	justify-content: center;
}

.adsbox-row-start {
	justify-content: flex-start;
}

.adsbox-row-end {
	justify-content: flex-end;
}

.adsbox-row > .adsbox-col--text {
	padding: var(--adsbox-col-pad);
	gap: var(--wp--preset--spacing--30);
	text-align: center;
}

/*
 * core/buttons is a flex container, so `text-align: center` on the column does
 * nothing to it: on /contact/ the heading and the paragraph sat centred while
 * "Schedule a meeting" hung off the far left of the same column. The buttons
 * follow the column's alignment instead of ignoring it.
 */
.adsbox-row > .adsbox-col--text > .wp-block-buttons:not(.is-content-justification-left):not(.is-content-justification-right):not(.is-content-justification-space-between) {
	justify-content: center;
}

/*
 * A NESTED row is a different problem from a top-level one. core/columns is
 * `flex-wrap: nowrap` above 782px, and the Elementor trees nest a five-column
 * run inside one half of a split section — so on /ppc-pay-per-click/ the five
 * shared 48% of the screen and the heading column, which carries no flex-basis
 * of its own, was crushed to about one character wide: "How our PPC service can
 * help you?" rendered as a vertical stack of single letters.
 *
 * The inline `flex-basis` core writes cannot be overridden from a stylesheet, so
 * the fix is `min-width` plus wrapping: a column that cannot fit its share drops
 * to the next line instead of collapsing.
 */
.adsbox-row .wp-block-columns.adsbox-row:has(> .wp-block-column:nth-child(3)) {
	flex-wrap: wrap !important;
	row-gap: var(--wp--preset--spacing--40);
	/*
	 * --adsbox-col-pad is 5vw — 72px a side at 1440 — which is the right inset
	 * for a column that is half the screen and absurd for one nested inside
	 * that half: /audit-strategy/'s four cards come out 240px wide, so 144px of
	 * that was padding and "Performance Assessment" set as PERFORM/ANC/E. The
	 * viewport does not size a nested column, so the viewport must not size its
	 * padding either.
	 */
	--adsbox-col-pad: clamp(0.75rem, 1.5vw, 1.5rem);
}

.adsbox-row .adsbox-row > .wp-block-column {
	min-width: min(100%, 15rem);
	flex-grow: 1;
}

/*
 * core ships `.wp-block-columns .wp-block-column[style*=flex-basis]{flex-grow:0}`
 * at the same (0,3,0) as the rule above, so a sized column in a nested row did
 * not grow and `space-between` put the leftover BETWEEN the two cards instead
 * of in them: /social-media/'s pair came out 240px and 263px with a 44px hole
 * down the middle, and the 240px one still broke "ENGAGEMENT" across a line.
 * A row that has wrapped has no gap to honour — the whole point of the wrap is
 * that the line is full — so inside a nested row the declared width is a
 * proportion, not a cap.
 */
.adsbox-row .adsbox-row > .wp-block-column[style*="flex-basis"] {
	flex-grow: 1;
}

/*
 * The section's own heading, sitting in the row as an unsized column beside
 * the items it introduces. Elementor gives it no width because Elementor's
 * rows do not wrap: it takes whatever the sized columns leave, and on every
 * one of these rows they leave nothing — /ppc-pay-per-click/ declares 4x40%
 * beside it, /social-media/ 40% + 48% inside a half-width column,
 * /audit-strategy/ two more. With the floor above, that is a 192px column
 * holding a 40px heading, so "How our PPC service can help you?" set one word
 * per line down the page.
 *
 * A column core did not size is the only one identifiable from CSS — core
 * writes `flex-basis` inline on every column that HAS a declared width — and
 * in a row of three or more it is always the lead-in. Given the whole first
 * line it becomes the site's own convention: a heading, then the items under
 * it, which is exactly what the "And Many More Services" and "How We Can Help
 * You?" bands already do on the pages whose source happened to state them as
 * separate containers.
 */
.adsbox-row:has(> .wp-block-column:nth-child(3)) > .wp-block-column:not([style*="flex-basis"]) {
	flex-basis: 100%;
	justify-content: flex-start;
}

/*
 * A heading in a nested column is set for the width it actually has. The
 * source states one size for a heading whether it is the whole band or one of
 * four cards inside half of one, and at 33.7px in a 252px column a single long
 * word does not fit: core's `.wp-block-column { word-break: break-word }`
 * then shreds it rather than overflowing, which is how the Bulgarian page's
 * "АНГАЖИРАНОСТ" came out АНГАЖИРАН / ОСТ. Nothing measures a broken word, so
 * the only defence is not to ask for one — the step down is to roughly the
 * `large` step, which fits the longest word on any of these cards.
 */
.adsbox-row .adsbox-row > .wp-block-column[style*="flex-basis"] :is(h2, h3, h4) {
	/* `!important` because the size it is overriding is one: WordPress emits
	   every `.has-*-font-size` preset class with `!important`, so a preset can
	   only be beaten by another declaration that carries it.
	 *
	 * `[style*="flex-basis"]` excludes the lead-in — the unsized column given
	 * the whole first line above. That one is the band's heading and keeps the
	 * size the source states; only the cards beside it are re-set.
	 *
	 * 0.8 of the `large` step, not a pixel count, so the fluid scale still does
	 * the responsive work. It is the ratio that fits the longest word any of
	 * these cards carries — Bulgarian "АНГАЖИРАНОСТ", 12 characters — inside the
	 * ~210px of text a nested column has at 1440. */
	font-size: calc(var(--wp--preset--font-size--large) * 0.8) !important;
	letter-spacing: -0.02em;
}

/*
 * An icon that arrived as an image block. Left alone an SVG with no intrinsic
 * width fills its container — the envelope on the three service pages rendered
 * about 350px tall. It is an icon on production; sized like one here. Scoped to
 * SVG so no photograph is touched, and only three pages carry one.
 */
.entry-content .wp-block-image img[src$=".svg"] {
	width: 3.5rem;
	height: auto;
}

/* A media column is the photograph and nothing else: no padding, and the image
   fills the column's full height however tall the text column beside it grows.
 *
 * `height: 100%` on the figure below only says that. It does not MAKE it true:
 * a percentage height against a column whose own height is `auto` resolves to
 * auto, so the photograph fell back to its natural aspect and it was the
 * photograph that set the band's height. Measured 2026-09-03 on /social-media/:
 * a portrait at 590px wide made the band 886px tall against 539px of copy, so a
 * third of the section was empty cream beside the text — the same "unbalanced,
 * lots of dead space" shape on every split section whose photo is taller than
 * the words next to it.
 *
 * Taking the figure out of flow makes the column the one that decides. The
 * column is then only as tall as the copy, floored at the clamp so a short
 * column is still a band and not a stripe, and `object-fit: cover` below does
 * the cropping. The floor moves from the figure to the column for the same
 * reason: an out-of-flow child cannot push its parent open, and on a phone the
 * column stacks and there is no copy beside it to give it a height at all. */
.adsbox-row > .adsbox-col--media {
	padding: 0;
	justify-content: stretch;
	position: relative;
	min-height: clamp(16rem, 34vw, 34rem);
}

.adsbox-row > .adsbox-col--media > :is(.wp-block-image, .adsbox-card-video, .adsbox-col-media, .adsbox-compare) {
	position: absolute;
	inset: 0;
	min-height: 0;
}

.adsbox-col-media,
.adsbox-row > .adsbox-col--media > .wp-block-image {
	/* core/image's figure is a shrink-to-fit box. Left alone it centres a photo
	   inside the column at its natural size, which reads as an inset picture
	   rather than as the full-bleed half of a split section. */
	display: block;
	width: 100%;
	margin: 0;
	height: 100%;
	min-height: clamp(16rem, 34vw, 34rem);
}

/*
 * `:not(.adsbox-card-video img)` is load-bearing, not tidiness. An animated
 * service card is a <video> with its poster frame beside it as a real <img>,
 * which `.adsbox-card-video .adsbox-card-video__poster { display: none }`
 * hides — at (0,2,0). This selector is (0,3,2) because `:not()` takes its
 * argument's specificity, so it was turning the poster back on: every
 * animated GIF on /our-services/ and /bg/our-services-2/ rendered the video
 * AND a still of its first frame, stacked, on desktop as well as on a phone.
 * That is the "two images cluttered together" reported 2026-09-02, and it was
 * never a mobile bug — nobody had looked at a media column holding a video.
 */
.adsbox-col-media img,
.adsbox-row > .adsbox-col--media img:not(.adsbox-compare img):not(.adsbox-card-video img) {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* A video in a media column fills it exactly as a photograph does. Left alone
   it sat at its own 2:1 in the middle of a square column. */
.adsbox-row > .adsbox-col--media > .adsbox-card-video {
	height: 100%;
	border-radius: 0;
}

.adsbox-row > .adsbox-col--media > .adsbox-card-video video {
	height: 100%;
	object-fit: cover;
}

/*
 * A comparison slider IS the media column on production: it fills the column
 * edge to edge, square, at the height of the copy beside it. Left as a plain
 * group it sat at the source image's natural 1:1 in the middle of a taller
 * column, which is what "the placement is wrong" looked like.
 */
/*
 * Selected on `:has`, not on .adsbox-col--media: layout.py calls a column
 * "media" when it holds an image NODE, and a comparison slider is a group. So
 * the slider lands in a text column, keeps that column's padding, and sits at
 * the source image's natural 1:1 in the middle of a taller column. On
 * production it is the column.
 */
/*
 * The comparison column keeps its declared width and does NOT grow. It used to:
 * the source declares 34% for the copy and 45% for the slider — 79%, and the
 * missing fifth was handed to the slider, which made it 66% of the viewport and
 * 950px tall against production's 648. Production leaves that fifth empty,
 * because the row declares `flex_justify_content: space-between` and Elementor
 * honours it. That justification is now carried out of the source as a row
 * class (layout._box_style -> blocks.justify_class), so the leftover is a gap
 * rather than something a column has to absorb.
 */
.adsbox-row > .wp-block-column:has(> .adsbox-compare) {
	padding: 0;
}

.adsbox-row > .wp-block-column > .adsbox-compare {
	border-radius: 0;
	block-size: 100%;
	min-block-size: clamp(16rem, 34vw, 34rem);
}

.adsbox-row > .wp-block-column > .adsbox-compare :is(img, .wp-block-image) {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

/* The four-up service row is cards on a band, so it keeps a gutter and its own
   breathing room — the only row on the site that does.
 *
 * The gutter is the columns' own padding, NOT a column-gap. Those four columns
 * are 4x25%, which is the whole row: a 24px `gap` on top of that is 72px more
 * than the line holds, so under `flex-wrap: wrap` the fourth card dropped to a
 * second line and the homepage's service band grew 600px. `flex-basis` is
 * inline on every one of them and cannot be reduced from a stylesheet, so the
 * air has to come out of the inside of the column instead — with `box-sizing:
 * border-box` (see the top of this file) padding is inside the basis, and the
 * band's outer inset gives back the half-gutter the first and last card now
 * carry. Same double count as `.adsbox-row:has(> .adsbox-col-round)` above. */
.adsbox-row:has(.adsbox-service-card) {
	row-gap: var(--wp--preset--spacing--30);
	column-gap: 0;
	padding: var(--wp--preset--spacing--60)
	         calc(var(--adsbox-col-pad) - var(--wp--preset--spacing--30) / 2);
}

.adsbox-row:has(.adsbox-service-card) > .wp-block-column {
	padding: 0 calc(var(--wp--preset--spacing--30) / 2);
}

/* --------------------------------------------------------- service cards */
/*
 * Production overlays the card title on the photograph. The heading stays a
 * real heading in the markup — it is the card's link text and its outline
 * entry — and is positioned here rather than being baked into an image.
 */

.adsbox-service-card {
	position: relative;
	overflow: hidden;
	border-radius: 1.25rem;
	isolation: isolate;
	/* .adsbox-card's ground and padding are for a card made of text. Here the
	   photograph is the card, so both are removed rather than shown as a frame
	   around it. */
	background: none;
	padding: 0;
}

.adsbox-service-card > .wp-block-image,
.adsbox-service-card > .adsbox-card-video {
	margin: 0;
	border-radius: inherit;
}

.adsbox-service-card img,
.adsbox-service-card video {
	display: block;
	width: 100%;
	aspect-ratio: 3 / 4;
	object-fit: cover;
}

.adsbox-service-card > :is(h2, h3, h4) {
	position: absolute;
	inset-block-start: 0;
	inset-inline: 0;
	margin: 0;
	padding: 1.25rem 1.25rem 2.5rem;
	color: #fff;
	font-size: clamp(1.125rem, 1.6vw, 1.75rem);
	line-height: 1.05;
	letter-spacing: -0.03em;
	text-transform: uppercase;
	/* Legibility over an unknown photograph, without dimming the whole image. */
	background: linear-gradient(to bottom, rgba(0, 0, 0, 0.62) 0%, rgba(0, 0, 0, 0.34) 55%, transparent 100%);
	text-shadow: 0 1px 12px rgba(0, 0, 0, 0.35);
}

/* The card's link is the whole card. The button itself stays in the markup for
   keyboard and screen-reader users and is pinned to the bottom edge. */
.adsbox-service-card > .wp-block-buttons {
	position: absolute;
	inset-block-end: 1rem;
	inset-inline-start: 1.25rem;
	margin: 0;
}

/* ------------------------------------------- consecutive one-cell grids */
/*
 * The assembler currently emits ONE core/group (grid layout) per flip card
 * rather than one grid holding all of them, so /about/'s fourteen team cards
 * render as fourteen stacked rows each containing a single ~280px card and
 * ~900px of empty space. That is a markup bug and it is reported rather than
 * papered over — but the page has to be presentable while it is open, so the
 * section lays its grid children out itself.
 *
 * The rule is written to retire on its own: it requires TWO adjacent grid
 * children, so the moment the assembler emits one grid holding every card it
 * stops matching and core's own grid layout takes over unmodified.
 */
.adsbox-section:has(> .is-layout-grid + .is-layout-grid):has(> .is-layout-grid > .adsbox-flip) {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: stretch;
	gap: var(--wp--preset--spacing--30);
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding: var(--wp--preset--spacing--60) var(--wp--preset--spacing--30);
}

/* The band's own heading is not one of the cards. */
.adsbox-section:has(> .is-layout-grid + .is-layout-grid):has(> .is-layout-grid > .adsbox-flip) > :not(.is-layout-grid) {
	flex: 0 0 100%;
	max-width: none;
	padding-inline: 0;
	margin-block: 0 var(--wp--preset--spacing--40);
}

.adsbox-section:has(> .is-layout-grid + .is-layout-grid):has(> .is-layout-grid > .adsbox-flip) > .is-layout-grid {
	flex: 1 1 15rem;
	max-width: 20rem;
	padding: 0;
	margin: 0;
}

/* ------------------------------------------------------------ dark bands */
/*
 * The ink ground is applied by the assembler wherever production had one, and
 * it lands on whichever element carried it in the Elementor tree — the band on
 * some pages, a single column of a split row on others. The first version of
 * this rule keyed on `.adsbox-section.has-contrast-background-color` and so
 * missed every column-level one: on /our-services/ four H2s (PERFORMANCE
 * MARKETING, EMAIL MARKETING, …) and four "Learn more" buttons were rendering
 * ink-on-ink, invisible, while every structural check stayed green.
 *
 * theme.json's own rules are all `:root :where(…)`, specificity 0,1,0. These
 * are 0,2,0, so they win without !important. `:not(.has-text-color)` leaves an
 * explicitly-coloured element alone — that is a deliberate choice by whoever
 * set it, not something to override.
 */

.has-contrast-background-color :is(h1, h2, h3, h4, h5, h6, p, li, dt, dd):not(.has-text-color),
.has-bark-background-color :is(h1, h2, h3, h4, h5, h6, p, li, dt, dd):not(.has-text-color) {
	color: var(--wp--preset--color--base);
}

/* A body link on an ink ground inherits contrast from theme.json — invisible
   for the same reason. It keeps its underline, so it still reads as a link. */
.has-contrast-background-color a:where(:not(.wp-element-button):not(.wp-block-button__link)),
.has-bark-background-color a:where(:not(.wp-element-button):not(.wp-block-button__link)) {
	color: var(--wp--preset--color--base);
}

/* The button's fill is `contrast`, so on an ink band it is the band. Inverted
   here rather than given an outline, because the light-ground button is solid
   and an outline version would read as a different, weaker control. */
.has-contrast-background-color :is(.wp-element-button, .wp-block-button__link),
.has-bark-background-color :is(.wp-element-button, .wp-block-button__link) {
	background-color: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
}

.has-contrast-background-color :is(.wp-element-button, .wp-block-button__link):is(:hover, :focus-visible),
.has-bark-background-color :is(.wp-element-button, .wp-block-button__link):is(:hover, :focus-visible) {
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--base);
}

/* --------------------------------------------------------------- mobile */

@media (max-width: 781px) {
	.adsbox-row > .adsbox-col--text {
		padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--30);
	}

	.adsbox-col-media,
	.adsbox-row > .adsbox-col--media > .wp-block-image {
		min-height: 0;
	}

	.adsbox-row:has(.adsbox-service-card) {
		padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--30);
	}

	/* A flip card is one per row on a phone, and /about/ has fourteen of them.
	   At the desktop 22rem that is a five-screen scroll of empty cream. */
	.adsbox-flip {
		min-height: 13rem;
	}
}

/* WordPress re-applies the root inline padding to the children of an alignfull
   block so their text lines up with the rest of the page. A split row is the one
   case where that is wrong: its photograph is meant to bleed off the edge of the
   screen, and the text column carries its own generous padding already. */
.adsbox-section > .adsbox-row,
.adsbox-section > .wp-block-columns {
	padding-inline: 0;
	margin-block: 0;
}

/* ----------------------------------------------------- testimonial quotes */
/*
 * Production shows one testimonial at a time, centred, with the quote in the
 * body size and the attribution under it. The first rebuild reused the card
 * shelf, which put three quotes side by side, stretched them all to the height
 * of the longest, and cut the third one off at the edge of the screen.
 */

.adsbox-carousel--quotes {
	max-width: var(--wp--style--global--content-size);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--30);
}

.adsbox-carousel--quotes .adsbox-carousel__track {
	gap: 0;
	align-items: start;
	scrollbar-width: none;
}

.adsbox-carousel--quotes .adsbox-carousel__track::-webkit-scrollbar {
	display: none;
}

.adsbox-carousel--quotes .adsbox-carousel__track > * {
	flex: 0 0 100%;
	scroll-snap-align: center;
	text-align: center;
	padding-inline: clamp(0rem, 4vw, 4rem);
}

.adsbox-quote {
	border: 0;
	margin: 0;
}

.adsbox-quote p:first-child {
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.6;
	margin-block-end: var(--wp--preset--spacing--30);
}

.adsbox-quote p:last-child {
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.4;
}

.adsbox-carousel--quotes .adsbox-carousel__nav {
	justify-content: center;
	margin-block-start: var(--wp--preset--spacing--30);
}

/* --------------------------------------------------- the four-card row */
/*
 * Restated after the alignfull opt-out above, which zeroes the inline padding
 * for every row: the service row is the one that needs it back, because its
 * cards sit ON the band rather than bleeding off it.
 */
.adsbox-section > .adsbox-row:has(.adsbox-service-card) {
	padding: var(--wp--preset--spacing--60)
	         calc(var(--adsbox-col-pad) - var(--wp--preset--spacing--30) / 2);
	/* The gutter is the columns' padding, not a column gap — see the bare-class
	   rule above for why 4x25% plus a 24px gap cannot fit on one line. */
	row-gap: var(--wp--preset--spacing--30);
	column-gap: 0;
}

/* ------------------------------------------------------------- embeds */

.wp-block-embed.is-type-video {
	max-width: var(--wp--style--global--content-size);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--30);
}

.wp-block-embed.is-type-video .wp-block-embed__wrapper {
	position: relative;
	aspect-ratio: 16 / 9;
}

.wp-block-embed.is-type-video iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
	border-radius: 1.25rem;
}

/* =========================================================================
   Design parity pass, 2026-09-02
   ------------------------------------------------------------------------
   Everything below was found by putting the production capture and a
   headless shot of the build site side by side. Each rule names the
   measurement it reproduces, because "it looked wrong" is not a spec and the
   next person will want to know which half is authoritative.
   ========================================================================= */

/* ------------------------------------------------ the footer's mailing form
 *
 * Elementor form widget inside footer 690: `show_labels: ""`, a bottom-border
 * only field (border_width 0/0/1/0), a 25px-radius Subscribe button at 20% of
 * the row width sitting NEXT to the input, and the consent checkbox under both.
 * The generic .adsbox-form rules are for the six-field page forms; this is the
 * inline one-field variant and it needs its own shape rather than a smaller
 * copy of the other.
 */

.adsbox-footer__form .adsbox-form {
	margin: 0;
	max-width: none;
	padding-block-end: 0;
}

/* Production hides the labels. Keep them for assistive tech rather than
   deleting them — the placeholder is not an accessible name. */
.adsbox-footer__form .adsbox-form .ff-el-input--label {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.adsbox-footer__form .adsbox-form .ff-el-form-control {
	border: 0;
	border-block-end: 1px solid var(--wp--preset--color--contrast);
	border-radius: 0;
	background: transparent;
	padding: 0.6rem 0.25rem;
	font-size: var(--wp--preset--font-size--small);
}

.adsbox-footer__form .adsbox-form .ff-el-form-control:focus-visible {
	border-block-end-color: var(--wp--preset--color--accent);
}

/* Black pill, not the brand pink: production's Subscribe button is #000 with
   white text, the same as every other button on the site. The pink is the
   hover state, which theme.json already carries for core buttons. */
.adsbox-footer__form .adsbox-form .ff-btn-submit {
	border-radius: 25px;
	background: var(--wp--preset--color--contrast);
	color: var(--wp--preset--color--base);
	font-size: var(--wp--preset--font-size--small);
	text-transform: uppercase;
	letter-spacing: -0.03em;
	padding: 0.7rem 1.5rem;
	width: auto;
}

.adsbox-footer__form .adsbox-form .ff-btn-submit:hover,
.adsbox-footer__form .adsbox-form .ff-btn-submit:focus-visible {
	background: var(--wp--preset--color--accent);
}

.adsbox-footer__form .adsbox-form .ff-el-form-check.ff-el-tc label {
	font-size: var(--wp--preset--font-size--x-small);
}

/* --------------------------------------------- testimonial client logos
 *
 * Production's carousel layout is `image_above`: the client's mark in a circle,
 * centred, above the quote. The images are real files (2023/11/14-17.jpg and
 * the 2024/01 logos) — defect 5's deleted portraits are on a template that
 * renders on no page, which is why they were wrongly dropped site-wide.
 */

.adsbox-quote__logo {
	margin: 0 auto var(--wp--preset--spacing--30);
	width: 5.5rem;
}

.adsbox-quote__logo img {
	width: 5.5rem;
	height: 5.5rem;
	border-radius: 50%;
	object-fit: cover;
}

/* The name, then the company under it — 20px/700 and 16px on production. */
.adsbox-quote p:last-child {
	margin-block-start: var(--wp--preset--spacing--30);
}

.adsbox-quote p:last-child strong {
	display: inline-block;
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 700;
	margin-block-end: 0.35rem;
}

/* Production puts the arrows either side of the quote, vertically centred, not
   in a row beneath it. The track is still the scroll container; only the
   buttons move. */
.adsbox-carousel--quotes {
	position: relative;
}

.adsbox-carousel--quotes .adsbox-carousel__nav {
	position: absolute;
	inset: 0;
	margin: 0;
	display: flex;
	align-items: center;
	justify-content: space-between;
	pointer-events: none;
}

.adsbox-carousel--quotes .adsbox-carousel__btn {
	pointer-events: auto;
	border: 0;
	background: transparent;
	font-size: 1.75rem;
	line-height: 1;
}

.adsbox-carousel--quotes .adsbox-carousel__btn[disabled] {
	opacity: 0.25;
}

/* ------------------------------------------------------- video facade
 *
 * `show_image_overlay: yes` with the agency's own still. Nothing is requested
 * from Google until the poster is clicked; video.js swaps in the iframe.
 */

.adsbox-video__poster {
	display: block;
	position: relative;
	inline-size: 100%;
	block-size: 100%;
	text-decoration: none;
	cursor: pointer;
}

.adsbox-video__poster img {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

.adsbox-video__play {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	translate: -50% -50%;
	inline-size: 5rem;
	block-size: 5rem;
	border: 2px solid var(--wp--preset--color--base);
	border-radius: 50%;
	background: rgba(24, 24, 24, 0.25);
	transition: background-color 0.2s ease, scale 0.2s ease;
}

/* The triangle, drawn rather than shipped as an asset. */
.adsbox-video__play::after {
	content: "";
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 54%;
	translate: -50% -50%;
	border-inline-start: 1.15rem solid var(--wp--preset--color--base);
	border-block: 0.7rem solid transparent;
}

.adsbox-video__poster:hover .adsbox-video__play,
.adsbox-video__poster:focus-visible .adsbox-video__play {
	background: rgba(24, 24, 24, 0.45);
	scale: 1.06;
}

/* ---------------------------------------------- service cards, corrected
 *
 * Production's cards are portrait — roughly 1:1.8 in the four-up row — with a
 * larger radius, and the title sits ON the photograph with no dark band behind
 * it. Only one of the four carries a link, so the visible pill button the first
 * rebuild emitted appeared on exactly one card and read as a mistake. The link
 * becomes the whole card instead: still in the accessibility tree, still
 * keyboard-reachable, invisible until focused.
 */

.adsbox-service-card {
	border-radius: 1.75rem;
}

.adsbox-service-card img,
.adsbox-service-card video {
	aspect-ratio: 5 / 9;
}

.adsbox-service-card > :is(h2, h3, h4) {
	padding: 1.5rem 1.25rem 3rem;
	font-weight: 400;
	letter-spacing: -0.055em;
	background: linear-gradient(to bottom, rgba(0, 0, 0, 0.4) 0%, transparent 100%);
	text-shadow: 0 1px 14px rgba(0, 0, 0, 0.45);
}

.adsbox-service-card > .wp-block-buttons {
	position: absolute;
	inset: 0;
	margin: 0;
}

/*
 * The hover production actually has, and the only affordance these cards carry:
 * `elementor-bg-transform-zoom-in` on the photograph and
 * `elementor-animated-item--grow` on the title, on all four cards. There is no
 * button on any of them — the EMAIL MARKETING card is the one wrapped in an
 * <a href="/social-media/">, and the other three carry no link at all.
 */
.adsbox-service-card > .wp-block-image,
.adsbox-service-card > .adsbox-card-video,
.adsbox-service-card img,
.adsbox-service-card video {
	transition: transform 0.6s cubic-bezier(0.2, 0.6, 0.2, 1);
}

.adsbox-service-card > :is(h2, h3, h4) {
	transition: transform 0.35s ease;
	transform-origin: top center;
}

.adsbox-service-card:hover img,
.adsbox-service-card:hover video,
.adsbox-service-card:focus-within img,
.adsbox-service-card:focus-within video {
	transform: scale(1.08);
}

.adsbox-service-card:hover > :is(h2, h3, h4),
.adsbox-service-card:focus-within > :is(h2, h3, h4) {
	transform: scale(1.06);
}

/* Only the card that has a link is a pointer. A card with no target that
   changes the cursor is a click that goes nowhere. */
.adsbox-service-card:has(.wp-block-buttons) {
	cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
	.adsbox-service-card :is(img, video, h2, h3, h4) {
		transition: none;
	}

	.adsbox-service-card:hover :is(img, video),
	.adsbox-service-card:focus-within :is(img, video) {
		transform: none;
	}
}

.adsbox-service-card > .wp-block-buttons .wp-block-button__link {
	position: absolute;
	inset: 0;
	padding: 0;
	border-radius: inherit;
	background: transparent;
	/* The label stays in the accessibility tree; it is the card's link text. */
	color: transparent;
	font-size: 0;
}

.adsbox-service-card > .wp-block-buttons .wp-block-button__link:focus-visible {
	outline: 3px solid var(--wp--preset--color--accent);
	outline-offset: -3px;
}

/* -------------------------------------------------------- the Clutch badge
 *
 * A third-party widget in its own <div>, dropped straight into the hero's text
 * column. Production centres it under the button; left as-is it hangs off the
 * left edge of a centred column.
 */
.adsbox-col--text > div:has(> .clutch-widget) {
	display: flex;
	justify-content: center;
	margin-block-start: var(--wp--preset--spacing--40);
}

/* Centring the widget's own box is not enough. Clutch's iframe takes width
   100% of whatever it is given and draws its badge LEFT-aligned inside it,
   cross-origin, where no rule of ours reaches — so a 300px box centred under
   the button still hangs its badge ~55px to the left of the button's centre.
   Measured 2026-09-03 by rendering the widget at 380/300/240/200/170/150px:
   the badge is a fixed ~186px wide at every one of them and clips below ~185.
   Sizing the box to the badge is what makes the centred box centre the badge.
   Both breakpoints — 195px fits the 342px column at 390 as well. */
.clutch-widget {
	flex: 0 0 auto;
	width: 186px;
	max-width: 100%;
	/* The badge arrives from a third-party script and is 45px tall — the
	   widget's own `data-height`. Until it lands the box is zero, so
	   everything below it in the hero's copy column sat 45px too high and then
	   jumped: a layout shift on the largest band of the most-visited page,
	   caused by a network response we do not control and cannot hurry.
	   Reserving the box is the only fix that works regardless of when Clutch
	   answers, and it costs nothing if they never do. */
	min-height: 45px;
}

/* On a phone the badge is the last thing in the hero's text column, and the
   band below the hero is a `--pull` section with -3rem of margin (the rule that
   sits the /about/ statistics on the hero photograph). At 390px that pull lands
   exactly on the badge — measured 2026-09-03: badge bottom 645, next section
   top 645. The pull is wanted; the collision is not, so the badge carries its
   own clearance: 3rem to pay the pull back, 1rem of air on top. */
@media (max-width: 781px) {
	.adsbox-col--text > div:has(> .clutch-widget) {
		margin-block-end: 4rem;
	}
}

/* Production puts the Subscribe button beside the field, not under it: the
   Elementor form is `email` at width 70 and `button_width: 20` with
   `button_align: end`, so the two share a line and the consent checkbox drops
   below both. Fluent Forms emits them as three sibling .ff-el-group blocks in
   DOM order email / consent / submit, so the submit is re-ordered rather than
   moved in the markup — the markup stays whatever Fluent Forms saved, which is
   what the client edits. */
.adsbox-footer__form .adsbox-form .fluentform fieldset {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	column-gap: var(--wp--preset--spacing--20);
}

/* The nonce/referer paragraph and the screen-reader legend are zero-height
   siblings; without a full-basis they wedge themselves onto the field's line. */
.adsbox-footer__form .adsbox-form .fluentform fieldset > p,
.adsbox-footer__form .adsbox-form .fluentform fieldset > legend {
	flex: 0 0 100%;
	margin: 0;
}

.adsbox-footer__form .adsbox-form .fluentform fieldset > .ff-el-group {
	flex: 1 1 60%;
	margin-block-end: 0;
}

.adsbox-footer__form .adsbox-form .fluentform fieldset > .ff_submit_btn_wrapper {
	order: 2;
	flex: 0 0 auto;
	margin: 0;
}

.adsbox-footer__form .adsbox-form .fluentform fieldset > .ff-el-group:has(.ff-el-tc) {
	order: 3;
	flex: 0 0 100%;
	margin-block-start: var(--wp--preset--spacing--20);
}

/* Below Elementor's mobile breakpoint the bar is logo + burger + booking
   button on 390px. Production solves it by hiding the logo outright
   (`hide_mobile` on the theme-site-logo widget), which leaves a phone visitor
   with no way back to the home page from the header. The logo shrinks here
   instead — same result for the row, without removing the link. */
@media (max-width: 767px) {
	.adsbox-header__bar {
		min-height: 65px;
		padding-inline: 6% 4%;
		gap: var(--wp--preset--spacing--20);
	}

	.adsbox-header .wp-block-site-logo img {
		width: 84px;
		height: 44px;
	}

	.adsbox-header__end .wp-block-button__link {
		padding-inline: 1rem;
		font-size: var(--wp--preset--font-size--x-small);
	}
}

/* ----------------------------------------------------------- closing CTA */
/*
 * The synced cta-band, on nine pages. It is a wp_block reference in
 * post_content, so it never passes through the per-widget typography that
 * layout.py carries for every other heading on the site — which is why it
 * rendered sentence-case at the theme's default heading rhythm while
 * production draws it uppercase at 40px on a 1em line with -3px of tracking.
 * Every value below is the source's own (settings on heading ec829dc / 185d72e2
 * and their siblings), identical across all eight CTA rows.
 */
.adsbox-cta .wp-block-heading {
	font-size: 32px;            /* the source's mobile step; see the two queries */
	font-weight: 400;
	line-height: 1;
	letter-spacing: -2px;
	text-transform: uppercase;
}

.adsbox-cta p {
	font-size: 16px;
	line-height: 26px;
	letter-spacing: -0.5px;
}

@media (min-width: 768px) {
	/* Elementor's tablet step is genuinely SMALLER than its mobile one here
	   (30px against 32px). Reproduced rather than regularised. */
	.adsbox-cta .wp-block-heading {
		font-size: 30px;
	}

	.adsbox-cta p {
		font-size: 15px;
	}
}

@media (min-width: 1025px) {
	.adsbox-cta .wp-block-heading {
		font-size: 40px;
		letter-spacing: -3px;
	}

	.adsbox-cta p {
		font-size: 16px;
	}
}

/*
 * The stack rhythm is the column's own `flex_gap: 50px`, not the theme's 24px
 * blockGap. Specificity has to beat core's `.wp-block-group.is-layout-flow >
 * :not(:first-child) { margin-block-start: var(--wp--style--block-gap) }`,
 * which is two classes and an element — hence the column in the selector rather
 * than an !important.
 */
.wp-block-column > .adsbox-cta > * + * {
	margin-top: 50px;
}

/*
 * `useRootPaddingAwareAlignments` gives every `alignfull` group the root's own
 * 24px side padding, which inside a column is not an escape from a measure —
 * it is 48px taken off one. The source column's padding is `0` on the right and
 * 4% on the left and nothing else; the 4% is on the column, above.
 */
.wp-block-column > .adsbox-cta.alignfull {
	padding-left: 0;
	padding-right: 0;
}

/*
 * The CTA column's geometry, straight out of the source: `width: 34%`,
 * `margin-left: 150px`, `padding: 1% 0 0 4%`. The rebuild was giving it
 * `--adsbox-col-pad` on all four sides — 72px — which cut the text measure to
 * 346px against production's 470 and is most of what "the left panel is way
 * different" was.
 *
 * Desktop only. The source declares `margin: 0` and `padding: 5% 8% 0` for the
 * same column on mobile, and core/columns has stacked by then anyway.
 */
@media (min-width: 1025px) {
	.adsbox-row > .wp-block-column:has(> .adsbox-cta) {
		margin-left: 150px;
		padding: 1% 0 0 4%;
	}
}

/* =========================================================================
 * Responsive visibility, cards, band art — the UGC pass, 2026-09-02
 * =========================================================================
 *
 * Phase 6c again, one level finer. Every one of the thirteen harness
 * dimensions was green while /user-generated-content/ rendered its 92%
 * statistic as another page's three-number band, its six card containers as
 * loose paragraphs, its two photographic bands as flat colour, its three
 * animated cards as 143 MB of GIF, and six of the seven photographs in its
 * closing strip not at all. None of that is a word, a link or a container, so
 * nothing measured it.
 */

/* ------------------------------------------------ responsive visibility */
/*
 * Elementor hides a widget per breakpoint with a class and the rebuild does the
 * same, from the element's OWN mask (never the inherited one — see
 * layout.Box.own_hidden). The breakpoints are Phase 4's, read off kit 671:
 * mobile <= 767, tablet 768-1024, desktop >= 1025.
 *
 * Why classes and not a drop: the two halves of a responsive split are not
 * always the same widget. The UGC page states its four video labels twice —
 * as `heading` widgets hidden on desktop and tablet and as `button` widgets
 * hidden on mobile — so the content-key collapse, which keys on widget type,
 * never paired them and the desktop page rendered both, a heading beside a
 * pill saying the same four things.
 */
@media (min-width: 1025px) {
	.adsbox-hide-desktop {
		display: none !important;
	}
}

@media (min-width: 768px) and (max-width: 1024px) {
	.adsbox-hide-tablet {
		display: none !important;
	}
}

@media (max-width: 767px) {
	.adsbox-hide-mobile {
		display: none !important;
	}
}

/* ------------------------------------------------------------- band art */
/*
 * A container carrying a background photograph AND content. is_media_only()
 * only ever covered the empty ones, so the testimonial backdrop and the
 * mission band's photograph were dropped and both rendered as flat fields.
 *
 * The photograph is a real <img> rather than a CSS background, for the same
 * reasons col_media() gives: it can be the LCP candidate, it carries alt text,
 * and every image crawler can see it.
 */
.adsbox-section--art {
	position: relative;
	isolation: isolate;
	/* These bands state a min-height (700px on the testimonials, 600px on the
	   mission band) and production centres their content in it. Left as flow,
	   the quote card sat at the top of a 700px photograph with 500px of empty
	   picture under it. */
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.adsbox-section--art > .adsbox-section__bg {
	position: absolute;
	inset: 0;
	margin: 0;
	z-index: 0;
	pointer-events: none;
}

.adsbox-section--art > .adsbox-section__bg img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.adsbox-section--art > *:not(.adsbox-section__bg) {
	position: relative;
	z-index: 1;
}

/*
 * The mission band sets white type over a dark photograph and states it —
 * `title_color: #FFF9F3` — which layout._typography now carries as a real
 * textColor attribute. A scrim keeps that legible whatever crop the photograph
 * lands on, which a colour attribute alone cannot promise.
 */
.adsbox-section--art:has(.has-shell-color, .has-base-color, .has-cream-color) > .adsbox-section__bg::after {
	content: "";
	position: absolute;
	inset: 0;
	background: rgba(24, 24, 24, 0.35);
}

/* --------------------------------------------------------------- cards */
/*
 * A nested container that states its own ground and a corner radius is a card.
 * Six on this page: the three "why UGC" boxes and the three audience boxes,
 * #F2ECE4 at a 30px radius. The rebuild dropped both properties, so they read
 * as three runs of loose text with a photograph under each.
 */
.adsbox-card-round {
	border-radius: 30px;
	padding: clamp(1.25rem, 2.2vw, 2rem);
	height: 100%;
}

.adsbox-card-round > :first-child {
	margin-block-start: 0;
}

.adsbox-card-round > :last-child {
	margin-block-end: 0;
}

/* A column that states a radius is a panel, not a column — the enquiry form
   sits in a 30px-rounded dune card beside its carousel. */
.wp-block-column.adsbox-col-round:not(.adsbox-col--media) {
	border-radius: 30px;
}

.adsbox-col--media.adsbox-col-round :is(img, .wp-block-image) {
	border-radius: 30px;
}

/* ------------------------------------------------------ one-up carousel */
/*
 * Every carousel on this page declares `slides_to_show: 1` and a 30px
 * `image_border_radius`. The rebuild ignored both, so a card that holds one
 * photograph at a time showed three side by side, square-cornered.
 */
.adsbox-carousel-x1 .adsbox-carousel__track {
	gap: 0;
	padding-block-end: 0;
	scrollbar-width: none;
}

.adsbox-carousel-x1 .adsbox-carousel__track::-webkit-scrollbar {
	display: none;
}

.adsbox-carousel-x1 .adsbox-carousel__track > * {
	flex: 0 0 100%;
	scroll-snap-align: center;
}

.adsbox-carousel-x2 .adsbox-carousel__track > * {
	flex: 0 0 calc(50% - var(--wp--preset--spacing--30) / 2);
}

.adsbox-carousel-x3 .adsbox-carousel__track > * {
	flex: 0 0 calc(33.333% - var(--wp--preset--spacing--30) * 2 / 3);
}

.adsbox-carousel-round :is(.adsbox-carousel__slide img, img),
img.adsbox-carousel-round,
.adsbox-carousel-round > img {
	border-radius: 30px;
}

/* Production floats its arrows over the picture. Only the one-up tracks get
   this: a shelf of cards keeps the nav under it, where it cannot cover a
   slide. */
.adsbox-carousel-x1 > .adsbox-carousel__nav {
	position: absolute;
	inset: 0;
	margin: 0;
	padding-inline: 0.75rem;
	display: flex;
	align-items: center;
	justify-content: space-between;
	pointer-events: none;
}

.adsbox-carousel-x1 > .adsbox-carousel__nav .adsbox-carousel__btn {
	pointer-events: auto;
	border: none;
	background: rgba(255, 255, 255, 0.82);
	box-shadow: 0 2px 12px rgba(24, 24, 24, 0.18);
	width: 2.4rem;
	height: 2.4rem;
}

.adsbox-carousel-x1 > .adsbox-carousel__nav .adsbox-carousel__btn[disabled] {
	opacity: 0.25;
}

/* Dots, added by carousel.js alongside the arrows. */
.adsbox-carousel__dots {
	display: flex;
	gap: 0.45rem;
	justify-content: center;
	margin-block-start: 0.9rem;
}

.adsbox-carousel__dot {
	appearance: none;
	border: none;
	padding: 0;
	width: 8px;
	height: 8px;
	border-radius: 999px;
	background: rgba(24, 24, 24, 0.22);
	cursor: pointer;
}

.adsbox-carousel__dot[aria-current="true"] {
	background: var(--wp--preset--color--contrast);
}

/* --------------------------------------------------------- testimonial */
/*
 * Production shows one testimonial at a time: a light rounded card floating on
 * the band's photograph, the client's portrait and name on one line and the
 * quote beneath. The markup order stays logo / quote / name — the reading order
 * a screen reader should get — and the grid puts the name back beside its face.
 */
.adsbox-carousel--quotes .adsbox-carousel__track {
	gap: 0;
	scrollbar-width: none;
}

.adsbox-carousel--quotes .adsbox-carousel__track::-webkit-scrollbar {
	display: none;
}

.adsbox-carousel--quotes .adsbox-carousel__track > * {
	flex: 0 0 100%;
	scroll-snap-align: center;
}

/* The measure goes on the CAROUSEL, not on the card. `max-width` on a flex
   child beats its own `flex-basis: 100%`, so two 42rem cards sat side by side
   in a 1440px band and the one-at-a-time carousel showed two at a time. */
.adsbox-carousel--quotes {
	max-width: 46rem;
	margin-inline: auto;
}

.adsbox-quote {
	background: var(--wp--preset--color--shell);
	border-radius: 24px;
	padding: clamp(1.5rem, 3vw, 2.25rem);
	display: grid;
	grid-template-columns: auto 1fr;
	column-gap: 1rem;
	align-items: center;
	color: var(--wp--preset--color--contrast);
}

.adsbox-quote__logo {
	grid-column: 1;
	grid-row: 1;
	margin: 0;
	width: 56px;
}

.adsbox-quote__logo img {
	width: 56px;
	height: 56px;
	border-radius: 999px;
	object-fit: cover;
}

.adsbox-quote > p {
	margin: 0;
}

.adsbox-quote > p:last-child {
	grid-column: 2;
	grid-row: 1;
	font-size: var(--wp--preset--font-size--medium);
}

.adsbox-quote > p:not(:last-child) {
	grid-column: 1 / -1;
	grid-row: 2;
	margin-block-start: 1.1rem;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.6;
}

.adsbox-quote:not(:has(.adsbox-quote__logo)) > p:last-child {
	grid-column: 1 / -1;
}

/* --------------------------------------------------------- one statistic */
/*
 * ONE counter is one number, not the About page's three-number band. The
 * source sets 188px on a black field beside the sentence that explains it.
 */
.adsbox-stat-solo {
	text-align: center;
}

.adsbox-stat-solo .adsbox-stat__value {
	margin: 0;
	font-weight: 400;
	line-height: 1;
	letter-spacing: -0.04em;
	/* 188px in the source. Stated here rather than left to the `display`
	   preset because .adsbox-stat__value already sets a clamp for the About
	   band's cards and would otherwise win by document order. */
	font-size: clamp(4rem, 12vw, 11.75rem);
	color: inherit;
	font-variant-numeric: tabular-nums;
}

.adsbox-stat-solo .adsbox-stat__label {
	margin-block-start: 0.75rem;
	font-size: var(--wp--preset--font-size--small);
	text-transform: uppercase;
	letter-spacing: 0.08em;
}

/* ------------------------------------------------------------- accordion */
/*
 * Production's toggle carries arrow-down.svg / arrow-up.svg at the right of a
 * 1px black rule. "+" and an en dash were a stand-in; these are the marks the
 * source states.
 */
.adsbox-faq .wp-block-details {
	border-bottom: 1px solid var(--wp--preset--color--contrast);
}

/*
 * NOT a flex row. A question is several inline nodes — "What " + <b>social
 * media platforms</b> + " do you cover?" — and each one becomes its own flex
 * item, so `justify-content: space-between` scattered the words of a single
 * sentence across the full 820px. The arrow is positioned out of flow instead,
 * and the question stays one continuous run of text aligned left.
 */
.adsbox-faq .wp-block-details summary {
	display: block;
	position: relative;
	padding-inline-end: 2rem;
	text-align: left;
	font-size: var(--wp--preset--font-size--medium);
}

.adsbox-faq .wp-block-details summary::after {
	content: "\2193";
	float: none;
	position: absolute;
	inset-inline-end: 0;
	top: 0;
	font-size: 1.1em;
	transition: transform 0.2s ease;
}

.adsbox-faq .wp-block-details[open] summary::after {
	content: "\2193";
	transform: rotate(180deg);
}

/*
 * An accordion reads left, and it was inheriting `text-align: center` from
 * `.adsbox-row > .adsbox-col--text`. That centring is right for the copy blocks
 * the rule was written for and wrong here, and it hid: a summary is a flex row,
 * so a question that fits on one line sits at the start of its box and looks
 * left-aligned anyway. Only the three questions long enough to wrap showed it —
 * and their second line was centred under the first while the short questions
 * beside them were flush left.
 */
.adsbox-faq {
	text-align: left;
}

/*
 * A column whose whole content is a card keeps the card's own inset, not the
 * 72px a text column carries: the source states 20px inline padding on the
 * three-up card columns, and 72px a side inside a 33% column left the cards
 * 272px wide against production's ~440.
 */
.adsbox-row > .adsbox-col--text:has(> .adsbox-card-round) {
	padding: 0 20px;
}

/*
 * Two specificity corrections, both measured in a same-origin iframe rather
 * than guessed from a screenshot.
 *
 * 1. `.adsbox-section > :not(.adsbox-row)...` caps a direct child at the 1200px
 *    wide measure, and the band's backdrop <figure> is a direct child — so an
 *    absolutely positioned `inset: 0` element came back 1200px wide, centred,
 *    and both photographic bands rendered as an inset panel on a white field.
 * 2. The same rule chain (six classes) beat `.adsbox-carousel--quotes`, so the
 *    quote track stayed 1152px and showed two testimonials at once.
 */
/*
 * `:not()` takes the specificity of its argument — the same trap the `:has()`
 * exemption for the logo band already records. The six-`:not()` chain above is
 * (0,7,0), so a three-class override lost silently and the backdrop came back
 * 1200px wide and centred. Both are handled by naming the exception INSIDE
 * that chain, above; these state the geometry positively.
 */
.adsbox-section--art > .wp-block-image.adsbox-section__bg {
	max-width: none;
	width: auto;
	margin: 0;
	padding: 0;
}

.adsbox-section > .wp-block-group.adsbox-carousel--quotes {
	max-width: 46rem;
	margin-inline: auto;
}

/*
 * `.adsbox-form` centres itself with `margin-inline: auto`, which is right when
 * it sits straight on a band. Inside a flex column it is not: an auto margin on
 * the cross axis DISABLES `align-items: stretch`, so the form sized to its own
 * content — 363px inside a 641px column — and every field stacked because two
 * 50% cells could not fit. Measured in a same-origin iframe; nothing in the
 * markup shows it.
 */
.wp-block-column > .adsbox-form {
	width: 100%;
	max-width: none;
	margin-block: 0 var(--wp--preset--spacing--50);
}

/*
 * The enquiry panel's own inset. The source states `padding: 20px 50px 0`, and
 * the column's 72px text padding left the form narrower than the card it sits
 * in was wide.
 */
.wp-block-column.adsbox-col-round:not(.adsbox-col--media) {
	padding: 20px 50px;
}

/*
 * A flex item's `min-width` is `auto`, so a slide whose content has a wide
 * min-content — the quote card is a grid, and `1fr` will not shrink past the
 * longest unbreakable run in it — pushes the whole track wider than the phone.
 * Measured in a 390px iframe: the quotes carousel came back 573px inside a
 * 390px section. The page did not scroll sideways, so no screenshot showed it.
 */
.adsbox-carousel__track > * {
	min-width: 0;
}

.adsbox-quote > p {
	min-width: 0;
	overflow-wrap: anywhere;
}

.adsbox-section > .wp-block-group.adsbox-carousel--quotes {
	max-width: min(46rem, 100%);
}

/* =========================================================================
 * UGC pass 2 — parallax, card gutters, the quote card, the slide band
 * =========================================================================
 * Kaloyan's second side-by-side. Everything below is a value the Elementor
 * source states and the rebuild was deciding for itself.
 */

/* ------------------------------------------------------------- parallax */
/*
 * `background_attachment: fixed` on the two photographic bands. The backdrop
 * is an <img>, not a CSS background, so the effect is built the way that
 * actually works with an element: `clip-path` on the section makes it the
 * containing block for a `position: fixed` descendant, so the photograph is
 * pinned to the viewport and clipped to the band. That is `background-
 * attachment: fixed` semantics without giving up the alt text, the LCP
 * candidate and the crawlable <img>.
 *
 * Desktop only, and only without a reduced-motion request: iOS Safari does not
 * honour fixed backgrounds, the clip-path variant janks on it, and the whole
 * point is decorative. Everything else falls back to the absolute cover above.
 */
@media (min-width: 1025px) and (prefers-reduced-motion: no-preference) {
	.adsbox-section--fixedbg {
		clip-path: inset(0);
	}

	.adsbox-section--fixedbg > .adsbox-section__bg {
		position: fixed;
		inset: 0;
		width: 100vw;
		height: 100vh;
	}

	.adsbox-section--fixedbg > .adsbox-section__bg img {
		width: 100vw;
		height: 100vh;
	}
}

/* --------------------------------------------------------- card gutters */
/*
 * Production draws the card's carousel arrows in the card's own margin, beside
 * the photograph. Ours sat on top of it — which is what "the photos are
 * overlapping" was.
 */
.adsbox-card-round .adsbox-carousel-x1 {
	padding-inline: 2.25rem;
}

/*
 * `navigation` is a source value, not a house style: the three "why UGC"
 * carousels state `arrows`, and the rebuild drew three dots under every one.
 */
.adsbox-carousel-nodots > .adsbox-carousel__dots {
	display: none;
}

.adsbox-carousel-noarrows > .adsbox-carousel__nav {
	display: none;
}

/*
 * A carousel is NOT a split-section photograph. `.adsbox-col--media` stretches
 * its image to the column's full height with `object-fit: cover`, which is
 * right for a hero half and wrong for a carousel — it is what turned the FAQ
 * photograph into a 1,400px column-height crop where production shows one
 * landscape frame at its own aspect.
 */
.adsbox-row > .adsbox-col--media > .adsbox-carousel,
.adsbox-row > .adsbox-col--media > .wp-block-group > .adsbox-carousel {
	width: 100%;
	height: auto;
	min-height: 0;
}

.adsbox-carousel img,
.adsbox-carousel__slide img {
	height: auto;
	min-height: 0;
	object-fit: contain;
}

/* ------------------------------------------------------ the quote card */
/*
 * `image_size: 80`, `title_typography_text_transform: uppercase`,
 * `content_typography_font_size: 16`, `background_color: #FFF9F3` — all stated
 * by the widget. The portrait and the name are ONE centred group; the previous
 * two-column grid pinned the portrait to the far left of the card and left the
 * name floating, which is what read as broken.
 */
.adsbox-quote {
	display: block;
	max-width: none;
	background: var(--wp--preset--color--shell);
	border-radius: 24px;
	padding: clamp(1.75rem, 3.2vw, 2.75rem) clamp(1.5rem, 4vw, 3.5rem);
	text-align: center;
	color: var(--wp--preset--color--contrast);
}

.adsbox-quote__head {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 1rem;
	margin: 0;
}

.adsbox-quote__logo {
	margin: 0;
	flex: 0 0 auto;
	width: 80px;
}

.adsbox-quote__logo img {
	width: 80px;
	height: 80px;
	border-radius: 999px;
	object-fit: cover;
}

.adsbox-quote__who {
	text-align: left;
}

.adsbox-quote__name {
	margin: 0;
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.4;
}

.adsbox-quote__from {
	margin: 0;
	font-size: var(--wp--preset--font-size--small);
	text-transform: uppercase;
	letter-spacing: 0.02em;
}

.adsbox-quote__body {
	margin-block-start: 1.75rem;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.75;
	letter-spacing: 0.01em;
}

/* The arrows sit OUTSIDE the card, in the band, which is why the track needs
   the gutter rather than the card. */
.adsbox-section > .wp-block-group.adsbox-carousel--quotes {
	/* Measured off production: the card is ~594px wide and ~428px tall at a
	   1470px viewport — narrower and taller than the first cut, which is what
	   gives the quote its six-line block rather than a wide five-line one. */
	max-width: min(46rem, 100%);
	padding-inline: 3rem;
}

/* --------------------------------------------------------- the slide band */
/*
 * `slides_height: 300px`, one slide at a time, centred on the band's own
 * ground. Not a card: the source states no radius, no shadow and the same
 * colour the container already carries.
 */
.adsbox-carousel--slides .adsbox-carousel__track > * {
	flex: 0 0 100%;
	scroll-snap-align: center;
}

/* The measure goes on the TRACK's padding, never on the slide: `max-width` on
   a flex child beats its own `flex-basis: 100%`, so two slides sat side by side
   — the same trap the quote card hit. */
.adsbox-slide {
	min-height: 300px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1.25rem;
	max-width: none;
	padding: clamp(1.5rem, 3vw, 2.5rem) max(0px, calc((100% - 46rem) / 2));
	text-align: center;
}

.adsbox-slide > * {
	margin-block: 0;
}

.adsbox-section > .wp-block-group.adsbox-carousel--slides {
	max-width: none;
	padding-inline: 3rem;
}

/* ------------------------------------------------------------- the form */
/*
 * `.adsbox-row > .adsbox-col--text` centres its column, which is right for a
 * heading and wrong for a form: every field label sat centred above its input
 * where production has them left, hard against the field.
 */
.adsbox-form,
.adsbox-form .fluentform {
	text-align: left;
}

.adsbox-form .fluentform .ff_submit_btn_wrapper {
	text-align: center;
}

/* --------------------------------------------------------------- the FAQ */
/*
 * `title_padding: 0 0 15px`, `space_between: 10px`, `border_width: 1px`,
 * `border_color: #000000`. The rebuild's rows were on the section's own
 * rhythm, roughly double.
 */
.adsbox-faq .wp-block-details {
	padding-block: 0 15px;
	margin-block-end: 10px;
}

.adsbox-faq .wp-block-details summary {
	/* `title_typography_font_size: 18` — theme.json's `medium` step. */
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 400;
	line-height: 1.55;
}

/* --------------------------------------------------- the closing strip */
/*
 * Four of the seven photographs sit inside an `adsbox-hide-mobile` wrapper,
 * because the source hides them on a phone. The media-column sizing rules were
 * written as direct-child selectors, so those four kept their natural size
 * while the other three stretched — three tall images, four short ones, and
 * the band's black ground showing through under them.
 */
/*
 * `.adsbox-carousel-round` is a one-slide carousel that collapsed to a plain
 * image — the FAQ photograph. It keeps its own aspect for the same reason a
 * carousel does; stretched to the column it became a 703px crop where
 * production shows one landscape frame.
 */
.adsbox-row > .adsbox-col--media .wp-block-image:not(.adsbox-carousel__slide):not(.adsbox-carousel-round) {
	display: block;
	width: 100%;
	margin: 0;
	height: 100%;
}

.adsbox-row > .adsbox-col--media .wp-block-group {
	height: 100%;
}

.adsbox-row > .adsbox-col--media .wp-block-image:not(.adsbox-carousel__slide):not(.adsbox-carousel-round) img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	min-height: 0;
}

/*
 * A row of six or more media columns is a STRIP, not a split section: the UGC
 * page closes with seven photographs tiled edge to edge at about 270px tall on
 * production. `.adsbox-col--media`'s `clamp(16rem, 34vw, 34rem)` is the right
 * floor for a hero half and roughly double what a strip wants.
 */
/*
 * EVERY column must be a media column, not just six of them. The four video
 * mockups sit in a row of EIGHT — four photographs and four labels the source
 * hides on desktop — so a bare `nth-child(6)` counted the hidden labels and
 * cropped four perfectly good phone screenshots to a 272px strip.
 */
.adsbox-row:has(> .wp-block-column:nth-child(6)):not(:has(> .wp-block-column:not(.adsbox-col--media))) > .adsbox-col--media .wp-block-image {
	min-height: 0;
	height: clamp(11rem, 19vw, 17rem);
}

.adsbox-row:has(> .wp-block-column:nth-child(6)):not(:has(> .wp-block-column:not(.adsbox-col--media))) > .adsbox-col--media .wp-block-image img {
	height: 100%;
}

/* =========================================================================
 * UGC pass 3 — snap accuracy, band rhythm, column alignment
 * ========================================================================= */

/* ------------------------------------------------- carousel snap accuracy */
/*
 * The middle slide of every carousel sat a few pixels off — a sliver of the
 * previous image on one side, the current one clipped on the other. Two causes,
 * both here:
 *
 *  * `scroll-snap-align: center` on an item exactly as wide as the snapport
 *    leaves two neighbouring snap points equidistant, so the browser is free to
 *    pick either. `start` on an item that fills the track is unambiguous.
 *  * `scroll-padding-inline` insets the snapport. Inherited from the shared
 *    track rule, it shifted every snap position on the one-up tracks, which
 *    carry no gutter of their own.
 */
.adsbox-carousel-x1 .adsbox-carousel__track,
.adsbox-carousel--quotes .adsbox-carousel__track,
.adsbox-carousel--slides .adsbox-carousel__track {
	scroll-padding-inline: 0;
}

.adsbox-carousel-x1 .adsbox-carousel__track > *,
.adsbox-carousel--quotes .adsbox-carousel__track > *,
.adsbox-carousel--slides .adsbox-carousel__track > * {
	scroll-snap-align: start;
}

/* Every testimonial card is the same size. The track's items were sized by
   their own copy, so a short quote made a short card and the difference read as
   the middle one being broken. */
.adsbox-carousel--quotes .adsbox-carousel__track {
	align-items: stretch;
}

/* No explicit height: an item that states one opts OUT of the flex line's
   stretch, which is precisely what left three testimonial cards at three
   different heights. */
.adsbox-quote {
	display: flex;
	flex-direction: column;
	justify-content: center;
}

/* ---------------------------------------------------- a media column's gutter */
/*
 * Production insets a carousel inside its column and puts the arrows in the
 * space that makes — the process photograph stops about 6% short of the band's
 * right edge rather than bleeding off it, and no arrow sits on a picture.
 */
.adsbox-row > .adsbox-col--media > .adsbox-carousel,
.adsbox-row > .adsbox-col--media > .wp-block-group > .adsbox-carousel {
	padding-inline: 2.5rem;
}

/* ------------------------------------------------------------ band rhythm */
/*
 * A band that is nothing but a section heading is a title, and production
 * leaves air under it before the cards start. Three on this page — "Защо
 * потребителско съдържание", "Ще подготвим за вас 4 типа" and "За какъв тип
 * бизнеси" — and all three ran straight into the row below.
 */
.adsbox-section:has(> h2:only-child),
.adsbox-section:has(> h3:only-child) {
	padding-block-end: clamp(2rem, 3.5vw, 3.5rem);
}

/* --------------------------------------------- the process band's carousel */
/*
 * The process carousel IS the media column of its band, and the rebuild handed
 * it the whole column: 640x486 of picture, edge to edge, touching the mission
 * band above it and the dune steps below with nothing in between. The source
 * widget states `_padding: 50px` on all four sides and both sites lay the band
 * out inside the same 1660px boxed container, so production shows a smaller
 * frame with air around it.
 *
 * The air is stated as the carousel's padding and the row's, not as the band's
 * margin: both are inside the band, so its mist ground paints them instead of
 * the page's white showing through — the seam the button band below had.
 *
 * `.adsbox-col--media > .adsbox-carousel-x1` is this carousel and no other. The
 * three "why UGC" cards nest theirs in a card group, the enquiry row nests its
 * in a `.adsbox-hide-mobile` wrapper, and a one-up carousel exists on no page
 * but the UGC pair.
 */
.adsbox-row > .adsbox-col--media > .adsbox-carousel-x1 {
	padding: 50px;
	max-width: 620px;
	margin-inline: auto;
}

.adsbox-section:has(> .adsbox-row > .adsbox-col--media > .adsbox-carousel-x1) > .adsbox-row {
	padding-block: var(--wp--preset--spacing--50);
}

/* ------------------------------------------ the audience cards, and the FAQ */
/*
 * Two more bands on the same page butt straight into their neighbours: the
 * three audience cards ("Fashion and beauty", "Commercial establishments",
 * "Technology and lifestyle") end with their photographs against the enquiry
 * band below, and the FAQ column starts on the very top edge of its own band.
 * Neither band states padding in the source and neither is `--pad`, so nothing
 * here is fighting an inline value.
 *
 * The FAQ selector asks for a media column as well, which is what separates the
 * UGC pair's two-column FAQ from /faq/, /faq-2/ and /landing/, where the
 * accordion is either bare on the band or paired with a second text column.
 */
.adsbox-section:not(.adsbox-section--pad):has(> .adsbox-row > .adsbox-col--text > .adsbox-card-round) {
	padding-block-end: clamp(2rem, 3.5vw, 3.5rem);
}

.adsbox-section:has(> .adsbox-row > .adsbox-col--text > .adsbox-faq):has(> .adsbox-row > .adsbox-col--media) {
	padding-block-start: clamp(2rem, 3.5vw, 3.5rem);
}

/*
 * A standalone button band used to be forced transparent, on the reasoning that
 * production paints it the same colour as the bands either side. It is: on the
 * UGC pair those bands are shell and so is this one, and the source states it —
 * `has-shell-background-color` is in the markup. Transparent showed the page's
 * white through instead, i.e. it MADE the seam it was written to remove. Five
 * bands site-wide are one button; three declare base or no colour, where
 * transparent and the declared ground paint identically, so honouring the
 * source's colour costs nothing and fixes the two that are cream.
 */

/* ------------------------------------------------- the enquiry row's alignment */
/*
 * A row pairing a photograph with a panel is top-aligned on production. Ours
 * centred both — `.adsbox-row > .wp-block-column { justify-content: center }`
 * — so the shorter of the two floated and the pair read as crooked.
 */
/*
 * The gap is a ROW gap only. A column gap here is counted twice: these rows
 * declare `space-between` and their columns already sum below 100% — the
 * four-up platform band is 4x24%, which leaves 58px of air at 1440 — so the
 * extra 36px of `gap` pushed the line to 1492px in a 1440px row. Under
 * `flex-wrap: nowrap` that was invisible, because the columns simply shrank;
 * the moment wrapping actually worked (see .wp-block-columns.adsbox-row above)
 * the fourth card dropped to a line of its own and the band grew 400px, on
 * /social-media/ and /ppc-pay-per-click/ both. Line-breaking happens before
 * flex-shrink, so a `min-width` floor cannot save a line that is already over.
 */
.adsbox-row:has(> .adsbox-col-round) {
	align-items: flex-start;
	row-gap: clamp(1rem, 2.5vw, 2.5rem);
	column-gap: 0;
}

.adsbox-row:has(> .adsbox-col-round) > .wp-block-column {
	justify-content: flex-start;
}

/*
 * The enquiry panel's own trailing space. `.adsbox-form` carries a 72px
 * padding-bottom and a 50px bottom margin, which are right for a form that is
 * the whole band on /contact/ and wrong inside a rounded panel that already
 * states 20px of its own: measured, 175px of empty dune sat under "Запази
 * фотосесия" — more air than the button, its label and its top margin put
 * together. The submit row's 35px is Fluent Forms' generic group spacing and it
 * is the last group, so it is trailing space too.
 */
.adsbox-col-round .adsbox-form {
	padding-block-end: 0;
	margin-block-end: var(--wp--preset--spacing--20);
}

/* The submit row by name, NOT `.ff-el-group:last-child`: Fluent Forms puts each
   field group inside its own `.ff-t-cell`, so every field in the two-column half
   of this form is a last child, and zeroing that collapsed the 35px between all
   four of them into a stack with no air. */
.adsbox-col-round .adsbox-form .fluentform .ff_submit_btn_wrapper {
	margin-block-end: 0;
}

/* ===================================================================
 * THE PHONE
 * ===================================================================
 *
 * Every rule above this line was written against a 1440px viewport and
 * measured against production's desktop capture. Nothing in the harness
 * measures a 390px one — the same shape as Phase 6b's flat stack and 6c's
 * wrong typography: thirteen green dimensions over a layout nobody had
 * looked at.
 *
 * Measured in a same-origin 390px iframe across all 28 pages before any of
 * this was written: no page overflows horizontally (scrollWidth is 390
 * everywhere) and no row fails to stack. What is wrong on a phone is
 * *composition* — desktop geometry surviving into a viewport a quarter of
 * its width. Each block below names the number it was written against.
 */

/* ------------------------------------------------------------------
 * Elementor's min-height is a desktop value
 *
 * `min_height` is carried out of the source as a real block attribute
 * (blocks.section), and it is stated per container at the desktop
 * breakpoint: 500px on every /our-services/ band, 750px on the homepage's
 * "WE GROW YOUR BUSINESS SALES," split. On a 390px viewport the same band
 * holds a heading, three lines of copy and a photograph — about 470px of
 * content inside a 750px floor, so the page shows a 280px void under every
 * one of them.
 *
 * It is an inline style (core serialises `dimensions.minHeight` itself), so
 * a stylesheet needs `!important` to reach it — the same price the negative
 * `--pull` margin already pays two hundred lines up.
 *
 * The `--art` bands are exempt and must stay exempt: their photograph is an
 * absolutely positioned `.adsbox-section__bg` and the stated height is the
 * only thing giving that band any height at all.
 */
/* The process carousel's 50px desktop frame is a sixth of a 390px viewport on
   each side, which takes the picture down to 290px. The source states the phone
   value separately — `_padding_mobile: 0 10 50 10` — and it is the one to use. */
@media (max-width: 781px) {
	.adsbox-row > .adsbox-col--media > .adsbox-carousel-x1 {
		padding: 0 10px 50px;
	}
}

@media (max-width: 781px) {
	.adsbox-section:not(.adsbox-section--art)[style*="min-height"] {
		min-height: 0 !important;
	}
}

/* ------------------------------------------------------------------
 * A band's stated height belongs to the row inside it, not to white space
 *
 * The section carries the source's `min_height` as an inline style and lays
 * its children out in flow, so the row inside is only as tall as its own
 * content: on /our-services/ the five bands are 500px floors holding rows of
 * 419-490px, which renders as a white strip under each photograph and above
 * the next band. Elementor's container IS the flex parent, so the row there
 * fills the stated height and the split reads as one continuous field.
 *
 * `min-height: inherit` takes the parent's own computed value, so this needs
 * no second copy of the number and follows the mobile zeroing above for free.
 * The `--art` bands are exempt for the same reason they are exempt there:
 * their height is scaffolding for an absolutely positioned photograph.
 */
.adsbox-section:not(.adsbox-section--art)[style*="min-height"] > .adsbox-row {
	min-height: inherit;
}

/* ------------------------------------------------------------------
 * A stacked column still needs the band's rhythm
 *
 * `.adsbox-section--pad > .adsbox-row > .wp-block-column { padding-block: 0 }`
 * exists because the band already carries the source's own `3% 8%` and the
 * columns must not add a second helping beside it. Above 782px that is
 * right — the columns sit side by side inside one padded band.
 *
 * Below 782px they stack, and the rule is what put /contact/'s phone
 * number, street address and email address in one unbroken block with no
 * air between them: three columns, zero vertical padding, a 3% band pad
 * shared by all three. Its specificity (0,3,0) beat the mobile column rule
 * (0,2,0) regardless of order, which is why the existing mobile block did
 * not save it.
 */
@media (max-width: 781px) {
	.adsbox-section--pad > .adsbox-row > .wp-block-column {
		padding-block: var(--wp--preset--spacing--40);
	}

	.adsbox-section--pad > .adsbox-row > .wp-block-column:first-child {
		padding-block-start: 0;
	}

	.adsbox-section--pad > .adsbox-row > .wp-block-column:last-child {
		padding-block-end: 0;
	}
}

/* ------------------------------------------------------------------
 * The four-up service row is two-up on a phone, not one-up
 *
 * The cards are `aspect-ratio: 5 / 9`, which is production's four-up
 * portrait and is right at the ~330px column a 1440px viewport gives them.
 * Stacked full-width by core's own below-782px rule they become 342 x 616
 * on a phone and 768 x 1382 on a small tablet — measured, the homepage's
 * card band was 2878px tall at 390 and 5393px at 768, four screens of
 * scrolling for four links.
 *
 * Two-up at 3:4 puts the whole set on one screen, which is what the section
 * is for. `display: grid` on the row also disposes of core's
 * `flex-basis: 100% !important` without having to outrank it.
 */
@media (max-width: 781px) {
	/* `.adsbox-section > …` and not the bare class: the band-level rule that
	   gives this row its desktop gutter is one class heavier and would keep
	   the 72px padding and the 24px gap. */
	.adsbox-section > .adsbox-row:has(.adsbox-service-card),
	.adsbox-row:has(.adsbox-service-card) {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--wp--preset--spacing--20);
		padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--30);
	}

	/* The card owns the aspect, not the picture inside it. Left on the
	   <img>/<video> the two cards in a row sized independently, the grid row
	   took the taller of them, and `justify-content: center` on the column
	   floated each card in the leftover — two cards at different heights with
	   a band of empty ground under the pair. */
	/* The generic mobile rule puts 48px of vertical padding on every text
	   column, and a card column is not a text column: it made each grid cell
	   276px around a 212px card. */
	.adsbox-row:has(.adsbox-service-card) > .wp-block-column,
	.adsbox-row:has(.adsbox-service-card) > .adsbox-col--text {
		justify-content: stretch;
		padding: 0;
	}

	/* `height: 100%` here would win over the aspect: the column stretches to
	   the grid row, the row is sized by whichever card is tallest, and the
	   card then takes that height instead of its own 3:4. */
	.adsbox-service-card {
		aspect-ratio: 3 / 4;
	}

	.adsbox-service-card > .wp-block-image,
	.adsbox-service-card > .adsbox-card-video {
		height: 100%;
	}

	.adsbox-service-card img,
	.adsbox-service-card video {
		height: 100%;
		aspect-ratio: auto;
		object-fit: cover;
	}

	.adsbox-service-card > :is(h2, h3, h4) {
		padding: 0.85rem 0.85rem 2rem;
		font-size: 0.9375rem;
		letter-spacing: -0.03em;
	}
}

/* ------------------------------------------------------------------
 * A media column that stacks is a photograph, not a half of a split
 *
 * `.adsbox-col-media` states `height: 100%` so it can match the height of
 * the copy beside it. Stacked, "beside it" no longer exists and the
 * percentage resolves against an auto-height column, so the safe thing is
 * to say so rather than to rely on the circular-reference fallback.
 * `min-height` is already zeroed in the older mobile block; the cap here
 * stops a 3:4 portrait taking a whole screen on its own.
 */
@media (max-width: 781px) {
	.adsbox-col-media,
	.adsbox-row > .adsbox-col--media > .wp-block-image {
		height: auto;
	}

	.adsbox-col-media img,
	.adsbox-row > .adsbox-col--media img:not(.adsbox-compare img):not(.adsbox-card-video img) {
		height: auto;
		max-height: 60vh;
	}

	/* Stacked, the video is a picture in the flow and sizes to its own
	   aspect; the desktop fill would leave it 0 tall in an auto column. */
	.adsbox-row > .adsbox-col--media > .adsbox-card-video,
	.adsbox-row > .adsbox-col--media > .adsbox-card-video video {
		height: auto;
	}
}

/* ------------------------------------------------------------------
 * The mobile menu is a tree, and core flattens it
 *
 * Phase 6c's fifth pass made the header nav a tree: the Bulgarian menu
 * nests the UGC page under Услуги, which is the only nested item the site
 * has, and core/navigation-submenu draws the chevron and the hover panel on
 * desktop. Below 600px core swaps the whole thing for an overlay — and the
 * overlay undoes both halves of that:
 *
 *   .is-menu-open … .wp-block-navigation__submenu-icon { display: none }
 *   .is-menu-open … .has-child .wp-block-navigation__submenu-container {
 *       height: auto; opacity: 1; visibility: visible; position: static }
 *
 * So on a phone the chevron is gone and the child list is permanently
 * expanded, unindented, at the same weight as its siblings — measured, the
 * Bulgarian overlay showed five equal items with UGC СЪДЪРЖАНИЕ reading as
 * a fifth service rather than as one. That is the same information
 * architecture fault the desktop nav had before Phase 6c, wearing the look
 * of a deliberate choice.
 *
 * The markup already carries everything needed: core prints a real
 * <button class="wp-block-navigation-submenu__toggle"> with the chevron
 * inside it, wired to the Interactivity API, and its own
 * `[aria-expanded="true"] ~ .submenu-container` rule. Only the overlay's
 * overrides have to be taken back. Nothing here is JavaScript.
 *
 * Specificity is the whole game: core's open rule is five classes
 * (.responsive-container.is-menu-open .container-content .has-child
 * .submenu-container) with the rest inside :where(), which contributes
 * nothing. `.adsbox-nav` is on the <nav> and makes every selector below
 * one class heavier.
 */

/* The chevron is a real button. It is what the toggle IS, so showing it is
   showing the control, not decorating it. 44px of touch target either way. */
.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	.wp-block-navigation__submenu-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	margin: 0;
	padding: 0;
}

.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	.wp-block-navigation__submenu-icon svg {
	width: 14px;
	height: 14px;
	transition: rotate 0.2s ease;
}

/* Label and chevron on one line, the panel on the next. The overlay makes
   every navigation item `flex-direction: column`, which would stack the
   chevron under its own label. */
.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	.wp-block-navigation-item.has-child {
	flex-flow: row wrap;
	align-items: center;
	justify-content: center;
}

/* Closed until the chevron is tapped. `height: 0` as well as `visibility`,
   because core keeps a closed submenu at both and measuring only one is how
   a 26px-tall panel gets mistaken for a clipping bug. */
.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	.has-child
	.wp-block-navigation__submenu-container {
	flex-basis: 100%;
	height: 0;
	min-width: 0;
	padding: 0;
	overflow: hidden;
	visibility: hidden;
	opacity: 0;
}

.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	.has-child
	.wp-block-navigation-submenu__toggle[aria-expanded="true"]
	~ .wp-block-navigation__submenu-container {
	height: auto;
	padding-block: var(--wp--preset--spacing--20) 0;
	overflow: visible;
	visibility: visible;
	opacity: 1;
}

/* A child reads as a child: lighter, smaller, and set in from the parents
   it hangs under. */
.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	.wp-block-navigation__submenu-container
	.wp-block-navigation-item__content {
	font-size: var(--wp--preset--font-size--x-small);
	opacity: 0.75;
}

/* The overlay itself: production's menu is centred and generously spaced,
   and core's default gap is the header's own 70px clamp, which is a lot of
   scrolling on a phone. */
.adsbox-nav .wp-block-navigation__responsive-container.is-menu-open {
	padding-block-start: 5.5rem;
}

.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__container {
	gap: var(--wp--preset--spacing--40);
}

/* ------------------------------------------------------------------
 * The overlay is a sheet over the page, not a blank white screen
 *
 * core paints `.is-menu-open` with `background-color: inherit`, and the
 * header group is `has-base-background-color`, so the phone menu is an
 * opaque white rectangle with four items in the middle of it and the rest
 * of the screen blank under them. Nothing tells the reader the page is
 * still behind the menu.
 *
 * A translucent sheet plus `backdrop-filter` keeps the page visible and
 * unreadable at the same time, which is what says "this is temporary".
 * The blur is progressive-enhancement: without support the colour alone
 * still has to be opaque enough to read black text on, so the fallback is
 * a near-solid white and the blurred branch raises the transparency. Two
 * declarations, feature-queried — not one rgba() that is a compromise
 * between both.
 *
 * `-webkit-backdrop-filter` is not optional; iOS Safari shipped the
 * prefixed property for years and it is the browser this menu is for.
 *
 * The selector is core's own, one class heavier. core paints the white with
 * `.wp-block-navigation:not(.has-background) .responsive-container.is-menu-open
 * :not(.disable-default-overlay)` — five classes. `.adsbox-nav .container
 * .is-menu-open` is three, so the first attempt at this lost silently: the
 * animation on the same element applied and the colour did not, which is
 * exactly the shape of bug that reads as "the CSS didn't load".
 */
.adsbox-nav.wp-block-navigation:not(.has-background)
	.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) {
	background-color: rgba(255, 255, 255, 0.96);
}

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.adsbox-nav.wp-block-navigation:not(.has-background)
		.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) {
		background-color: rgba(255, 255, 255, 0.72);
		-webkit-backdrop-filter: blur(18px) saturate(1.4);
		backdrop-filter: blur(18px) saturate(1.4);
	}
}

/* The close button was flush against the right edge of the screen. core
   gives the open container a `clamp(1rem, root-padding, 20px)` inset and
   then places the button at `right: 0` — but an absolutely positioned
   element resolves against its containing block's *padding box*, so that
   padding is exactly what `right: 0` skips over. The inset has to be on
   the button. 44px square is the tap target the header already uses.

   The button is absolute inside `.responsive-dialog`, which core positions
   relatively — so with the overlay's own 5.5rem top padding the X measured
   104px down the screen, floating between the top edge and the first menu
   item. Taking the dialog out of the positioning chain resolves the button
   against the fixed container instead, which is the box it looks like it
   belongs to. */
.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-dialog {
	position: static;
}

.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-close {
	top: 1rem;
	right: 1rem;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	border-radius: 999px;
}

.adsbox-nav
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-close:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/* Opening the menu is a state change with no transition to hang off — the
   class is added and the element goes from `display: none`. `animation`
   runs on class application, which `transition` cannot do, so the sheet
   fades its blur in and the items arrive under it a beat later.
   `animation-fill-mode: both` holds the first frame during the delay;
   without it every item flashes at full opacity for one frame before its
   delay starts. Closing is instant — core removes the class and the
   element is gone; animating that needs JS and is not worth it. */
@media (prefers-reduced-motion: no-preference) {
	.adsbox-nav .wp-block-navigation__responsive-container.is-menu-open {
		animation: adsbox-nav-sheet 0.24s ease-out both;
	}

	.adsbox-nav
		.wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__container
		> .wp-block-navigation-item,
	.adsbox-nav
		.wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__responsive-container-close {
		animation: adsbox-nav-item 0.28s cubic-bezier(0.16, 1, 0.3, 1) both;
	}

	.adsbox-nav
		.wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__container
		> .wp-block-navigation-item:nth-child(1) {
		animation-delay: 0.06s;
	}

	.adsbox-nav
		.wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__container
		> .wp-block-navigation-item:nth-child(2) {
		animation-delay: 0.11s;
	}

	.adsbox-nav
		.wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__container
		> .wp-block-navigation-item:nth-child(3) {
		animation-delay: 0.16s;
	}

	.adsbox-nav
		.wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__container
		> .wp-block-navigation-item:nth-child(n + 4) {
		animation-delay: 0.21s;
	}

	/* The chevron already rotates; the panel it opens should fade rather
	   than appear. Only opacity — the panel's height goes 0 → auto, which
	   does not interpolate, and a transition naming it would silently do
	   nothing while reading as if it worked. */
	.adsbox-nav
		.wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__responsive-container-content
		.has-child
		.wp-block-navigation__submenu-container {
		transition: opacity 0.2s ease;
	}
}

@keyframes adsbox-nav-sheet {
	from {
		opacity: 0;
		-webkit-backdrop-filter: blur(0);
		backdrop-filter: blur(0);
	}

	to {
		opacity: 1;
	}
}

@keyframes adsbox-nav-item {
	from {
		opacity: 0;
		translate: 0 12px;
	}

	to {
		opacity: 1;
		translate: 0 0;
	}
}

/* ------------------------------------------------------------------
 * Stacked, the photograph always comes first
 *
 * The split sections alternate on purpose: /our-services/ runs
 * [text | video], [video | text], [text | video], [video | text] so the eye
 * zig-zags down the page. Stacked in source order that alternation becomes
 * text, video, video, text, text, video — two videos butted against each
 * other with nothing between them, which is what reads as "two gifs
 * cluttered together" on a phone. There is no zig-zag at one column wide;
 * there is only rhythm, and the rhythm production's own mobile sections use
 * is picture, then words.
 *
 * `order` on the column only, so nothing about the desktop row moves.
 */
@media (max-width: 781px) {
	.adsbox-row > .adsbox-col--media {
		order: -1;
	}
}

/* ------------------------------------------------------------------
 * Grids of cards go two-up, not one-up
 *
 * core's grid layout is `repeat(auto-fill, minmax(16rem, 1fr))`, so at 390px
 * every grid on the site collapses to a single column. On /about/ that is
 * fifteen team portraits at ~456px each — 7,505px of scrolling measured, for
 * a section whose job is "here is the team", and the same shape as the
 * service-card band two blocks up.
 *
 * Two-up at 16rem would still be one column, so the minimum has to come down
 * with the count. `minmax(0, 1fr)` twice is unconditional and needs no
 * guessing about what fits.
 */
@media (max-width: 781px) {
	.wp-block-group.is-layout-grid:has(> .adsbox-flip) {
		grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
		gap: var(--wp--preset--spacing--20) !important;
	}

	.adsbox-flip__overlay :is(h1, h2, h3, h4, h5, h6) {
		font-size: var(--wp--preset--font-size--small);
	}

	.adsbox-flip__overlay p {
		font-size: var(--wp--preset--font-size--x-small);
	}

	/* Three stacked statistic cards at the desktop 15rem floor is 818px of
	   band for three numbers. They still need to read as cards. */
	.adsbox-stat {
		min-height: 9rem;
		padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--30);
	}

	.adsbox-stat__value {
		font-size: clamp(2.75rem, 14vw, 3.5rem);
	}
}

/* ------------------------------------------------------------------
 * A band's stated padding is a desktop value too
 *
 * `layout._box_style()` carries a container's declared vertical padding as a
 * real `spacing.padding` attribute, and Elementor states it at the desktop
 * breakpoint: the homepage's service band declares `padding: 100px 0`, which
 * on a 390px viewport is 200px of empty ground around a 547px row — a third
 * of the band. The percentage ones go the other way: `2%` is 7.8px at 390 and
 * reads as no band at all.
 *
 * Both are replaced by one viewport-relative step. It is `!important` because
 * core serialises the attribute into an inline style, which is the same price
 * the negative `--pull` margin and the stated `min-height` already pay.
 */
@media (max-width: 781px) {
	.adsbox-section--pad {
		padding-block: clamp(1.5rem, 8vw, 3rem) !important;
	}
}

/* =========================================================================
 * /user-generated-content/ on a phone — 2026-09-03
 * =========================================================================
 *
 * Four faults, reported off a full-page 390px capture with all sixteen
 * dimensions green. Check M asserts that a page does not scroll sideways, that
 * no band is a void and that nothing is said twice; none of that is the same
 * question as "is this the right thing in the right order". Every one of these
 * is a row that a rule written for a two-column SPLIT section reached and
 * should not have.
 *
 * Measured, not guessed: the live DOM in a same-origin 390px iframe, the same
 * probe check M uses.
 */

@media (max-width: 781px) {
	/*
	 * 1. A column whose only content is hidden at this breakpoint is still a
	 * column.
	 *
	 * `.adsbox-hide-mobile` sits on the GROUP inside the column, never on the
	 * column, because that is where the source's own mask sits. Stacked, the
	 * column still takes its own line — 0px tall here, but it holds its place
	 * in the row and stops the visible ones sharing the width. The closing
	 * photograph strip carries four of its seven that way, the FAQ
	 * illustration one.
	 */
	.adsbox-row > .wp-block-column:has(> .adsbox-hide-mobile:only-child) {
		display: none;
	}

	/*
	 * 2. The four video types are a label and its photograph, four times over
	 * — not a split section.
	 *
	 * "Stacked, the photograph always comes first" (above) puts `order: -1` on
	 * every media column in the row. In a two-column split that is the whole
	 * point. In this row it collected all four photographs at the top and
	 * dropped all four labels underneath them, so a phone got four unlabelled
	 * screenshots and then a list of four names — which is what "the
	 * screenshots are broken" is.
	 *
	 * The row is [label, photo] x 4 in source order because the labels are the
	 * mobile half of a responsive pair and Elementor states them first. Every
	 * pair is swapped instead, so each photograph keeps its own caption under
	 * it. Nothing here can reach a desktop: the labels are
	 * `adsbox-hide-desktop` and only this page's two language variants have
	 * them.
	 */
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(1) { order: 2; }
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(2) { order: 1; }
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(3) { order: 4; }
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(4) { order: 3; }
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(5) { order: 6; }
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(6) { order: 5; }
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(7) { order: 8; }
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column:nth-child(8) { order: 7; }

	/* A label is a caption for the picture above it, not a section heading. */
	.adsbox-row > .wp-block-column.adsbox-hide-desktop {
		padding: var(--wp--preset--spacing--20) var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
	}

	/*
	 * 3. Stacked cards need the gap the row does not have.
	 *
	 * `.adsbox-row` zeroes its gap so a split section's photograph meets its
	 * copy with no seam. A row of CARDS is the opposite shape: side by side
	 * the 30px radius reads as three cards, stacked with `gap: 0` the three
	 * grounds fuse into one 1,084px slab. Measured on the audience row — three
	 * columns at y 8726, 9087 and 9448, each 361px tall, touching exactly.
	 */
	.adsbox-row:has(> .wp-block-column > .adsbox-card) {
		row-gap: var(--wp--preset--spacing--40);
	}

	/*
	 * 4. Alignment stated at the desktop breakpoint is a desktop value.
	 *
	 * `.adsbox-row > .adsbox-col--text` already centres a stacked column,
	 * which is the site's rhythm on a phone everywhere else. An Elementor
	 * widget that declares `align: left` serialises as `has-text-align-left`
	 * on the block itself and outranks it — so the "ЗНАЕШЕ ЛИ, ЧЕ…" band
	 * ran its heading and its sentence hard against the left edge with a
	 * centred 92% underneath them. Direct children only: a card's or an
	 * accordion's own copy is nested deeper and keeps what it was given.
	 */
	.adsbox-row > .adsbox-col--text > .has-text-align-left {
		text-align: center;
	}

	/*
	 * 5. The closing strip is a strip at every width.
	 *
	 * Seven photographs at 14.286% each, four of them masked off on mobile.
	 * Stacked, the three survivors became three full-bleed 390px photographs
	 * — 528px of page where production shows one band. Rule 1 above takes the
	 * four hidden columns out of the flow; this keeps the remaining three
	 * side by side and holds production's portrait crop (206x272 measured at
	 * 1440) instead of letting each fall to its own 2.2:1 letterbox.
	 *
	 * `!important` twice because core's own stacking rule is `!important`
	 * twice: `flex-wrap: wrap` on the container and `flex-basis: 100%` on
	 * every column.
	 */
	.adsbox-section > .adsbox-row:has(> .adsbox-col--media:nth-child(7)) {
		flex-wrap: nowrap !important;
	}

	.adsbox-section > .adsbox-row:has(> .adsbox-col--media:nth-child(7)) > .wp-block-column {
		flex: 1 1 0 !important;
		min-width: 0;
		aspect-ratio: 206 / 272;
	}

	/*
	 * 2b. A phone mockup is not a full-bleed photograph.
	 *
	 * The four video-type images are 800x1422 renders of a handset with the
	 * frame tight to the canvas edge, and a media column bleeds its picture to
	 * the edge of the screen by design (see `.adsbox-col--media` above). At
	 * 1440 that is a 333px column inside a 1440px band and the mockup reads as
	 * an object on a ground; at 390 the column IS the screen, so the bezel ran
	 * off both sides and its rounded corners were cut — reported as "the phone
	 * images are still cropped".
	 *
	 * The inset restores the desktop proportion (333 of 1440 leaves the
	 * mockup at 23% of the band; 326 of 390 is as close as a phone gets), and
	 * `contain` on an image whose aspect already matches its box is belt and
	 * braces against a rounding crop at an odd viewport width.
	 */
	.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .adsbox-col--media {
		padding-inline: var(--wp--preset--spacing--40);
	}

	/*
	 * The crop itself is NOT a mobile question and the rule that used to sit
	 * here — `> .adsbox-col--media img { height: auto; object-fit: contain }`
	 * — never fired: (0,4,1) against the (0,5,1) of
	 * `.adsbox-row > .adsbox-col--media .wp-block-image:not(...):not(...) img`
	 * in the closing-strip block above. It read as a fix and measured as
	 * `height: 256px; object-fit: cover` at 390 for five days. The whole band
	 * is taken out of the media-column machinery at every width instead —
	 * see "the four mockups keep their whole height" below.
	 */
}

/* =========================================================================
 * The four video-type mockups keep their whole height — 2026-09-08
 * =========================================================================
 *
 * Reported as "the screenshots are cut, so they're not at their full height,
 * neither on mobile nor on desktop": the phones on /user-generated-content/
 * and its Bulgarian twin lose their top and bottom bezels.
 *
 * Measured in the same-origin iframe probe, not guessed. The four images are
 * 800x1422. At 1440 the column is 360 wide, so the whole phone is 640 tall —
 * the `<img>` box measured 360x490 with `object-fit: cover`, and 490 is
 * `.adsbox-col--media`'s own `min-height: clamp(16rem, 34vw, 34rem)` (34vw of
 * 1440 = 489.6). At 390 the box measured 390x256, which is the same clamp at
 * its 16rem floor. Both are the split-section machinery doing exactly what it
 * was written to do: a media column is floored, its figure is taken out of
 * flow at `inset: 0`, and the photograph is cropped to whatever height the
 * COPY beside it decided.
 *
 * There is no copy beside these. The row is [label, mockup] x 4 — a gallery of
 * four whole objects, where the picture is the thing being sized and nothing
 * else in the row has an opinion about the height. So the band opts out of all
 * three: no floor, the figure back in flow, and the image at its own aspect.
 * The section's own `min-height: 450px` still holds the band open; the row is
 * now 640 tall at 1440 and about 580 at 390 because that is how tall a phone
 * is at that width.
 *
 * Specificity is stated, not inherited. `:has(> .wp-block-column.adsbox-hide-desktop)`
 * is this page's two language variants and nothing else (the labels are the
 * mobile half of a responsive pair and only this band has them), and
 * `.wp-block-column` is named alongside `.adsbox-col--media` purely to carry
 * the selector to (0,6,x) — one class above the `.wp-block-image ... img`
 * rules in the closing-strip block, which are what silently beat the previous
 * attempt at this fix. Winning on document order alone would have been a
 * rule that any later edit could turn back off.
 */
.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column.adsbox-col--media {
	min-height: 0;
}

.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column.adsbox-col--media > .wp-block-image {
	position: static;
	height: auto;
	min-height: 0;
}

.adsbox-row:has(> .wp-block-column.adsbox-hide-desktop) > .wp-block-column.adsbox-col--media .wp-block-image img {
	height: auto;
	max-height: none;
	object-fit: contain;
}

/* =========================================================================
 * /social-media/ and its Bulgarian twin — 2026-09-03
 * =========================================================================
 *
 * Five faults reported off a desktop read of the page, none of them visible to
 * a check that measures words, markup or attributes. The structural half of
 * each lives in assemble-pages._social_organic_design(); this is the design
 * half. Every selector is scoped by a band class the assembler puts on that one
 * section, so nothing here can reach another page.
 */

/*
 * 1. The split hero.
 *
 * `.adsbox-row > .adsbox-col--text > .wp-block-buttons` is centred, which is
 * right for /our-services/, where the heading and the copy above the button
 * are centred too. This hero states all three at the left — the source's own
 * alignment — so a centred button hung in the middle of its own column with
 * the copy left of it.
 */
.adsbox-hero-split .adsbox-row > .adsbox-col--text {
	text-align: left;
}

.adsbox-hero-split .adsbox-row > .adsbox-col--text > .wp-block-buttons {
	justify-content: flex-start;
}

/*
 * 2. PROJECTS — a fixed grid, not a track.
 *
 * The group carries core's grid layout, so core emits its own
 * `repeat(auto-fill, minmax(24rem, 1fr))` as an inline rule in the head. The
 * doubled class is what wins that tie without `!important`: a tie on
 * specificity is broken by source order and nothing guarantees this file loads
 * last.
 *
 * Two columns rather than `auto-fill` because four cells at four different
 * viewport widths is four different layouts — 4 x 1, 2 x 2, 3 + 1 — and the
 * 3 + 1 is what the carousel already looked like.
 */
.wp-block-group.adsbox-project-grid {
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: clamp(1rem, 2.2vw, 2rem);
	/* The band is full-bleed (layout: default), and a grid is not covered by
	   the section's loose-content inset. Same 5vw the text columns use, so the
	   first cell's left edge lands under the "PROJECTS" heading. */
	padding-inline: clamp(1.5rem, 5vw, 5.5rem);
	/* The inset is padding on a block that is already the full width of the
	   band, so without this it is added OUTSIDE the box: measured at 430px, the
	   grid ran 48px past the right edge and the section's `overflow: clip` cut
	   the cells rather than showing a scrollbar — invisible to check M, which
	   asks whether the page scrolls sideways. */
	box-sizing: border-box;
	max-width: 100%;
}

/* One size for all four. The four sources are 1000-1080px square-ish crops of
   the same shoot; without a stated ratio each cell took its own image's height
   and the row read as a ragged shelf. */
.adsbox-project-grid .adsbox-grid__cell img {
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: cover;
	border-radius: 30px;
}

@media (max-width: 781px) {
	.wp-block-group.adsbox-project-grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

/*
 * 3. The four-platform band as cards.
 *
 * Four 24% columns of centred text on white, separated by nothing but air: the
 * 13px stub rule above each heading was the only thing marking where one
 * platform ended and the next began, and the four paragraphs are long enough
 * that at 1440 they read as one continuous block of type in four ragged
 * gutters.
 *
 * `flex-basis: 0` with `flex-grow: 1` rather than a column gap on top of the
 * stated 4 x 24%: those widths already leave only 4% of slack, and a gap on a
 * row is added to the line BEFORE flex-shrink runs, so a stated gutter is what
 * dropped the fourth card onto a line of its own the day `.adsbox-row`'s wrap
 * started working (see the note on `.adsbox-row:has(> .adsbox-col-round)`).
 * With no basis to overflow, the gap is free. `!important` beats the inline
 * `flex-basis: 24%` core serialises onto the column.
 */
/* The band butts against the heading band above it and the closing CTA below,
   because .adsbox-section removes the flow gap between bands by design. Four
   cards need air on all four sides or they read as a strip wedged between two
   other sections: measured, the cards touched the "See the platforms where we
   are strongest" line and the CTA photograph. */
.adsbox-platform-cards {
	padding-block: clamp(2rem, 4vw, 3.5rem);
}

.adsbox-platform-cards .adsbox-row {
	--adsbox-card-gap: clamp(1rem, 2vw, 2rem);
	align-items: stretch;
	gap: var(--adsbox-card-gap);
	/* The band is full-bleed and the cards are its content: without this the
	   first and last card ran off both edges of the screen. The same 5vw the
	   text columns state, so the row lines up with every other band. */
	padding-inline: clamp(1.5rem, 5vw, 5.5rem);
	/* 2 x 2, not 4 x 1. Four cards across a 1440px band are ~300px wide holding
	   45 words each, which is a column of type rather than a card.
	   `!important` because core/columns ships
	     @media (min-width: 782px) { .wp-block-columns { flex-wrap: nowrap !important } }
	   and a plain `wrap` here would be a comment — the same trap
	   `.adsbox-row`'s own wrap fell into until 2026-09-03. */
	flex-wrap: wrap !important;
}

/* The source centres each column's content, and the card is taller than its
   copy, so a short one would otherwise pin its text to the top. The floor is
   what makes a pair read as squares rather than as two wide letterboxes: at
   1440 a card is ~575px across. */
.adsbox-platform-cards .adsbox-row > .wp-block-column {
	justify-content: center;
	min-height: clamp(18rem, 34vw, 30rem);
}

.adsbox-platform-cards .adsbox-row > .wp-block-column {
	/* All three parts are `!important` together. core ships
	 *   .wp-block-columns .wp-block-column[style*=flex-basis] { flex-grow: 0 }
	 * at the same (0,3,0) as this rule and loads after the theme, so a
	 * non-important `flex-grow: 1` lost the tie: with `flex-basis: 0` winning
	 * and `flex-grow: 0` winning too, all four cards collapsed to nothing and
	 * "Facebook" set one letter per line. */
	flex: 1 1 calc(50% - var(--adsbox-card-gap)) !important;
	min-width: 0;
}

/* The card itself. `.wp-block-column.adsbox-col-round` states `20px 50px` for
   the enquiry panel on another page; three classes match that specificity and
   this file's own source order settles it. */
.adsbox-platform-cards .wp-block-column.adsbox-col-round {
	background: var(--wp--preset--color--cream);
	padding: clamp(1.5rem, 2.6vw, 2.5rem);
}

/* The stub is `--left` in the source, under headings the source centres. In a
   card 250px wide that reads as a misplaced mark rather than as a rule. */
.adsbox-platform-cards .adsbox-rule--stub {
	margin-inline: auto;
}

@media (max-width: 781px) {
	/* One card per line. core stacks columns here on its own, but this row's
	   basis is `!important` and would otherwise hold two 50% cards on a 390px
	   screen. The desktop square floor goes with it — at one column wide a card
	   is as tall as its copy. */
	.adsbox-platform-cards .adsbox-row > .wp-block-column {
		flex-basis: 100% !important;
		min-height: 0;
	}
}
