/* ==========================================================================
   Unodevs — front-end styles

   Every colour, radius, shadow, blur and timing value below is a CSS
   custom property printed at runtime by inc/dynamic-css.php from the
   saved dashboard palette. Nothing here is hard coded, which is what
   makes the theme restyle itself when the palette changes.

   Contents
   1.  Reset & base
   2.  Layout primitives
   3.  Typography
   4.  Buttons & controls
   5.  Glass material
   6.  Header layouts
   7.  Mobile drawer & search
   8.  Home sections
   9.  Cards, blog, single
   10. Forms
   11. Footer layouts
   12. Coming soon
   13. Utilities & floating UI
   14. Motion
   15. Responsive
   16. Reduced motion / transparency / contrast
   ========================================================================== */


/* 1. Reset & base
   ========================================================================== */

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;

	/*
	 * BASE FONT SIZE LIVES HERE, NOT ON <body>. This is the whole fix.
	 *
	 * It used to be `body { font-size: var(--u-font-size) }`, and the
	 * control appeared to do nothing at all. The reason is that 189 of the
	 * theme's type sizes are written in `rem`, and `rem` resolves against
	 * the ROOT element, not against body. So the slider moved body — and
	 * body's size was inherited by almost nothing, because every heading,
	 * lead, label and list in every section sets its own rem size. Dragging
	 * it from 14 to 20 changed unclassed paragraph text and nothing else.
	 * On the Contour demo, whose visible type is all rem and vw, it changed
	 * literally nothing on screen.
	 *
	 * It is expressed as a PERCENTAGE rather than a pixel value on purpose.
	 * A px value here would override the font size a visitor has set in
	 * their own browser, which is the one accessibility preference a theme
	 * has no business overriding. A percentage scales their choice instead
	 * of replacing it: someone browsing at 20px still gets 20px at the 100%
	 * setting, and 125% of it at the largest.
	 *
	 * Only type moves. Spacing, widths and every media query in the theme
	 * are in px, so the layout and the breakpoints stay exactly where they
	 * are — the page does not reflow, the text just gets bigger.
	 */
	font-size: var(--u-font-scale, 100%);
}

body {
	margin: 0;
	background: var(--u-bg);
	color: var(--u-text);
	font-family: var(--u-font-body);
	font-size: 1rem;
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;

	/*
	 * The sideways-scroll guard, and the ONLY element in the theme that
	 * carries one.
	 * ------------------------------------------------------------------
	 * 17.0.4 added `overflow-x: clip` to <html> on top of this, as a
	 * "second line of defence". It broke every sticky element in the
	 * theme — the scrolling reel, the pinned story, the process stack —
	 * and nothing reported an error anywhere.
	 *
	 * The mechanism is worth knowing, because the rule looks harmless.
	 * `overflow-x: hidden` here makes this element's overflow-y compute to
	 * `auto`. That normally does not matter, because <body>'s overflow is
	 * propagated up to the viewport and body itself never becomes a
	 * scroller. Propagation only happens while <html> is `visible`. The
	 * moment <html> was `clip`, propagation stopped, <body> became a real
	 * scroll container, and every `position: sticky` inside it started
	 * sticking to a box that never scrolls.
	 *
	 * Measured, not assumed: html clip alone — sticky fine. body hidden
	 * alone — sticky fine. Both — the pinned element scrolls away at
	 * -800px.
	 *
	 * The added rule is gone. The guard is back where it was for the
	 * theme's whole life, and the real defence against sideways scroll is
	 * not a guard at all — it is not overflowing in the first place, which
	 * is what the audit-panel fix in 17.0.4 actually did.
	 */
	overflow-x: hidden;
}

img, svg, video { max-width: 100%; height: auto; display: block; }

a { color: inherit; text-decoration: none; }

button { font: inherit; color: inherit; }

:focus-visible {
	outline: 2px solid var(--u-primary);
	outline-offset: 3px;
	border-radius: var(--u-radius-sm);
}

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

.u-skip {
	position: absolute;
	top: -100px; left: 16px;
	z-index: 200;
	padding: 12px 18px;
	background: var(--u-primary);
	color: #fff;
	border-radius: var(--u-radius-sm);
	transition: top 160ms var(--u-ease-out);
}
.u-skip:focus { top: 16px; }

/* Icons inherit stroke so they follow the palette automatically. */
.u-icon {
	width: 20px; height: 20px;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.7;
	stroke-linecap: round;
	stroke-linejoin: round;
	flex: none;
}


/* 2. Layout primitives
   ========================================================================== */

.u-container {
	width: 100%;
	max-width: var(--u-container);
	margin-inline: auto;
	padding-inline: clamp(20px, 5vw, 40px);
}
.u-container-narrow { max-width: 760px; }

.u-main { display: block; min-height: 40vh; }

.u-section { padding-block: clamp(64px, 9vw, 128px); }

.u-layout { display: grid; gap: clamp(32px, 5vw, 56px); }
.u-layout.has-sidebar { grid-template-columns: minmax(0, 1fr) 300px; }

/* Sidebar side is a pure order swap — the markup order never changes,
   so the reading order for assistive tech stays correct. */
.u-sidebar-left .u-layout.has-sidebar { grid-template-columns: 300px minmax(0, 1fr); }
.u-sidebar-left .u-content { order: 2; }
.u-sidebar-left .u-sidebar { order: 1; }

.u-grid { display: grid; gap: clamp(16px, 2.4vw, 28px); }
.u-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.u-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.u-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.u-list { display: flex; flex-direction: column; gap: 20px; }


/* 3. Typography
   ========================================================================== */

h1, h2, h3, h4 {
	font-family: var(--u-font-heading);
	font-weight: 640;
	margin: 0 0 .5em;
	letter-spacing: -0.02em;
	line-height: 1.15;
	text-wrap: balance;
}

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

/* Tracking is size-specific: tight on display sizes, near zero on body. */
.u-display {
	font-family: var(--u-font-heading);
	font-size: clamp(2.4rem, 6vw, 4.4rem);
	line-height: 1.03;
	letter-spacing: -0.035em;
	margin: 0 0 .35em;
	font-weight: 680;
}

.u-section-title {
	font-size: clamp(1.7rem, 3.2vw, 2.6rem);
	letter-spacing: -0.028em;
}

.u-lead {
	font-size: clamp(1.05rem, 1.5vw, 1.25rem);
	line-height: 1.55;
	color: var(--u-muted);
	max-width: 62ch;
	letter-spacing: -0.005em;
}

.u-eyebrow {
	font-size: .78rem;
	font-weight: 620;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--u-primary);
	margin: 0 0 .9em;
}

.u-section-head { max-width: 62ch; margin-bottom: clamp(32px, 4vw, 56px); }
.u-section-text { color: var(--u-muted); }
.u-section-foot { margin-top: 36px; }

.u-prose { font-size: 1.05rem; line-height: 1.75; }
.u-prose > * + * { margin-top: 1.1em; }
.u-prose h2 { margin-top: 1.8em; font-size: 1.6rem; }
.u-prose h3 { margin-top: 1.6em; font-size: 1.25rem; }
.u-prose a { color: var(--u-primary); text-decoration: underline; text-underline-offset: 3px; }
.u-prose img { border-radius: var(--u-radius); }
.u-prose blockquote {
	margin: 1.6em 0;
	padding: 4px 0 4px 22px;
	border-left: 3px solid var(--u-primary);
	color: var(--u-muted);
	font-size: 1.1rem;
}
.u-prose code {
	background: rgba(var(--u-text-rgb), .07);
	padding: .16em .42em;
	border-radius: var(--u-radius-sm);
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: .9em;
}
.u-prose pre {
	background: rgba(var(--u-text-rgb), .06);
	padding: 20px;
	border-radius: var(--u-radius);
	overflow-x: auto;
}
.u-prose pre code { background: none; padding: 0; }


/* 4. Buttons & controls
   ========================================================================== */

.u-btn {
	--btn-bg: transparent;
	--btn-fg: var(--u-text);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	padding: 12px 22px;
	border: 1px solid transparent;
	border-radius: var(--u-radius-pill);
	background: var(--btn-bg);
	color: var(--btn-fg);
	font-size: .95rem;
	font-weight: 560;
	letter-spacing: -0.005em;
	cursor: pointer;
	white-space: nowrap;
	/* Specific properties only — never `all`. */
	transition:
		transform 160ms var(--u-ease-out),
		background-color 180ms ease,
		border-color 180ms ease,
		box-shadow 200ms var(--u-ease-out);
}

/* Feedback lands on pointer-down, not on release. */
.u-btn:active { transform: scale(.97); }

.u-btn-primary {
	--btn-bg: var(--u-primary);
	--btn-fg: #fff;
	box-shadow: var(--u-shadow-sm);
}
.u-btn-ghost {
	--btn-bg: transparent;
	border-color: var(--u-border);
}
.u-btn-sm { padding: 8px 14px; font-size: .85rem; }
.u-btn-lg { padding: 15px 30px; font-size: 1rem; }

@media (hover: hover) and (pointer: fine) {
	.u-btn-primary:hover { box-shadow: var(--u-shadow); transform: translateY(-1px); }
	.u-btn-primary:active { transform: scale(.97); }
	.u-btn-ghost:hover { background: rgba(var(--u-text-rgb), .05); border-color: rgba(var(--u-text-rgb), .2); }
}

.u-icon-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 38px; height: 38px;
	border: 0;
	border-radius: var(--u-radius-pill);
	background: transparent;
	cursor: pointer;
	transition: transform 140ms var(--u-ease-out), background-color 160ms ease;
}
.u-icon-btn:active { transform: scale(.94); }
@media (hover: hover) and (pointer: fine) {
	.u-icon-btn:hover { background: rgba(var(--u-text-rgb), .07); }
}

/* Burger */
.u-burger {
	display: none;
	position: relative;
	width: 40px; height: 38px;
	border: 0;
	background: transparent;
	cursor: pointer;
	border-radius: var(--u-radius-pill);
}
.u-burger span {
	position: absolute;
	left: 10px;
	width: 20px; height: 1.8px;
	background: currentColor;
	border-radius: 2px;
	transition: transform 220ms var(--u-ease-out), opacity 140ms ease;
}
.u-burger span:nth-child(1) { top: 16px; }
.u-burger span:nth-child(2) { top: 22px; }
.u-burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(3px) rotate(45deg); }
.u-burger[aria-expanded="true"] span:nth-child(2) { transform: translateY(-3px) rotate(-45deg); }


/* 5. Glass material
   ========================================================================== */

/* Translucent functional layer. Content scrolls underneath it, and the
   surface materialises (blur + scale together) rather than plain fading. */
.u-glass {
	background: var(--u-glass-bg);
	backdrop-filter: blur(var(--u-blur)) saturate(var(--u-saturate));
	-webkit-backdrop-filter: blur(var(--u-blur)) saturate(var(--u-saturate));
	border: 1px solid var(--u-glass-edge);
}

.u-card {
	position: relative;
	background: var(--u-surface);
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-lg);
	padding: clamp(20px, 2.6vw, 30px);
	box-shadow: var(--u-shadow-sm);
	transition: transform 220ms var(--u-ease-out), box-shadow 260ms var(--u-ease-out);
}
.u-card.u-glass { border-color: var(--u-glass-edge); }


/* 6. Header layouts
   ========================================================================== */

.u-topbar {
	background: var(--u-primary);
	color: #fff;
	font-size: .85rem;
}
.u-topbar-inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding-block: 9px;
}
.u-topbar p { margin: 0; }

.u-header { position: relative; z-index: 100; }
.u-sticky-header .u-header { position: sticky; top: 0; }

.u-header-bar {
	display: flex;
	align-items: center;
	/*
	 * No `padding` and no `height` in this list, deliberately. See the note
	 * on the condense rule below: this element is in normal flow in most
	 * layouts, so transitioning anything that affects its box animates the
	 * whole page underneath it.
	 */
	transition:
		background-color 240ms ease,
		border-color 240ms ease,
		box-shadow 260ms var(--u-ease-out),
		transform 260ms var(--u-ease-out);
}

.u-header-inner,
.u-header-glass .u-header-bar {
	display: flex;
	align-items: center;
	gap: 20px;
	width: 100%;
}

/* Floating glass bar */
.u-header-glass { padding-top: 14px; }
.u-header-glass .u-header-bar {
	border-radius: var(--u-radius-pill);
	padding: 8px 10px 8px 20px;
	box-shadow: var(--u-shadow-sm);
}
.u-header-glass.is-scrolled .u-header-bar { box-shadow: var(--u-shadow); }

/* Classic bar */
.u-header-classic .u-header-bar { width: 100%; border-width: 0 0 1px; padding-block: 12px; }
.u-header-classic.is-scrolled .u-header-bar { box-shadow: var(--u-shadow-sm); }

/* Split */
.u-header-split .u-header-bar { width: 100%; border-width: 0 0 1px; padding-block: 12px; }
.u-header-split .u-header-inner { display: grid; grid-template-columns: 1fr auto 1fr; }
.u-header-split .u-header-center { justify-self: center; }
.u-header-split .u-header-end { justify-self: end; }

/* Stacked */
.u-header-stacked .u-header-bar { width: 100%; border-width: 0 0 1px; padding-block: 14px; flex-direction: column; }
.u-header-stacked .u-header-row { display: flex; align-items: center; justify-content: space-between; width: 100%; }
.u-header-stacked .u-header-row-nav { justify-content: center; padding-top: 10px; }

/* Transparent-over-hero, until you scroll */
.u-header.is-transparent:not(.is-scrolled) .u-header-bar {
	background: transparent;
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
	border-color: transparent;
	box-shadow: none;
}

/* ==========================================================================
   Condense on scroll — WHY THERE IS NO LONGER A HEIGHT CHANGE HERE
   ==========================================================================

   This rule used to be `padding-block: 6px`, shrinking the bar from 12px of
   padding to 6px the moment you scrolled past the threshold.

   THE BUG THAT CAUSED. `.u-header` is `position: relative` — it is in normal
   document flow, and only `.u-sticky-header .u-header` is sticky. An element
   in flow that changes height moves everything below it. So crossing the
   threshold pulled the entire page up by twelve pixels, over 240ms, while
   the visitor was still scrolling.

   Worse, it feeds back. The browser's scroll anchoring compensates for
   content above the viewport changing size by adjusting the scroll position,
   which fires another scroll event, which can put you back under the
   threshold — the class comes off, the header grows, the page moves down,
   and round again. Reported, accurately, as the header shaking on every
   demo whenever you scroll.

   A condensing header is only ever safe when it is taken out of flow. It is
   not, here, in three of the four layouts. So the condense is now purely
   visual: background, border and shadow, none of which cost a layout pass.
   Nothing moves, so nothing can oscillate.

   The transition below no longer lists `padding` either, for the same
   reason — an animating height is an animating page.
   ========================================================================== */

.u-header-start { display: flex; align-items: center; gap: 12px; }
.u-header-end { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.u-header-nav { display: flex; }

/* Logo position + nav alignment. These four rules are the whole
   left/centre/right control — order only, no duplicated markup. */
.u-logo-center .u-header-glass .u-header-start,
.u-logo-center .u-header-classic .u-header-start { order: 2; margin-inline: auto; }
.u-logo-center .u-header-glass .u-header-nav,
.u-logo-center .u-header-classic .u-header-nav { order: 1; }
.u-logo-center .u-header-glass .u-header-end,
.u-logo-center .u-header-classic .u-header-end { order: 3; }

.u-logo-right .u-header-glass .u-header-start,
.u-logo-right .u-header-classic .u-header-start { order: 3; margin-left: auto; }
.u-logo-right .u-header-glass .u-header-nav,
.u-logo-right .u-header-classic .u-header-nav { order: 1; }
.u-logo-right .u-header-glass .u-header-end,
.u-logo-right .u-header-classic .u-header-end { order: 2; margin-left: 0; }

.u-nav-left .u-header-nav { margin-right: auto; }
.u-nav-center .u-header-nav { margin-inline: auto; }
.u-nav-right .u-header-nav { margin-left: auto; }

/* Logo */
.u-logo-text { display: inline-flex; align-items: center; gap: 10px; font-weight: 660; letter-spacing: -0.02em; }
.u-logo-mark {
	width: 26px; height: 26px;
	border-radius: 9px;
	background: linear-gradient(135deg, var(--u-primary), var(--u-accent));
	box-shadow: var(--u-shadow-sm);
	flex: none;
}
.custom-logo { max-height: 40px; width: auto; }

/* Menu */
.u-menu {
	display: flex;
	align-items: center;
	gap: 4px;
	list-style: none;
	margin: 0;
	padding: 0;
}
.u-menu li { position: relative; }
.u-menu a {
	display: inline-block;
	padding: 8px 14px;
	border-radius: var(--u-radius-pill);
	font-size: .95rem;
	font-weight: 520;
	color: var(--u-muted);
	transition: color 160ms ease, background-color 160ms ease;
}
.u-menu > li.current-menu-item > a,
.u-menu > li.current_page_item > a { color: var(--u-text); }
@media (hover: hover) and (pointer: fine) {
	.u-menu a:hover { color: var(--u-text); background: rgba(var(--u-text-rgb), .06); }
}

/* Submenus scale in from the trigger, not from the centre of the screen. */
.u-menu .sub-menu {
	position: absolute;
	top: calc(100% + 8px);
	left: 0;
	min-width: 210px;
	list-style: none;
	margin: 0;
	padding: 6px;
	background: var(--u-glass-bg);
	backdrop-filter: blur(var(--u-blur)) saturate(var(--u-saturate));
	-webkit-backdrop-filter: blur(var(--u-blur)) saturate(var(--u-saturate));
	border: 1px solid var(--u-glass-edge);
	border-radius: var(--u-radius);
	box-shadow: var(--u-shadow);
	transform-origin: top left;
	transform: scale(.96) translateY(-4px);
	opacity: 0;
	visibility: hidden;
	transition:
		opacity 150ms var(--u-ease-out),
		transform 180ms var(--u-ease-out),
		visibility 0s linear 180ms;
}
.u-menu li:hover > .sub-menu,
.u-menu li:focus-within > .sub-menu {
	opacity: 1;
	visibility: visible;
	transform: none;
	transition-delay: 0s;
}
.u-menu .sub-menu a { display: block; width: 100%; }


/* 7. Mobile drawer & search
   ========================================================================== */

.u-drawer { position: fixed; inset: 0; z-index: 300; }
.u-drawer[hidden] { display: none; }

/* Navigation surfaces override the glass tint with the opaque one.
   These float over page content rather than sitting on the background, so
   the card-weight tint leaves text from the page showing through them. */
.u-drawer-panel.u-glass,
.u-search-panel.u-glass,
.u-bottomnav-inner.u-glass {
	background: var(--u-glass-solid, var(--u-surface));
}

.u-drawer-scrim {
	position: absolute; inset: 0;
	/* Dark enough to push the page back rather than merely tint it. */
	background: rgba(var(--u-shadow-rgb), .62);
	opacity: 0;
	transition: opacity 260ms var(--u-ease-drawer);
}
.u-drawer.is-open .u-drawer-scrim { opacity: 1; }

.u-drawer-panel {
	position: absolute;
	top: 0; bottom: 0;
	width: min(380px, 86vw);
	padding: 76px 26px 26px;
	display: flex;
	flex-direction: column;
	gap: 22px;
	overflow-y: auto;

	/* An opaque background of its own, rather than borrowed from .u-glass.

	   var(--u-surface) rather than a literal white: it is the palette's own
	   panel colour, it is always a solid hex, and it is the colour that
	   var(--u-text) below is guaranteed to be readable against. On every
	   demo currently shipped that resolves to white, which is what was
	   asked for — but a demo built on a dark palette gets a dark drawer
	   with light type instead of white text on white, which is what a
	   hardcoded #fff would have produced. */
	background: var(--u-surface);

	box-shadow: var(--u-shadow-lg);
	/* Exit direction mirrors entry — it leaves the way it came in. */
	transition: transform 320ms var(--u-ease-drawer);
	will-change: transform;
}
/* The edge that faces the page gets the border — the other three sit off
   the viewport, so a border on all four is three lines nobody can see. */
.u-drawer-right .u-drawer-panel { right: 0; transform: translateX(100%); border-left: 1px solid var(--u-border); }
.u-drawer-left .u-drawer-panel { left: 0; transform: translateX(-100%); border-right: 1px solid var(--u-border); }
.u-drawer-fullscreen .u-drawer-panel { inset: 0; width: 100%; transform: translateY(-100%); border: 0; }
.u-drawer.is-open .u-drawer-panel { transform: none; }

.u-drawer-close {
	position: absolute;
	top: 20px; right: 20px;
	width: 40px; height: 40px;
	display: grid; place-items: center;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-pill);
	background: transparent;
	cursor: pointer;
	transition: transform 140ms var(--u-ease-out);
}
.u-drawer-close:active { transform: scale(.94); }

.u-drawer .u-menu { flex-direction: column; align-items: stretch; gap: 2px; }
.u-drawer .u-menu a { padding: 12px 14px; font-size: 1.15rem; color: var(--u-text); }
.u-drawer .sub-menu {
	position: static;
	opacity: 1;
	visibility: visible;
	transform: none;
	background: none;
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
	border: 0;
	box-shadow: none;
	padding-left: 12px;
}
.u-drawer-foot { margin-top: auto; display: grid; gap: 12px; }
.u-drawer-mail { color: var(--u-muted); font-size: .92rem; }

/* Search overlay */
.u-search-overlay { position: fixed; inset: 0; z-index: 310; display: grid; place-items: start center; }
.u-search-overlay[hidden] { display: none; }
.u-search-scrim {
	position: absolute; inset: 0;
	background: rgba(var(--u-shadow-rgb), .4);
	opacity: 0;
	transition: opacity 200ms var(--u-ease-out);
}
.u-search-overlay.is-open .u-search-scrim { opacity: 1; }
.u-search-panel {
	position: relative;
	width: min(620px, 92vw);
	margin-top: 14vh;
	padding: 26px;
	border-radius: var(--u-radius-lg);
	box-shadow: var(--u-shadow-lg);
	/* Materialise: blur and scale animate together. */
	opacity: 0;
	transform: scale(.96) translateY(-8px);
	filter: blur(6px);
	transition:
		opacity 180ms var(--u-ease-out),
		transform 220ms var(--u-ease-out),
		filter 220ms var(--u-ease-out);
}
.u-search-overlay.is-open .u-search-panel { opacity: 1; transform: none; filter: none; }
.u-search-panel form { display: flex; gap: 10px; }
.u-search-panel input[type="search"] {
	flex: 1;
	padding: 14px 16px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius);
	background: var(--u-surface);
	color: var(--u-text);
	font-size: 1.05rem;
}
.u-search-hint { margin: 12px 0 0; font-size: .82rem; color: var(--u-muted); }


/* 8. Home sections
   ========================================================================== */

.u-hero { position: relative; padding-block: clamp(90px, 13vw, 180px) clamp(50px, 7vw, 90px); overflow: hidden; }

/* A large, slow-moving background is deliberately low contrast and is
   removed entirely for reduced-motion visitors (see section 16). */
.u-hero-glow {
	position: absolute;
	top: -30%; left: 50%;
	width: min(1100px, 130vw);
	aspect-ratio: 1;
	transform: translateX(-50%);
	background:
		radial-gradient(circle at 35% 40%, rgba(var(--u-primary-rgb), .30), transparent 58%),
		radial-gradient(circle at 68% 55%, rgba(var(--u-accent-rgb), .26), transparent 60%);
	filter: blur(70px);
	pointer-events: none;
	opacity: .9;
}

.u-hero-inner { position: relative; }
.u-hero-title { max-width: 16ch; }
.u-hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }

.u-hero-card {
	margin-top: clamp(48px, 6vw, 80px);
	max-width: 560px;
	padding: 0;
	overflow: hidden;
	border-radius: var(--u-radius-lg);
	box-shadow: var(--u-shadow-lg);
}
.u-hero-card-bar {
	display: flex; gap: 7px;
	padding: 14px 18px;
	border-bottom: 1px solid var(--u-glass-edge);
}
.u-hero-card-bar span { width: 10px; height: 10px; border-radius: 50%; background: rgba(var(--u-text-rgb), .18); }
.u-hero-card-body { display: flex; align-items: flex-end; justify-content: space-between; gap: 28px; padding: 26px; }
.u-hero-metric strong { display: block; font-size: 3rem; line-height: 1; letter-spacing: -0.04em; font-family: var(--u-font-heading); }
.u-hero-metric span { display: block; max-width: 22ch; margin-top: 8px; font-size: .86rem; color: var(--u-muted); }
.u-hero-bars { display: flex; align-items: flex-end; gap: 6px; height: 84px; }
.u-hero-bars span {
	width: 12px;
	height: var(--h);
	min-height: 8px;
	border-radius: 4px;
	background: linear-gradient(180deg, var(--u-primary), var(--u-accent));
	opacity: .85;
	transform-origin: bottom;
}

/* Client marquee */
.u-logos { padding-block: clamp(30px, 4vw, 50px); }
.u-logos-label { font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--u-muted); margin-bottom: 22px; }
.u-marquee { overflow: hidden; mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.u-marquee-track {
	display: flex;
	gap: 56px;
	width: max-content;
	/* Constant motion -> linear. Anything else reads as stuttering. */
	animation: u-marquee 34s linear infinite;
}
.u-marquee:hover .u-marquee-track { animation-play-state: paused; }
@keyframes u-marquee { to { transform: translateX(-50%); } }
.u-logo-item {
	font-size: 1.25rem;
	font-weight: 600;
	letter-spacing: -0.02em;
	color: var(--u-muted);
	opacity: .65;
	white-space: nowrap;
}

/* Services */
.u-service h3 { font-size: 1.15rem; margin-bottom: .45em; }
.u-service p { color: var(--u-muted); font-size: .95rem; margin: 0; }
.u-service-icon {
	display: grid; place-items: center;
	width: 44px; height: 44px;
	margin-bottom: 18px;
	border-radius: 13px;
	background: linear-gradient(135deg, rgba(var(--u-primary-rgb), .16), rgba(var(--u-accent-rgb), .16));
	color: var(--u-primary);
}
.u-service-icon svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

@media (hover: hover) and (pointer: fine) {
	.u-service:hover, .u-post-card:hover, .u-member:hover { transform: translateY(-3px); box-shadow: var(--u-shadow); }
}

/* Split feature */
.u-split-inner { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(32px, 5vw, 72px); align-items: center; }
/* One control mirrors every split section on the site. */
.u-flip .u-split-copy { order: 2; }
.u-flip .u-split-media { order: 1; }

.u-ticks { list-style: none; margin: 1.4em 0; padding: 0; display: grid; gap: 10px; }
.u-ticks li { position: relative; padding-left: 28px; color: var(--u-muted); }
.u-ticks li::before {
	content: "";
	position: absolute;
	left: 0; top: .48em;
	width: 15px; height: 8px;
	border-left: 2px solid var(--u-primary);
	border-bottom: 2px solid var(--u-primary);
	transform: rotate(-45deg);
}

.u-panel-stack { position: relative; padding: 20px; }
.u-panel { border-radius: var(--u-radius-lg); }
.u-panel-back, .u-panel-mid { position: absolute; inset: 0; }
.u-panel-back { transform: translate(22px, -20px) scale(.94); opacity: .5; }
.u-panel-mid { transform: translate(11px, -10px) scale(.97); opacity: .7; }
.u-panel-front { position: relative; padding: 26px; box-shadow: var(--u-shadow-lg); display: grid; gap: 14px; }
.u-panel-row { display: flex; align-items: center; gap: 12px; }
.u-panel-row span:first-child { width: 26px; height: 26px; border-radius: 8px; background: linear-gradient(135deg, var(--u-primary), var(--u-accent)); opacity: .8; flex: none; }
.u-panel-row span + span { height: 9px; border-radius: 5px; background: rgba(var(--u-text-rgb), .13); }
.u-panel-row .wide { width: 100%; }
.u-panel-row .mid { width: 70%; }
.u-panel-row .short { width: 42%; }
.u-panel-quote { padding: 10px; }
.u-panel-quote p { font-size: 1.15rem; letter-spacing: -0.02em; margin: 0; }

/* Stats */
.u-stats-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(20px, 3vw, 40px); }
.u-stat strong {
	display: block;
	font-family: var(--u-font-heading);
	font-size: clamp(2.4rem, 5vw, 3.6rem);
	line-height: 1;
	letter-spacing: -0.04em;
	font-variant-numeric: tabular-nums;
	background: linear-gradient(135deg, var(--u-primary), var(--u-accent));
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}
.u-stat span { display: block; margin-top: 10px; color: var(--u-muted); font-size: .92rem; }


/* Testimonials */
.u-quote-rail {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: min(400px, 82vw);
	gap: 20px;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	padding-bottom: 14px;
	overscroll-behavior-x: contain;
	scrollbar-width: thin;
}
.u-quote { scroll-snap-align: start; }
.u-quote blockquote { margin: 0; font-size: 1.05rem; letter-spacing: -0.01em; }
.u-quote figcaption { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--u-border); }
.u-quote figcaption strong { display: block; }
.u-quote figcaption span { color: var(--u-muted); font-size: .88rem; }

/* Closing CTA */
.u-closing-panel {
	border-radius: var(--u-radius-lg);
	padding: clamp(36px, 6vw, 72px);
	text-align: center;
	box-shadow: var(--u-shadow);
	background-image: linear-gradient(135deg, rgba(var(--u-primary-rgb), .10), rgba(var(--u-accent-rgb), .10));
}
.u-closing-panel h2 { font-size: clamp(1.7rem, 3.6vw, 2.8rem); max-width: 20ch; margin-inline: auto; }
.u-closing-panel p { color: var(--u-muted); max-width: 52ch; margin-inline: auto; }
.u-closing-panel .u-btn { margin-top: 26px; }

/* About / Services extras */
.u-avatar {
	display: grid; place-items: center;
	width: 54px; height: 54px;
	margin-bottom: 16px;
	border-radius: var(--u-radius-pill);
	background: linear-gradient(135deg, var(--u-primary), var(--u-accent));
	color: #fff;
	font-weight: 640;
	letter-spacing: .02em;
}
.u-member h3 { font-size: 1.05rem; margin-bottom: .2em; }
.u-member p { color: var(--u-muted); font-size: .9rem; margin: 0; }

.u-package { display: flex; flex-direction: column; }
.u-package.is-featured { border-color: rgba(var(--u-primary-rgb), .45); box-shadow: var(--u-shadow); }
.u-badge {
	position: absolute;
	top: 18px; right: 18px;
	padding: 5px 12px;
	border-radius: var(--u-radius-pill);
	background: rgba(var(--u-primary-rgb), .14);
	color: var(--u-primary);
	font-size: .72rem;
	font-weight: 620;
	letter-spacing: .04em;
	text-transform: uppercase;
}
.u-package-name { font-size: 1.2rem; margin-bottom: .3em; }
.u-package-price { font-family: var(--u-font-heading); font-size: 1.9rem; letter-spacing: -0.03em; margin: 0 0 .4em; }
.u-package-summary { color: var(--u-muted); font-size: .94rem; }
.u-package .u-ticks { flex: 1; }
.u-fineprint { margin-top: 26px; color: var(--u-muted); font-size: .85rem; }

.u-detail-item h3 { font-size: 1.15rem; }
.u-detail-item p { color: var(--u-muted); margin: 0; }

/* Accordion */
.u-accordion { display: grid; gap: 10px; }
.u-acc-item {
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius);
	background: var(--u-surface);
	overflow: hidden;
}
.u-acc-item summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding: 18px 20px;
	cursor: pointer;
	font-weight: 560;
	list-style: none;
}
.u-acc-item summary::-webkit-details-marker { display: none; }
.u-acc-chevron { transition: transform 200ms var(--u-ease-out); }
.u-acc-item[open] .u-acc-chevron { transform: rotate(180deg); }
.u-acc-body { padding: 0 20px 20px; color: var(--u-muted); }


/* 9. Cards, blog, single
   ========================================================================== */

.u-page-head { padding-block: clamp(44px, 6vw, 84px) clamp(20px, 3vw, 36px); }
.u-page-head-lg { padding-block: clamp(80px, 11vw, 150px) clamp(24px, 4vw, 44px); }
.u-page-title { font-size: clamp(2rem, 5vw, 3.4rem); letter-spacing: -0.035em; }
.u-page-sub { color: var(--u-muted); max-width: 62ch; }

.u-breadcrumbs { margin-bottom: 20px; font-size: .85rem; color: var(--u-muted); }
.u-breadcrumbs ol { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0; padding: 0; }
.u-breadcrumbs li + li::before { content: "/"; margin-right: 8px; opacity: .5; }
.u-breadcrumbs a { transition: color 140ms ease; }
@media (hover: hover) and (pointer: fine) { .u-breadcrumbs a:hover { color: var(--u-text); } }

.u-post-card { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.u-card-link { position: absolute; inset: 0; z-index: 2; }
.u-card-media { overflow: hidden; background: rgba(var(--u-text-rgb), .05); }
.u-card-media img {
	width: 100%;
	aspect-ratio: 16 / 11;
	object-fit: cover;
	transition: transform 480ms var(--u-ease-out);
}
@media (hover: hover) and (pointer: fine) {
	.u-post-card:hover .u-card-media img { transform: scale(1.04); }
}
.u-card-body { padding: 24px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.u-card-title { font-size: 1.18rem; margin: 0; letter-spacing: -0.022em; }
.u-card-excerpt { color: var(--u-muted); font-size: .94rem; margin: 0; }
.u-card-more {
	display: inline-flex; align-items: center; gap: 6px;
	margin-top: auto; padding-top: 8px;
	font-size: .88rem; font-weight: 560; color: var(--u-primary);
}
.u-card-more .u-icon { width: 16px; height: 16px; transition: transform 200ms var(--u-ease-out); }
@media (hover: hover) and (pointer: fine) {
	.u-post-card:hover .u-card-more .u-icon,
	.u-post-row:hover .u-card-more .u-icon { transform: translateX(3px); }
}

.u-meta { display: flex; flex-wrap: wrap; gap: 14px; font-size: .82rem; color: var(--u-muted); }
.u-meta-item a { color: inherit; }

/* Wide list row. Thumbnail side is a single order swap. */
.u-post-row { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: 26px; align-items: center; padding: 16px; }
.u-row-media { border-radius: var(--u-radius); overflow: hidden; }
.u-row-media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; transition: transform 480ms var(--u-ease-out); }
.u-media-right .u-post-row { grid-template-columns: minmax(0, 1fr) 280px; }
.u-media-right .u-row-media { order: 2; }
.u-media-right .u-row-body { order: 1; }
.u-row-body { display: flex; flex-direction: column; gap: 10px; }

/* Single post */
.u-single-head { padding-block: clamp(50px, 8vw, 110px) clamp(18px, 3vw, 32px); }
.u-single-title { font-size: clamp(2rem, 4.6vw, 3.1rem); letter-spacing: -0.035em; }
.u-single-media { margin: 0 0 clamp(32px, 5vw, 56px); }
.u-single-media img { width: 100%; border-radius: var(--u-radius-lg); box-shadow: var(--u-shadow); }

.u-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 32px; }
.u-tags a {
	padding: 6px 13px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-pill);
	font-size: .82rem;
	color: var(--u-muted);
	transition: border-color 160ms ease, color 160ms ease;
}
@media (hover: hover) and (pointer: fine) { .u-tags a:hover { color: var(--u-text); border-color: var(--u-primary); } }

.u-share { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
.u-share-label { font-size: .85rem; color: var(--u-muted); }

.u-author { display: flex; gap: 18px; align-items: flex-start; margin-top: 40px; border-radius: var(--u-radius-lg); }
.u-author-avatar { border-radius: 50%; flex: none; }
.u-author-name { font-size: 1.05rem; margin: 0 0 .3em; }
.u-author-bio { color: var(--u-muted); font-size: .93rem; margin: 0; }

.u-post-nav { display: flex; justify-content: space-between; gap: 20px; margin-top: 44px; padding-top: 26px; border-top: 1px solid var(--u-border); font-size: .95rem; }
.u-post-nav span { display: block; font-size: .78rem; text-transform: uppercase; letter-spacing: .1em; color: var(--u-muted); margin-bottom: 5px; }
.u-post-nav-next { text-align: right; margin-left: auto; }

.u-related { margin-top: 64px; }

.u-pagination { margin-top: 48px; }
.u-pagination .nav-links { display: flex; flex-wrap: wrap; gap: 8px; }
.u-pagination .page-numbers {
	display: grid; place-items: center;
	min-width: 40px; height: 40px;
	padding: 0 12px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-pill);
	font-size: .9rem;
	transition: border-color 160ms ease, background-color 160ms ease;
}
.u-pagination .page-numbers.current { background: var(--u-primary); border-color: var(--u-primary); color: #fff; }

.u-empty { text-align: center; border-radius: var(--u-radius-lg); }
.u-empty .u-searchform { justify-content: center; margin-top: 20px; }

/* Sidebar & widgets */
.u-sidebar { display: grid; gap: 20px; align-content: start; position: sticky; top: 92px; }
.u-widget-title { font-size: 1rem; margin-bottom: .8em; }
.u-widget ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.u-widget a { color: var(--u-muted); transition: color 140ms ease; }
@media (hover: hover) and (pointer: fine) { .u-widget a:hover { color: var(--u-text); } }

/* Comments */
.u-comments { margin-top: 64px; }
.u-comment-list { list-style: none; margin: 0 0 40px; padding: 0; display: grid; gap: 24px; }
.u-comment-list .children { list-style: none; margin: 24px 0 0 clamp(16px, 4vw, 44px); padding: 0; display: grid; gap: 24px; }
.u-comment-list .comment-body { padding: 22px; border: 1px solid var(--u-border); border-radius: var(--u-radius); background: var(--u-surface); }
.u-comment-list .comment-author img { border-radius: 50%; margin-right: 10px; }
.u-comment-list .comment-meta { font-size: .82rem; color: var(--u-muted); margin-bottom: 10px; }
.u-comment-form { margin-top: 40px; border-radius: var(--u-radius-lg); }
.u-comments-closed { color: var(--u-muted); font-size: .92rem; }


/* 10. Forms
   ========================================================================== */

.u-form { display: grid; gap: 18px; border-radius: var(--u-radius-lg); }
.u-field { display: grid; gap: 7px; }
.u-field label, .comment-form label { font-size: .88rem; font-weight: 540; }

.u-form input,
.u-form select,
.u-form textarea,
.comment-form input:not([type="submit"]):not([type="checkbox"]),
.comment-form textarea,
.u-searchform input {
	width: 100%;
	padding: 12px 14px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius);
	background: var(--u-surface);
	color: var(--u-text);
	font: inherit;
	font-size: .96rem;
	transition: border-color 160ms ease, box-shadow 180ms var(--u-ease-out);
}
.u-form input:focus,
.u-form select:focus,
.u-form textarea:focus {
	outline: none;
	border-color: var(--u-primary);
	box-shadow: 0 0 0 3px rgba(var(--u-primary-rgb), .18);
}
.u-form textarea { resize: vertical; }

.u-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.u-form-msg { margin: 0; font-size: .9rem; min-height: 1.2em; }
.u-form-msg.is-error { color: #d13438; }
.u-form-msg.is-ok { color: var(--u-primary); }

/* Button loading state. The spinner spins fast on purpose — a faster
   spinner makes the same wait feel shorter. */
.u-btn-spinner {
	display: none;
	width: 15px; height: 15px;
	border: 2px solid rgba(255, 255, 255, .4);
	border-top-color: #fff;
	border-radius: 50%;
	animation: u-spin 620ms linear infinite;
}
.u-btn.is-loading .u-btn-spinner { display: block; }
.u-btn.is-loading { pointer-events: none; }
@keyframes u-spin { to { transform: rotate(360deg); } }

.u-searchform { display: flex; gap: 8px; }

.u-contact-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, .8fr); gap: clamp(24px, 4vw, 48px); align-items: start; }
.u-flip .u-contact-form-wrap { order: 2; }
.u-flip .u-contact-side { order: 1; }
.u-contact-side { display: grid; gap: 16px; }
.u-contact-side h2 { font-size: 1rem; margin-bottom: .5em; }
.u-contact-side p, .u-contact-side address { color: var(--u-muted); font-size: .94rem; font-style: normal; margin: 0; }
.u-contact-side a { color: var(--u-primary); }


/* 11. Footer layouts
   ========================================================================== */

.u-footer { margin-top: clamp(64px, 9vw, 128px); border-top: 1px solid var(--u-border); padding-top: clamp(44px, 6vw, 80px); }

.u-footer-top { display: grid; gap: clamp(28px, 4vw, 52px); padding-bottom: clamp(36px, 5vw, 64px); }
.u-footer-top.u-cols-2 { grid-template-columns: minmax(0, 1.5fr) repeat(2, minmax(0, 1fr)); }
.u-footer-top.u-cols-3 { grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr)); }
.u-footer-top.u-cols-4 { grid-template-columns: minmax(0, 1.5fr) repeat(4, minmax(0, 1fr)); }

.u-footer-about { color: var(--u-muted); font-size: .94rem; margin: 18px 0 14px; max-width: 40ch; }
.u-footer-address { font-style: normal; color: var(--u-muted); font-size: .9rem; }
.u-footer-menu { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; font-size: .94rem; }
.u-footer-menu a { color: var(--u-muted); transition: color 140ms ease; }
@media (hover: hover) and (pointer: fine) { .u-footer-menu a:hover { color: var(--u-text); } }

.u-cta-panel {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: clamp(24px, 4vw, 56px);
	flex-wrap: wrap;
	padding: clamp(32px, 5vw, 60px);
	border-radius: var(--u-radius-lg);
	background-image: linear-gradient(135deg, rgba(var(--u-primary-rgb), .12), rgba(var(--u-accent-rgb), .12));
	margin-bottom: clamp(32px, 4vw, 56px);
}
.u-cta-copy h2 { font-size: clamp(1.5rem, 3vw, 2.3rem); max-width: 18ch; margin: 0 0 .4em; }
.u-cta-copy p { color: var(--u-muted); max-width: 46ch; margin: 0; }
.u-cta-action { display: grid; gap: 12px; justify-items: start; }
.u-cta-mail { color: var(--u-muted); font-size: .9rem; }

.u-footer-bottom {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 18px;
	flex-wrap: wrap;
	padding-block: 26px;
	border-top: 1px solid var(--u-border);
	font-size: .88rem;
	color: var(--u-muted);
}
.u-footer-minimal .u-footer-bottom { border-top: 0; }
.u-copyright { margin: 0; }
.u-footer-bottom-end { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.u-legal-menu, .u-social-menu { list-style: none; display: flex; gap: 16px; margin: 0; padding: 0; }

/* Bottom bar item side */
.u-footer-align-left .u-footer-bottom { justify-content: flex-start; }
.u-footer-align-right .u-footer-bottom { justify-content: flex-end; flex-direction: row-reverse; }
.u-footer-align-center .u-footer-bottom { justify-content: center; text-align: center; }


/* 12. Coming soon
   ========================================================================== */

.u-coming { display: grid; min-height: 100vh; place-items: center; }
.u-coming-wrap {
	display: grid;
	place-items: center;
	width: 100%;
	min-height: 100vh;
	padding: 24px;
	background-size: cover;
	background-position: center;
}
.u-coming-panel {
	width: min(640px, 100%);
	padding: clamp(32px, 6vw, 60px);
	border-radius: var(--u-radius-lg);
	text-align: center;
	box-shadow: var(--u-shadow-lg);
}
.u-coming-brand { display: flex; justify-content: center; margin-bottom: 30px; }
.u-coming-panel .u-lead { margin-inline: auto; }
.u-countdown { display: flex; justify-content: center; gap: clamp(14px, 4vw, 34px); margin: 34px 0; }
.u-countdown div { display: grid; gap: 4px; }
.u-countdown span {
	font-family: var(--u-font-heading);
	font-size: clamp(1.8rem, 5vw, 2.8rem);
	line-height: 1;
	letter-spacing: -0.04em;
	font-variant-numeric: tabular-nums;
}
.u-countdown small { font-size: .74rem; text-transform: uppercase; letter-spacing: .1em; color: var(--u-muted); }
.u-notify { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-top: 28px; }
.u-notify input {
	flex: 1 1 240px;
	padding: 13px 16px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius);
	background: var(--u-surface);
	color: var(--u-text);
	font: inherit;
}
.u-notify-msg { flex-basis: 100%; margin: 4px 0 0; font-size: .88rem; }
.u-notify-msg.is-error { color: #d13438; }
.u-notify-msg.is-ok { color: var(--u-primary); }
.u-coming-contact { margin: 26px 0 0; font-size: .9rem; }
.u-coming-contact a { color: var(--u-primary); }


/* 13. Utilities & floating UI
   ========================================================================== */

.u-404 { padding-block: clamp(90px, 14vw, 180px); text-align: center; }
.u-404-actions { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; margin-top: 32px; }

.u-progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 400; pointer-events: none; }
.u-progress span {
	display: block;
	height: 100%;
	width: 0;
	background: linear-gradient(90deg, var(--u-primary), var(--u-accent));
	transform-origin: left;
}

.u-to-top {
	position: fixed;
	right: 22px; bottom: 22px;
	z-index: 200;
	display: grid;
	place-items: center;
	width: 46px; height: 46px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-pill);
	background: var(--u-surface);
	box-shadow: var(--u-shadow-sm);
	cursor: pointer;
	/* Starts small but never at scale(0) — nothing appears from nothing. */
	opacity: 0;
	transform: scale(.9) translateY(8px);
	pointer-events: none;
	transition: opacity 180ms var(--u-ease-out), transform 220ms var(--u-ease-out);
}
.u-to-top.is-visible { opacity: 1; transform: none; pointer-events: auto; }
.u-to-top:active { transform: scale(.94); }

.u-preloader {
	position: fixed; inset: 0; z-index: 500;
	display: grid; place-items: center;
	background: var(--u-bg);
	transition: opacity 300ms var(--u-ease-out), visibility 0s linear 300ms;
}
.u-preloader.is-done { opacity: 0; visibility: hidden; }
.u-preloader-mark {
	width: 34px; height: 34px;
	border-radius: 11px;
	background: linear-gradient(135deg, var(--u-primary), var(--u-accent));
	animation: u-pulse 1s var(--u-ease-in-out) infinite alternate;
}
@keyframes u-pulse { to { transform: scale(.75) rotate(45deg); opacity: .6; } }


/* 14. Motion
   ========================================================================== */

/* Scroll reveal. Entering -> ease-out, so the first frame is the biggest
   one and the movement reads as immediate. */
[data-u-reveal] {
	opacity: 0;
	transform: translateY(14px);
	transition:
		opacity 520ms var(--u-ease-out),
		transform 620ms var(--u-ease-out);
	transition-delay: var(--u-delay, 0ms);
}
[data-u-reveal].is-in { opacity: 1; transform: none; }
.u-no-motion [data-u-reveal] { opacity: 1; transform: none; transition: none; }

/* Hero headline. Each word rises behind a clipping mask. */
[data-u-words] .u-word { display: inline-block; overflow: hidden; vertical-align: bottom; }
[data-u-words] .u-word > span {
	display: inline-block;
	transform: translateY(105%);
	transition: transform 760ms var(--u-ease-out);
	transition-delay: calc(var(--i) * 55ms);
}
[data-u-words].is-in .u-word > span { transform: none; }
.u-no-motion [data-u-words] .u-word > span { transform: none; transition: none; }

/* Page transition. A short fade only — a translate here would fight the
   browser's own scroll restoration. */
.u-page { opacity: 1; transition: opacity 180ms ease; }
.u-page.is-leaving { opacity: 0; }

/* Tilt is applied by JS through a spring; the element only declares that
   it is composited. */
[data-u-tilt] { will-change: transform; transform-style: preserve-3d; }


/* 15. Responsive
   ========================================================================== */

@media (max-width: 1080px) {
	.u-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.u-footer-top.u-cols-3,
	.u-footer-top.u-cols-4 { grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
	.u-header-nav { display: none; }
	.u-burger { display: block; }
	.u-header-split .u-header-inner { grid-template-columns: auto 1fr auto; }
	.u-header-split .u-header-center { justify-self: start; }

	.u-layout.has-sidebar,
	.u-sidebar-left .u-layout.has-sidebar { grid-template-columns: minmax(0, 1fr); }
	.u-sidebar { position: static; }
	.u-sidebar-left .u-content { order: 1; }
	.u-sidebar-left .u-sidebar { order: 2; }

	.u-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.u-split-inner { grid-template-columns: minmax(0, 1fr); }
	.u-flip .u-split-copy, .u-flip .u-split-media { order: 0; }
	.u-stats-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.u-contact-grid { grid-template-columns: minmax(0, 1fr); }
	.u-flip .u-contact-form-wrap, .u-flip .u-contact-side { order: 0; }
	.u-post-row,
	.u-media-right .u-post-row { grid-template-columns: minmax(0, 1fr); }
	.u-media-right .u-row-media, .u-media-right .u-row-body { order: 0; }
	.u-hero-card-body { flex-direction: column; align-items: flex-start; gap: 20px; }
}

@media (max-width: 620px) {
	.u-grid-2, .u-grid-3, .u-grid-4 { grid-template-columns: minmax(0, 1fr); }
	.u-footer-top.u-cols-2,
	.u-footer-top.u-cols-3,
	.u-footer-top.u-cols-4 { grid-template-columns: minmax(0, 1fr); }
	.u-header-end .u-btn { display: none; }
	.u-topbar-inner a { display: none; }
	.u-footer-bottom, .u-footer-align-right .u-footer-bottom { flex-direction: column; align-items: flex-start; }
}


/* 16. Reduced motion / transparency / contrast
   ========================================================================== */

/* Reduced motion is not "no feedback" — opacity and colour transitions
   stay, positional and vestibular motion goes. */
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }

	[data-u-reveal] { transform: none; transition: opacity 200ms ease; }
	[data-u-words] .u-word > span { transform: none; transition: opacity 200ms ease; }

	.u-marquee-track { animation: none; transform: none; }
	.u-hero-glow { display: none; }
	.u-preloader-mark { animation: none; }
	.u-btn-spinner { animation-duration: 1.4s; }

	.u-drawer-panel { transition: opacity 200ms ease; }
	.u-search-panel { filter: none; transform: none; transition: opacity 200ms ease; }
	.u-card-media img, .u-row-media img { transition: none; }

	.u-btn:active { transform: none; }
	.u-to-top { transform: none; }
}

@media (prefers-reduced-transparency: reduce) {
	.u-glass, .u-menu .sub-menu { background: var(--u-surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
	.u-hero-glow { opacity: .35; }
}

@media (prefers-contrast: more) {
	.u-glass { background: var(--u-surface); border-color: var(--u-text); }
	.u-menu a, .u-card-excerpt, .u-lead, .u-meta { color: var(--u-text); }
	.u-btn-ghost { border-color: var(--u-text); }
}

@media print {
	.u-header, .u-footer, .u-to-top, .u-progress, .u-drawer, .u-search-overlay { display: none !important; }
	body { background: #fff; color: #000; }
}


/* ======================================================================= */
/* Header over hero media                                                   */
/* -----------------------------------------------------------------------
   When the hero carries a background image or video, the header stops
   being a bar and becomes type floating on the picture. The server decides
   this — see inc/header-state.php — and prints .u-header-over-media on the
   body plus .is-over-media on the header, so there is no flash of a solid
   header before script runs.

   Three separate problems, solved in order below:

     1. the header has to physically overlap the hero rather than sit above
        it in the flow
     2. everything in it has to be legible on a photograph the theme has
        never seen
     3. it has to become an ordinary solid header again the moment you
        scroll past the hero
   ======================================================================= */

/*
 * 1. Out of the flow.
 *
 * A sticky header still occupies its space in the document, so the
 * photograph would start below it and you would get a strip of page colour
 * across the top of the image. Taking the header out of the flow lets the
 * hero rise to y=0 and the header float on top of it.
 *
 * fixed when the site has a sticky header, absolute when it does not —
 * both remove it from the flow, and the choice preserves whatever
 * behaviour the setting promised.
 */
.u-header-over-media .u-header {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
}

.u-sticky-header.u-header-over-media .u-header {
	position: fixed;
}

/*
 * 2a. The bar goes completely.
 *
 * Not "more transparent" — no background, no border, no shadow, no blur.
 * A 10%-opacity bar over a photograph reads as a smudge, which is worse
 * than either a solid bar or nothing at all.
 */
.u-header.is-over-media:not(.is-scrolled) .u-header-bar {
	background: transparent;
	border-color: transparent;
	box-shadow: none;
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
}

/*
 * 2b. The veil.
 *
 * This is the part that makes it safe rather than lucky. The theme cannot
 * know whether the photograph is a dark interior or a bright sky, so white
 * navigation on its own is a coin toss. A soft gradient from the very top,
 * fading out well before the bottom of the header, guarantees a dark
 * substrate for the type without reading as a bar — it looks like the
 * natural vignette at the top of a photograph, which is why it is a
 * gradient and not a flat panel.
 *
 * It is behind the header's own content because .u-header carries z-index,
 * which makes it a stacking context and keeps the -1 local.
 */
.u-header.is-over-media::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 210%;
	z-index: -1;
	pointer-events: none;
	background: linear-gradient(
		180deg,
		rgba(var(--u-header-veil, 8, 10, 14), 0.5) 0%,
		rgba(var(--u-header-veil, 8, 10, 14), 0.22) 45%,
		transparent 100%
	);
	transition: opacity 240ms ease;
}

/* 2c. Everything in it goes white. The burger and the icon buttons draw
   themselves in currentColor, so the colour on the header is enough for
   those; the nav links and the logo set their own and need saying. */
.u-header.is-over-media:not(.is-scrolled) {
	color: #fff;
}

.u-header.is-over-media:not(.is-scrolled) .u-header-nav a,
.u-header.is-over-media:not(.is-scrolled) .u-menu a,
.u-header.is-over-media:not(.is-scrolled) .u-logo-text,
.u-header.is-over-media:not(.is-scrolled) .u-logo-name,
.u-header.is-over-media:not(.is-scrolled) .u-icon-btn,
.u-header.is-over-media:not(.is-scrolled) .u-burger {
	color: #fff;
}

/* The ghost CTA borrows the page border colour, which is a pale hairline
   meant for a light surface and disappears entirely over a photograph. */
.u-header.is-over-media:not(.is-scrolled) .u-btn-ghost {
	--btn-fg: #fff;
	border-color: rgba(255, 255, 255, 0.5);
}

/* The primary CTA goes to white rather than the accent. Brand colours are
   warm mid-tones far more often than not, and a warm mid-tone button on a
   photograph is invisible — a near-white block is the one thing that
   separates from any picture. */
.u-header.is-over-media:not(.is-scrolled) .u-btn-primary {
	--btn-bg: #fff;
	--btn-fg: var(--u-text);
}

@media (hover: hover) {
	.u-header.is-over-media:not(.is-scrolled) .u-icon-btn:hover {
		background: rgba(255, 255, 255, 0.16);
	}
}

/*
 * 3. Back to normal on scroll.
 *
 * Everything above is scoped to :not(.is-scrolled), so the solid header
 * returns on its own. Only the veil needs switching off explicitly,
 * because it is drawn on the header rather than by it.
 */
.u-header.is-over-media.is-scrolled::before {
	opacity: 0;
}

@media (prefers-contrast: more) {
	/*
	 * A gradient is a probabilistic answer, and somebody asking for more
	 * contrast has said they do not want one. The header gets a real
	 * surface and its ordinary ink back.
	 */
	.u-header.is-over-media:not(.is-scrolled) .u-header-bar {
		background: var(--u-surface);
		border-color: var(--u-border);
	}

	.u-header.is-over-media:not(.is-scrolled),
	.u-header.is-over-media:not(.is-scrolled) .u-header-nav a,
	.u-header.is-over-media:not(.is-scrolled) .u-menu a,
	.u-header.is-over-media:not(.is-scrolled) .u-logo-text,
	.u-header.is-over-media:not(.is-scrolled) .u-logo-name,
	.u-header.is-over-media:not(.is-scrolled) .u-icon-btn,
	.u-header.is-over-media:not(.is-scrolled) .u-burger {
		color: var(--u-text);
	}

	.u-header.is-over-media::before {
		display: none;
	}
}

@media print {
	/* Back into the flow, or the first line of the page prints underneath
	   the masthead. */
	.u-header-over-media .u-header,
	.u-sticky-header.u-header-over-media .u-header {
		position: static;
	}

	.u-header.is-over-media::before { display: none; }

	.u-header.is-over-media,
	.u-header.is-over-media .u-header-nav a,
	.u-header.is-over-media .u-logo-text { color: #000; }
}


/* ======================================================================= */
/* Bottom bar — glass mode                                                  */
/* -----------------------------------------------------------------------
   Off by default. Design -> Header -> Mobile bottom bar -> "Transparent
   glass bar", with a strength slider next to it.

   Why it is opt-in rather than the default: the rule a few hundred lines
   above deliberately forces every navigation surface to the opaque tint,
   because a drawer, a search panel or a bottom bar floats over arbitrary
   page content rather than sitting on the page background. At a card-weight
   tint the headline scrolling underneath shows straight through the tab
   labels. That is still true — this just makes it a decision somebody can
   take with their eyes open, on their own content, instead of a decision
   the theme takes for them.

   Three things make it survivable:

     - the tint has its own variable with a 35% floor, rather than following
       the card setting, which somebody may quite reasonably have set to 20%
     - the blur is heavier than a card's, because blur is what turns text
       behind the bar from words into texture
     - it falls back to solid where backdrop-filter is unsupported, and
       where the visitor has asked for reduced transparency
   ======================================================================= */

.u-bottomnav.is-glass .u-bottomnav-inner.u-glass {
	background: var(--u-bnav-glass, var(--u-glass-solid, var(--u-surface)));

	/*
	 * Half again the blur of an ordinary glass surface, and a saturation
	 * lift on top.
	 *
	 * Blur is doing the real work: it is what stops the text underneath
	 * being readable as text, which is the difference between a frosted
	 * panel and a transparency bug. The saturation is cosmetic — blurring
	 * washes colour out, and pushing it back is what stops the bar looking
	 * like grey fog over a colourful page.
	 */
	backdrop-filter: blur(calc(var(--u-blur, 14px) * 1.5)) saturate(1.7);
	-webkit-backdrop-filter: blur(calc(var(--u-blur, 14px) * 1.5)) saturate(1.7);

	border: 1px solid var(--u-glass-edge);

	/* A hairline of the surface colour along the top edge. Real frosted
	   glass catches light on its leading edge, and without it a translucent
	   bar has no defined top and reads as a smear rather than an object. */
	box-shadow:
		inset 0 1px 0 rgba(var(--u-surface-rgb), 0.6),
		var(--u-shadow-lg);
}

/*
 * No backdrop-filter, no glass.
 *
 * Without the blur, a 62%-opacity bar is not frosted glass — it is a
 * see-through bar with legible page text behind legible label text, which
 * is worse than either. The fallback is the ordinary solid bar.
 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.u-bottomnav.is-glass .u-bottomnav-inner.u-glass {
		background: var(--u-glass-solid, var(--u-surface));
		box-shadow: var(--u-shadow-lg);
	}
}

@media (prefers-reduced-transparency: reduce) {
	/* Somebody who has asked for less transparency has answered this
	   question already, and their answer outranks the setting. */
	.u-bottomnav.is-glass .u-bottomnav-inner.u-glass {
		background: var(--u-glass-solid, var(--u-surface));
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
		box-shadow: var(--u-shadow-lg);
	}
}

@media (prefers-contrast: more) {
	.u-bottomnav.is-glass .u-bottomnav-inner.u-glass {
		background: var(--u-surface);
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
		border-color: var(--u-border);
	}
}


/* Cookie settings — the withdraw control in the footer bottom bar.
   Styled as a quiet link rather than a button, because it sits among the
   legal menu items and a filled button there would read as a call to
   action rather than as the small administrative control it is. */
.u-cookies-reopen {
	border: 0;
	background: none;
	padding: 0;
	font: inherit;
	font-size: 0.85rem;
	color: var(--u-muted);
	cursor: pointer;
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.u-cookies-reopen:hover {
	color: var(--u-text);
}

.u-cookies[hidden] {
	display: none;
}


/* ======================================================================= */
/* Per-section type and alignment                                           */
/* -----------------------------------------------------------------------
   Studio -> Section styles writes custom properties onto a wrapper around
   the section. These rules read them.

   The wrapper generates no box: display: contents means it takes part in
   inheritance and in selectors while producing no layout of its own. That
   is the only reason it is safe around a position: sticky section — a real
   element there would break the pinning the moment it acquired a padding,
   an overflow or a transform.

   EVERY PROPERTY IS BEHIND ITS OWN CLASS, and that is the important part.

   The tempting version is one rule with fallbacks:

       font-family: var(--u-sec-font, inherit);
       font-size:   var(--u-sec-size, revert-layer);

   Both are wrong. `inherit` on a heading does not mean "whatever the theme
   set for headings" — it means the parent element's value, which is the
   body font at the body weight. So setting any single control on a section
   would fire the whole rule and silently replace a demo's serif heading
   with the body sans. `revert-layer` outside a cascade layer reverts to the
   browser's own stylesheet, which is worse.

   A class per control has no such semantics: the declaration is simply not
   in the cascade unless that control was set.
   ======================================================================= */

/* =======================================================================
   WHY EVERY DECLARATION BELOW IS !important
   =======================================================================

   This was the "I set a smaller heading and nothing happens" bug, and it
   had nothing to do with saving. The value saved correctly every time.

   Every demo styles its headings through the body class:

       body.u-style-maxi .u-section-title { font-size: ... }   (0,2,1)

   and the override here was:

       .u-sec-has-size .u-section-title   { font-size: ... }   (0,2,0)

   The demo selector carries one more component, so it wins on specificity.
   And even at a tie it would still have won, because the demo stylesheets
   are enqueued after main.css. The override lost the cascade twice over
   and produced no visible change at any setting.

   Raising specificity by hand cannot fix this reliably: style-maxi.css
   contains `body.u-style-maxi .u-footer-cta .u-section-title`, at (0,3,1),
   and a child theme is free to go higher still. Any number chosen here is
   a number somebody can beat, and the failure is silent when they do.

   !important is the right instrument rather than a workaround. This is a
   user-authored override layer whose entire purpose is to defeat the
   theme's own styling — that is the case the flag exists for. Nothing
   above it has a legitimate claim to win: if the site owner has explicitly
   chosen a heading size for a section, no demo stylesheet should be able
   to quietly overrule it.

   The declarations are still absent unless the control was set, because
   the u-sec-has-* class is only printed for controls that carry a value.
   An untouched site has no !important anywhere.
   ======================================================================= */

.u-sec {
	display: contents;
}

/* The heading elements each control reaches. */
.u-sec-has-font .u-section-title,
.u-sec-has-font .u-hero-title,
.u-sec-has-font .u-display,
.u-sec-has-font .u-reel-line p,
.u-sec-has-font .u-page-title {
	font-family: var(--u-sec-font) !important;
}

.u-sec-has-weight .u-section-title,
.u-sec-has-weight .u-hero-title,
.u-sec-has-weight .u-display,
.u-sec-has-weight .u-reel-line p,
.u-sec-has-weight .u-page-title {
	font-weight: var(--u-sec-weight) !important;
}

.u-sec-has-color .u-section-title,
.u-sec-has-color .u-hero-title,
.u-sec-has-color .u-display,
.u-sec-has-color .u-reel-line p,
.u-sec-has-color .u-page-title {
	color: var(--u-sec-color) !important;
}

.u-sec-has-size .u-section-title,
.u-sec-has-size .u-hero-title,
.u-sec-has-size .u-display,
.u-sec-has-size .u-reel-line p,
.u-sec-has-size .u-page-title {
	font-size: var(--u-sec-size) !important;
}

.u-sec-has-text .u-section-text,
.u-sec-has-text .u-lead,
.u-sec-has-text .u-reel-note,
.u-sec-has-text .u-depth-item p {
	color: var(--u-sec-text) !important;
}

/* The section heading block and its intro paragraph. The content below
   keeps its own layout: centring a three-column grid does not centre it,
   it makes it lopsided. */
.u-sec-has-align .u-section-head,
.u-sec-has-align .u-reel-head,
.u-sec-has-align .u-depth-head {
	text-align: var(--u-sec-align) !important;
}

/*
 * A centred heading block needs its measure re-anchored too.
 *
 * The intro paragraph has a max-width. Centring the words inside that
 * column while the column itself still hugs the left edge looks like a
 * mistake rather than a choice. The margin is computed in PHP from the
 * alignment, because "centre means auto, right means auto 0" is a one-line
 * lookup and a selector that tries to derive it is a puzzle.
 */
.u-sec-has-align .u-section-head .u-section-text,
.u-sec-has-align .u-section-head .u-lead,
.u-sec-has-align .u-reel-head .u-section-text {
	margin-inline: var(--u-sec-measure) !important;
}

@media print {
	/* A colour chosen for a screen is rarely right for paper, and a heading
	   set to white prints as nothing at all. */
	.u-sec-has-color .u-section-title,
	.u-sec-has-color .u-hero-title,
	.u-sec-has-color .u-display,
	.u-sec-has-text .u-section-text,
	.u-sec-has-text .u-lead {
		/* !important here is a consequence of !important on the screen rule
		   above, not a separate decision. Print rules normally win simply by
		   coming later, but they cannot outrank an !important declaration
		   without carrying one themselves — and without this a heading set
		   to white would print as a blank space on the page. */
		color: #000 !important;
	}
}

/* ==========================================================================
   PAGE TOOLS — price calculator and website audit
   ==========================================================================

   Structural only. This is what makes a wizard a wizard and a result list a
   result list; the demo style layers decide what it all looks like.

   It lives in main.css rather than in a demo layer because both tools are
   reachable from a page template in every demo. A calculator that only has
   a layout in one of five designs is a calculator that is broken in four.

   NOTE ON THE NO-JAVASCRIPT STATE
   -------------------------------
   All four steps are visible by default and the script hides the inactive
   ones. Written the other way round — hidden here, revealed by script — a
   blocked or failed script would leave a page with one visible question and
   no way to reach the rest, and the prices would be invisible to a search
   engine. The default state of any progressive enhancement should be the
   working one.
   ========================================================================== */

.u-calc { display: grid; gap: 24px; }

@media (min-width: 1000px) {
	.u-calc {
		grid-template-columns: minmax(0, 1fr) minmax(0, 340px);
		grid-template-areas:
			"rail   out"
			"panels out"
			"nav    out";
		align-items: start;
		gap: 20px 32px;
	}
	.u-calc-rail { grid-area: rail; }
	.u-calc-panels { grid-area: panels; }
	.u-calc-nav { grid-area: nav; }
	.u-calc-out { grid-area: out; position: sticky; top: 96px; }
}

/* The rail is built by script, so it is empty and invisible without it. */
.u-calc-rail { display: flex; flex-wrap: wrap; gap: 8px; }
.u-calc-rail:empty { display: none; }

.u-rail-dot {
	display: inline-flex;
	align-items: baseline;
	gap: 8px;
	padding: 8px 14px;
	border: 1px solid var(--u-border);
	background: transparent;
	color: var(--u-muted);
	font: inherit;
	font-size: 0.82rem;
	cursor: pointer;
	border-radius: var(--u-radius-sm, 6px);
}

.u-rail-dot.is-active { color: var(--u-text); border-color: var(--u-text); }
.u-rail-dot.is-done { color: var(--u-text); }
.u-rail-n { font-variant-numeric: tabular-nums; opacity: .6; }

.u-calc-step {
	border: 0;
	margin: 0;
	padding: 0;
	min-width: 0;
}

.u-calc-step:focus { outline: none; }

.u-calc-legend {
	display: flex;
	align-items: baseline;
	gap: 12px;
	font-size: clamp(1.2rem, 2.4vw, 1.6rem);
	font-weight: 600;
	letter-spacing: -0.02em;
	margin-bottom: 16px;
	padding: 0;
}

.u-calc-n { font-variant-numeric: tabular-nums; color: var(--u-muted); font-size: .8em; }

.u-calc-hint { color: var(--u-muted); margin: 0 0 14px; font-size: .92rem; }

.u-calc-options { display: grid; gap: 10px; }

@media (min-width: 700px) {
	.u-calc-options-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.u-calc-option {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: start;
	gap: 12px;
	padding: 14px 16px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-sm, 8px);
	cursor: pointer;
	transition: border-color 140ms ease, background-color 140ms ease;
}

.u-calc-option:hover { border-color: var(--u-text); }

/* The native control is kept, not replaced.

   It is made small rather than display:none, because a hidden input is not
   focusable in some browsers and :checked styling then has nothing to hang
   the focus ring on. Keeping it in the layout keeps keyboard operation and
   screen-reader semantics exactly as the browser intended. */
.u-calc-option input { margin-top: .2em; accent-color: var(--u-primary); }

.u-calc-option:focus-within {
	outline: 2px solid var(--u-primary);
	outline-offset: 2px;
}

.u-calc-option:has(input:checked) {
	border-color: var(--u-text);
	background: color-mix(in srgb, var(--u-primary) 7%, transparent);
}

.u-calc-option-body { display: grid; gap: 3px; min-width: 0; }
.u-calc-option-title { font-weight: 600; }
.u-calc-option-text { color: var(--u-muted); font-size: .88rem; line-height: 1.5; }

.u-calc-price {
	font-variant-numeric: tabular-nums;
	font-weight: 600;
	white-space: nowrap;
}

/* Stepper. */
.u-stepper { display: inline-flex; align-items: stretch; border: 1px solid var(--u-border); border-radius: var(--u-radius-sm, 8px); overflow: hidden; }
.u-stepper-btn { border: 0; background: transparent; color: inherit; font: inherit; font-size: 1.2rem; width: 48px; cursor: pointer; }
.u-stepper-btn:hover { background: color-mix(in srgb, var(--u-text) 8%, transparent); }
.u-stepper-field {
	width: 88px;
	text-align: center;
	border: 0;
	border-inline: 1px solid var(--u-border);
	background: transparent;
	color: inherit;
	font: inherit;
	font-variant-numeric: tabular-nums;
	padding: 12px 0;
}

.u-calc-nav { display: flex; gap: 10px; }
.u-calc-nav .u-btn[hidden] { display: none; }

/* Result panel. */
.u-calc-out {
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-sm, 8px);
	padding: 20px;
	display: grid;
	gap: 12px;
	align-content: start;
}

.u-calc-out-label { margin: 0; font-size: .72rem; color: var(--u-muted); }

.u-calc-out-total {
	margin: 0;
	font-size: clamp(1.6rem, 3.4vw, 2.3rem);
	font-weight: 700;
	letter-spacing: -0.03em;
	font-variant-numeric: tabular-nums;
	line-height: 1.05;
}

.u-calc-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.u-calc-lines:empty { display: none; }

.u-calc-line {
	display: flex;
	justify-content: space-between;
	gap: 12px;
	font-size: .86rem;
	color: var(--u-muted);
	border-bottom: 1px dotted var(--u-border);
	padding-bottom: 5px;
}

.u-calc-line-amount { font-variant-numeric: tabular-nums; white-space: nowrap; }

.u-calc-note,
.u-calc-privacy { margin: 0; font-size: .72rem; color: var(--u-muted); line-height: 1.55; }

.u-calc-actions { display: grid; gap: 8px; }

/* Panel entrance. Direction-aware, so going back visibly reverses. */
.u-calc.is-enhanced .u-calc-step[data-u-dir="fwd"] { animation: u-panel-fwd 260ms cubic-bezier(.16,1,.3,1) both; }
.u-calc.is-enhanced .u-calc-step[data-u-dir="back"] { animation: u-panel-back 260ms cubic-bezier(.16,1,.3,1) both; }

@keyframes u-panel-fwd { from { opacity: 0; transform: translateX(14px); } }
@keyframes u-panel-back { from { opacity: 0; transform: translateX(-14px); } }


/* -- Audit ---------------------------------------------------------------- */

.u-audit { display: grid; gap: 22px; }

/* Let every part of the audit panel shrink to the screen it is on.
   ------------------------------------------------------------------
   This is the fix for a real, measured 93px of sideways scroll on a 390px
   phone — the blank strip down the right-hand side of the page.

   The cause is a default almost nobody expects: grid and flex items get
   `min-width: auto`, not `0`. That means they refuse to be laid out
   narrower than their own content, and a bare `<input type="text">` carries
   an intrinsic width of roughly twenty characters whether you asked for one
   or not. So the input insisted on ~177px, the "https://" prefix and the
   button took the rest, and the form declared itself 444px wide inside a
   331px column. The grid track grew to match, the page grew with it, and
   `overflow-x: hidden` on <body> hid the evidence on desktop while phones
   happily scrolled into the empty space.

   `min-width: 0` on the chain is what actually permits shrinking. The
   `flex-basis: 8ch` is what makes the input take its share of whatever is
   left rather than dictating the size of the row. */
.u-audit > *,
.u-calc > * { min-width: 0; }

.u-audit-form { min-width: 0; }

.u-audit-field {
	display: flex;
	align-items: stretch;
	gap: 0;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-sm, 8px);
	overflow: hidden;
	max-width: 620px;
	min-width: 0;
	flex-wrap: wrap;
}

.u-audit-scheme {
	display: flex;
	align-items: center;
	padding: 0 4px 0 14px;
	color: var(--u-muted);
	font-size: .82rem;
	white-space: nowrap;
}

.u-audit-input {
	flex: 1 1 8ch;
	width: 100%;
	min-width: 0;
	border: 0;
	background: transparent;
	color: inherit;
	font: inherit;
	padding: 14px 10px;
}

.u-audit-input:focus { outline: none; }
.u-audit-field:focus-within { outline: 2px solid var(--u-primary); outline-offset: 2px; }

.u-audit-field .u-btn { border-radius: 0; }

/* On a phone the button takes its own line under the field.
   Three things on one row at 390px leaves the input about 90px wide — too
   narrow to see the domain you just typed, which is the one thing you need
   to check before submitting. A full-width button is also a bigger tap
   target, so this is not purely a concession to the width. */
@media (max-width: 560px) {
	.u-audit-field .u-btn {
		flex: 1 0 100%;
		border-top: 1px solid var(--u-border);
	}
}

.u-audit-msg,
.u-audit-privacy { margin: 10px 0 0; font-size: .74rem; color: var(--u-muted); }

.u-audit-h {
	font-size: .74rem;
	text-transform: uppercase;
	letter-spacing: .1em;
	color: var(--u-muted);
	margin: 22px 0 8px;
}

.u-audit-note { margin: 0 0 10px; font-size: .84rem; color: var(--u-muted); max-width: 62ch; line-height: 1.55; }

.u-audit-list { list-style: none; margin: 0; padding: 0; }

.u-audit-row {
	display: grid;
	grid-template-columns: 18px 1fr auto;
	gap: 12px;
	align-items: center;
	padding: 11px 0;
	border-bottom: 1px solid var(--u-border);
}

.u-audit-mark { width: 10px; height: 10px; border-radius: 50%; justify-self: center; }
.u-audit-row.is-pass .u-audit-mark { background: #16a34a; }
.u-audit-row.is-warn .u-audit-mark { background: #d97706; }
.u-audit-row.is-fail .u-audit-mark { background: #dc2626; }

/* "Not visible from a browser" is not a failure and must not be coloured
   like one. A hollow ring reads as "not applicable" rather than "bad". */
.u-audit-row.is-na .u-audit-mark {
	background: transparent;
	border: 1px solid var(--u-border);
}

.u-audit-detail { color: var(--u-muted); font-size: .84rem; text-align: right; }

.u-audit-links { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.u-audit-links a {
	display: inline-block;
	padding: 8px 14px;
	border: 1px solid var(--u-border);
	border-radius: var(--u-radius-sm, 6px);
	font-size: .84rem;
	text-decoration: none;
	color: inherit;
}
.u-audit-links a:hover { border-color: var(--u-text); }

.u-audit[data-u-audit-busy] .u-audit-msg::after {
	content: "";
	display: inline-block;
	width: 1em;
	animation: u-dots 1.2s steps(4) infinite;
}

@keyframes u-dots { to { content: "…"; } }

.u-audit-ref-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }

.u-audit-ref-row {
	display: grid;
	grid-template-columns: 42px minmax(0, 1fr);
	gap: 4px 16px;
	padding: 18px 0;
	border-top: 1px solid var(--u-border);
}

.u-audit-ref-row h3 { margin: 0; font-size: 1.02rem; font-weight: 600; }
.u-audit-ref-row p { margin: 0; grid-column: 2; color: var(--u-muted); font-size: .9rem; line-height: 1.6; max-width: 68ch; }
.u-audit-ref-n { grid-row: span 2; color: var(--u-muted); font-size: .74rem; }

@media (prefers-reduced-motion: reduce) {
	.u-calc.is-enhanced .u-calc-step[data-u-dir="fwd"],
	.u-calc.is-enhanced .u-calc-step[data-u-dir="back"] { animation: none; }
	.u-audit[data-u-audit-busy] .u-audit-msg::after { animation: none; content: "…"; }
}

/* ==========================================================================
   SECTION BACKDROPS — image or video behind a hero
   ==========================================================================

   Structural only, and shared. The demo layers decide how the text sitting
   on top of one should look; this decides what a backdrop IS.

   Nothing here applies unless the section carries .has-backdrop, and PHP
   only prints that class when a backdrop is actually configured. An
   untouched site pays nothing for this file.
   ========================================================================== */

.has-backdrop {
	position: relative;
	isolation: isolate;
}

.u-backdrop {
	position: absolute;
	inset: 0;
	overflow: hidden;
	/* Behind the content but inside the section's own stacking context, so
	   a backdrop can never escape upward over the header or a sticky bar. */
	z-index: -1;
}

.u-backdrop-img,
.u-backdrop-video,
.u-backdrop-embed iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border: 0;
	display: block;
}

/* An embed cannot be object-fit — it is an iframe with its own aspect ratio,
   and a 16:9 frame in a 21:9 hero letterboxes with black bars down the
   sides. So it is oversized and centred instead: whichever axis runs out
   first, the frame is already bigger than it, and the overflow is clipped.

   The numbers are the smallest that cover every viewport shape a phone or
   monitor actually produces. */
.u-backdrop-embed iframe {
	top: 50%;
	left: 50%;
	width: 177.78vh;   /* 16/9 of the viewport height */
	min-width: 100%;
	height: 56.25vw;   /* 9/16 of the viewport width  */
	min-height: 100%;
	transform: translate(-50%, -50%);
	pointer-events: none;
}

/* The scrim.

   A single darkening layer rather than a gradient, because the text in
   these two heroes is not confined to the top or the bottom — the console
   has a status strip along the bottom edge and the grid hero has a fact row
   there, so a top-weighted gradient would leave the lower content sitting
   on bare photograph.

   It is not optional and not a taste decision: text over an arbitrary
   photograph has no contrast guarantee at all. The image is chosen by the
   site owner, its exposure is unknown here, and the only thing that can be
   said with certainty about white text on it is that it might be
   unreadable. */
.u-backdrop-scrim {
	position: absolute;
	inset: 0;
	display: block;
	background: rgb(0 0 0 / var(--u-bd-scrim, 0.55));
}

/* While a video is still deferred — or was never sent, because the visitor
   asked for reduced motion, is on a metered connection, or is on a phone —
   the poster image is what shows. It is already in the markup, so there is
   nothing to swap and no flash of empty black. */
.has-backdrop-video .u-backdrop-img {
	z-index: 0;
}

.has-backdrop-video .u-backdrop-video,
.has-backdrop-video .u-backdrop-embed {
	z-index: 1;
}

.u-backdrop-scrim {
	z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
	/* The script already declines to attach a source, so this is belt and
	   braces for a video that arrived some other way — a cached page, a
	   builder preview, a child theme printing its own. */
	.u-backdrop-video,
	.u-backdrop-embed { display: none; }
}

@media print {
	.u-backdrop { display: none; }
}


/* -- Volume hero (WebGL) --------------------------------------------------
   The canvas is decoration behind real text, never a container for it. The
   words, the buttons and the still image are all laid out by CSS and would
   sit exactly where they sit now if the canvas never appeared — which for a
   reduced-motion visitor, a phone, or anyone whose CDN request fails, is
   what happens.
   ------------------------------------------------------------------------ */

.u-volume {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	display: flex;
	align-items: center;
	min-height: min(86vh, 780px);
	padding-block: clamp(90px, 12vw, 150px);
}

/* Canvas and still both fill the section and both sit behind the type.
   The still is underneath rather than swapped in, so the fallback path has
   no flash of empty space before the image arrives. */
.u-volume-canvas,
.u-volume-still {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	z-index: -1;
	pointer-events: none;
}

.u-volume-still {
	background-image: var(--u-vol-poster, none);
	background-size: cover;
	background-position: center;
}

/* The canvas paints its own opaque ground, so it covers the still the
   moment it is live. Before that it is transparent and the still shows. */
.u-volume-canvas { display: block; opacity: 0; transition: opacity 700ms ease; }
.u-volume.is-live .u-volume-canvas { opacity: 1; }

.u-volume-inner { position: relative; }

/*
 * TWO HALVES, BUT ONLY WHEN THERE IS SOMETHING TO PUT IN THE SECOND ONE.
 *
 * .has-split is added by PHP when a picture has been chosen, and only then.
 * A hero with no picture keeps the single full-width column it always had,
 * so this is additive: an existing site sees no change at all until somebody
 * sets an image. That is also why the grid lives on the modifier rather than
 * on .u-volume-inner with the second column collapsing to zero — an empty
 * grid track still eats the gap, and the copy would shift left for no
 * visible reason.
 *
 * 1.05fr against 1fr, not 1fr against 1fr. The copy column carries a
 * headline, a lead and two buttons and needs the extra measure; a dead
 * even split left the headline breaking a word earlier than it should.
 */
.u-volume.has-split .u-volume-inner {
	display: grid;
	grid-template-columns: 1.05fr 1fr;
	gap: clamp(32px, 5vw, 72px);
	align-items: center;
}

.u-volume-copy { min-width: 0; }

.u-volume-figure {
	margin: 0;
	min-width: 0;
}

/*
 * A fixed aspect box, so the hero cannot change height when the picture
 * loads or when somebody swaps a landscape shot for a square one. Reserving
 * the space up front is also what keeps this off the layout-shift budget.
 */
.u-volume-image {
	display: block;
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: var(--u-radius-lg);
	border: 1px solid var(--u-border);
	box-shadow: var(--u-shadow-lg);
	background: var(--u-surface);
}

.u-volume-eyebrow { margin: 0 0 14px; }

/*
 * Larger than the shared hero size, on purpose.
 *
 * .u-hero-title inherits h1 at the theme default, which is tuned for heroes
 * that sit on a plain background. This one sits on a moving lattice, and
 * type that holds its own against a background in motion has to be heavier
 * than type that only has to sit on paper. The floor matters more than the
 * ceiling here: 2.7rem rather than the inherited size is what stops the
 * headline shrinking into the field on a laptop.
 */
/*
 * THE HEADLINE HAS TWO SIZES, BECAUSE IT HAS TWO AMOUNTS OF ROOM.
 *
 * The full-width hero has the whole container to play with and gets a real
 * increase: 83px at a 1455px viewport against the 70px it inherited before.
 *
 * The split hero cannot have that. Its copy column measures 557px at the
 * same viewport, and a fifteen-character line — "a point of view", which is
 * the shipped default — needs roughly 0.48em per character, so anything past
 * about 77px wraps it onto a third line. A headline that wraps somewhere the
 * author did not choose looks worse than a smaller one. 71px is what fits
 * with room to spare, and it is still a shade larger than the old inherited
 * size.
 *
 * Anyone who wants more than this now has the base font size control, which
 * scales the whole page and actually works as of 22.0.0.
 */
.u-volume-title {
	margin: 0 0 22px;
	font-size: clamp(2.9rem, 6.2vw, 5.2rem);
	line-height: 1.02;
	letter-spacing: -0.032em;
	max-width: 15ch;
}

.u-volume.has-split .u-volume-title {
	font-size: clamp(2.5rem, 4.9vw, 4.5rem);
}

.u-volume-lead {
	max-width: 46ch;
	margin: 0;
	color: var(--u-muted);
	font-size: clamp(1.12rem, 1.6vw, 1.36rem);
	line-height: 1.5;
}

/* Narrower than the section break, because the two columns run out of
   measure before the section runs out of width. Below this the picture
   goes under the copy rather than beside it. */
@media (max-width: 900px) {
	.u-volume.has-split .u-volume-inner {
		grid-template-columns: 1fr;
		gap: clamp(28px, 6vw, 44px);
	}

	/* Landscape rather than 4:3 once it is full width: a stacked hero image
	   at 4:3 pushes the buttons off a phone screen entirely. */
	.u-volume-image { aspect-ratio: 16 / 10; }
}

.u-volume-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin: 30px 0 0;
}

/* Phones get less height and no pointer swell to chase, so the type gets
   the room instead. */
@media (max-width: 780px) {
	.u-volume { min-height: 0; padding-block: clamp(72px, 16vw, 110px); }
	.u-volume-title { max-width: 100%; }
}

/* Nothing moves, and the canvas is never created — but if a stylesheet is
   cached from before that was true, make sure it cannot fade in either. */
@media (prefers-reduced-motion: reduce) {
	.u-volume-canvas { display: none; }
	.u-volume.is-live .u-volume-canvas { opacity: 0; }
}

@media print {
	.u-volume { min-height: 0; padding-block: 24px; }
	.u-volume-canvas, .u-volume-still { display: none; }
}

.u-band {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: clamp(28px, 5vw, 72px);
	align-items: center;
}

/* No picture: one centred column, which is a good way to break up a run. */
.u-band.is-textonly {
	grid-template-columns: minmax(0, 68ch);
	justify-content: center;
	text-align: center;
}

/* A service tile whose heading links to its own page.
   ------------------------------------------------------------------------
   The anchor wraps the heading only, so the accessible name of the link is
   the service name rather than the name plus its whole description. The
   pseudo-element is what makes the rest of the card clickable, and it sits
   above the tile but below anything else interactive in it. */
.u-tile-link {
	color: inherit;
	text-decoration: none;
}

.u-tile-link::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
	z-index: 1;
}

.u-tile-link:hover { color: var(--u-primary); }

/* The card needs to be the positioning context or the stretched link covers
   the page instead of the card. */
.u-tile { position: relative; }

/* Focus has to be visible on the card, not on a zero-height heading. */
.u-tile:focus-within {
	outline: 2px solid var(--u-primary);
	outline-offset: 3px;
}

/* ==========================================================================
   Guarantee
   ========================================================================== */

.u-guarantee {
	display: grid;
	gap: clamp(24px, 4vw, 56px);
	padding: clamp(28px, 4vw, 52px);
	border-radius: var(--u-radius-lg);
	border: 1px solid var(--u-border);
}

@media (min-width: 860px) {
	.u-guarantee { grid-template-columns: 1.15fr 1fr; align-items: center; }
}

.u-guarantee-title {
	font-size: clamp(1.5rem, 2.8vw, 2.2rem);
	letter-spacing: -0.024em;
	margin: 0 0 14px;
}

.u-guarantee-text { margin: 0; color: var(--u-muted); max-width: 56ch; }

.u-guarantee-period {
	margin: 14px 0 0;
	font-weight: 600;
	color: var(--u-text);
}

.u-guarantee-points {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 12px;
}

.u-guarantee-points li {
	position: relative;
	padding-left: 28px;
	color: var(--u-text);
}

.u-guarantee-points li::before {
	content: "";
	position: absolute;
	left: 4px;
	top: 0.5em;
	width: 10px;
	height: 6px;
	border-left: 2px solid var(--u-primary);
	border-bottom: 2px solid var(--u-primary);
	transform: rotate(-45deg);
}

/* A field that has been marked invalid has to LOOK invalid too — colour
   alone is not enough, so the border weight changes with it for anyone who
   cannot distinguish the red. */
.u-audit-input[aria-invalid="true"],
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] {
	border-color: #b32d2e;
	box-shadow: 0 0 0 2px rgba(179, 45, 46, .18);
}

.u-audit-input[aria-invalid="true"] ~ .u-audit-msg,
.u-audit-msg:not(:empty) { margin-top: 10px; }

/* The message goes red only when it is describing a problem. It is the same
   element for progress and for errors, so the state comes from the field. */
.u-audit-form:has(input[aria-invalid="true"]) .u-audit-msg { color: #b32d2e; }


/* ==========================================================================
   Dark mode
   ==========================================================================

   Almost nothing is needed here, because dark mode swaps the palette at the
   source — every --u-bg, --u-text and --u-accent in the theme is already
   dark by the time any stylesheet reads it. What is left is the handful of
   things a colour swap cannot express.
   ========================================================================== */

/* Shadows are a light-ground device. On a near-black page a drop shadow is
   invisible at best and a grey smear at worst, so panels get a rim of light
   instead — which is how depth reads in the dark. */
body.u-dark {
	--u-shadow-sm: 0 0 0 1px rgba(var(--u-text-rgb), 0.06);
	--u-shadow: 0 0 0 1px rgba(var(--u-text-rgb), 0.08);
	--u-shadow-lg: 0 0 0 1px rgba(var(--u-text-rgb), 0.10), 0 24px 60px rgba(0, 0, 0, 0.5);
}

/* ==========================================================================
   Type specimen services
   ==========================================================================

   A ruled list where each service is set as a specimen: one enormous
   character, the name, a description, and a note in the margin.

   Every rule that styled this originally was scoped to body.u-style-uno21 and
   went when that demo did. Rewritten here against the theme's own variables,
   so it works on any demo and follows dark mode without a style layer having
   to exist for it.
   ========================================================================== */

.u-specimen-list {
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--u-border);
}

.u-specimen-row {
	display: grid;
	grid-template-columns: clamp(80px, 12vw, 180px) minmax(0, 1fr) minmax(0, 200px);
	gap: 24px;
	align-items: baseline;
	padding: clamp(20px, 3vw, 36px) 0;
	border-bottom: 1px solid var(--u-border);
}

/*
   The specimen character is the one place in the theme where the accent is
   decorative rather than actionable, and it earns it — it is the thing being
   specimened. Dark mode lightens the accent for contrast before it gets
   here, so it stays legible on both grounds.
*/
.u-specimen-mark {
	font-family: var(--u-font-heading);
	font-size: clamp(3.4rem, 9vw, 7rem);
	font-weight: 700;
	line-height: 0.8;
	letter-spacing: -0.06em;
	color: var(--u-accent);
	align-self: start;
}

.u-specimen-title {
	margin: 0 0 8px;
	font-size: clamp(1.4rem, 3vw, 2.2rem);
	font-weight: 700;
	letter-spacing: -0.035em;
	line-height: 1.05;
}

.u-specimen-text {
	margin: 0;
	color: var(--u-muted);
	font-size: 0.96rem;
	line-height: 1.55;
	max-width: 56ch;
}

.u-specimen-meta {
	font-family: var(--u-font-body);
	font-size: 0.72rem;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--u-muted);
	text-align: right;
	white-space: nowrap;
}

@media (max-width: 900px) {
	/* The margin note moves under the description rather than being squeezed
	   into a column too narrow to hold "from €1,290 · 6–10 weeks". */
	.u-specimen-row { grid-template-columns: clamp(64px, 14vw, 120px) minmax(0, 1fr); }
	.u-specimen-meta { grid-column: 2; text-align: left; margin-top: 8px; white-space: normal; }
}

@media (max-width: 560px) {
	.u-specimen-row { grid-template-columns: 52px minmax(0, 1fr); gap: 16px; }
}
