/*
Theme Name:        Valera Erd- und Tiefbau
Theme URI:         https://valera-tiefbau.de
Description:       Individuelles Theme für die Website der Valera Erd- und Tiefbau. Es bündelt das Erscheinungsbild der Marke – Farben, Typografie, Raster und Abstände – und liefert die verwendeten Schriften lokal vom eigenen Server aus, ohne externe Anbieter. Als untergeordnetes Theme baut es auf Hello Elementor auf, sodass Aktualisierungen des übergeordneten Themes keine Anpassungen überschreiben.
Author:            waterproof.agency
Author URI:        https://waterproof.agency
Template:          hello-elementor
Version:           1.0.0
Requires at least: 6.0
Tested up to:      7.0
Requires PHP:      8.0
License:           GNU General Public License v2 or later
License URI:       https://www.gnu.org/licenses/gpl-2.0.html
Text Domain:       valera
Tags:              elementor, custom-colors, custom-logo, custom-menu, editor-style, translation-ready
*/

/*
 * Hand-written rules only. Everything derived from the design lives in
 * assets/tokens.css, which is GENERATED from extract/design-data.json.
 *
 * Before adding a rule here, check whether the Elementor kit can express it.
 * If it can, it belongs in the kit, not in this file — a value in two places
 * is a value that will disagree with itself.
 */

/*
 * Sticky footer.
 *
 * Hello Elementor leaves `body` as `display:block` with no min-height, so on
 * any page shorter than the viewport the footer stops directly under the
 * content and the rest of the screen is blank. Measured on the proof page:
 * body min-height 0, document 1779px against a 1000px viewport — fine there,
 * wrong the moment a page is short (a thank-you page, a 404, an early draft).
 *
 * `100dvh` rather than `100vh`: on mobile browsers the toolbars make `vh`
 * taller than the visible area, which pushes the footer below the fold and
 * creates a scrollbar on a page that should not have one.
 */
body {
	display: flex;
	flex-direction: column;
	min-height: 100dvh;
}

body > .site-main,
body > main {
	flex: 1 0 auto;
}

body > .site-header,
body > .site-footer {
	flex: 0 0 auto;
}

/*
 * Admin bar + sticky footer.
 *
 * When an editor is logged in, WordPress prints `html { margin-top: 32px
 * !important }` inline. A body at `min-height: 100dvh` is then 32px taller
 * than the window, so a short page grows a scrollbar and the admin bar sits
 * over the footer — the long-standing overlap.
 *
 * Subtract the bar's own height rather than a hardcoded number: WordPress
 * declares `--wp-admin--admin-bar--height: 32px` on `html` and redefines it to
 * 46px inside `@media (max-width: 782px)` (wp-includes/css/admin-bar.css lines
 * 2 and 765, read on this install). One rule therefore covers desktop and
 * mobile, and keeps covering them if WordPress changes the number.
 *
 * Below 600px the bar becomes `position: absolute` and scrolls away, but the
 * reserved margin stays, so subtracting is still the correct behaviour.
 */
body.admin-bar {
	min-height: calc(100dvh - var(--wp-admin--admin-bar--height, 32px));
}

/*
 * Anything pinned to the top of the viewport has to clear the bar too, or it
 * hides underneath it. The design's header is sticky, so this is here before
 * the header exists rather than after somebody notices.
 */
body.admin-bar .valera-sticky-top {
	top: var(--wp-admin--admin-bar--height, 32px);
}

/*
 * ...and BELOW 601px it has to stop clearing it, because there the bar is not
 * there to clear.
 *
 * WordPress switches the admin bar to `position: absolute` under 601px, so it
 * scrolls away with the document. Elementor Pro's sticky does not know that: it
 * offsets the sticky header by the admin bar's height whatever the bar is doing,
 * and writes that offset inline. Measured on the rendered page: admin bar at
 * -575, sticky header still pinned at `top: 32px`, with 32px of the page (46 on
 * a phone) showing above the header. Logged out there is no admin bar and no
 * offset, which is why this is a defect only the three of us ever see — and why
 * it survived four verification passes.
 *
 * `menu.js` measures the admin bar's own bottom edge and publishes it as
 * `--valera-bar-top` under `.valera-adminbar-loose`, so the header follows it
 * continuously instead of stepping when it is half scrolled. The class is the
 * script saying "I am managing this"; without it, Elementor's own offset stands.
 * `!important` because Elementor's is inline and there is no other way past it
 * (D55).
 */
.valera-adminbar-loose .valera-header.elementor-sticky--active {
	top: var(--valera-bar-top, 0px) !important;
}

/*
 * No horizontal overflow, ever.
 *
 * `clip` and not `hidden`: `overflow-x: hidden` on a scroll container silently
 * disables `position: sticky` for every descendant, and the design's header is
 * sticky. `clip` cuts the overflow without creating a scroll container, so the
 * sticky header keeps working.
 */
html {
	overflow-x: clip;
}

body {
	overflow-x: clip;
	max-width: 100%;
}

/*
 * Flex and grid children default to `min-width: auto`, which refuses to shrink
 * below their content. One long word is then enough to push a column past the
 * viewport. German makes this routine rather than rare — "Hangabfangungs-
 * arbeiten", "Grundstücksentwässerung" — so the break is allowed rather than
 * left to chance at 360px.
 */
.e-con > .e-con-inner > *,
.elementor-widget {
	min-width: 0;
}

/*
 * German breaks long, and the page has to break with it.
 *
 * `Grundstücksentwässerung`, `Hangabfangungsarbeiten`,
 * `Böschungsabfangung` — at 360px these are wider than the column they sit in,
 * and a compound that does not break either overflows or forces a horizontal
 * scrollbar. Two rules, and both are needed:
 *
 *   `hyphens: auto` breaks at the points the language allows, which is why the
 *   document's `lang="de"` matters — the browser picks its German dictionary
 *   from it, and with `lang="en"` it would hyphenate German by English rules or
 *   not at all.
 *
 *   `overflow-wrap: break-word` is the backstop for what has no hyphenation
 *   point at all: an e-mail address, a URL, a part number.
 */
body {
	hyphens: auto;
	overflow-wrap: break-word;
	/*
	 * And a floor, because `hyphens: auto` is greedy: it breaks any word it can
	 * at the end of any line, whether or not the whole word would have fitted on
	 * the next one. On the CTA at 360 that produced „Ihr Projekt ver-dient" and
	 * „und er-stellen" — ordinary eight- and nine-letter words cut in half, which
	 * reads as a fault rather than as typesetting.
	 *
	 * The number counts CHARACTERS, and Chrome counts the word's trailing
	 * punctuation among them. Measured 2026-08-19, with `hyphens: manual` as the
	 * control and `overflow-wrap` off so nothing else could break the word:
	 * at `10 4 4` „Setzungen" (9) stays whole and „Setzungen," (9 + comma)
	 * breaks. Both render on /leistungen/stuetzmauer-hangsicherung/, and the
	 * comma is what tipped it. So the effective floor for a word at the end of a
	 * sentence or a clause is one letter lower than the number says.
	 *
	 * Twelve is where the two cases actually separate once that is accounted for.
	 * Below it are the words German writes whole — verdient, Struktur, Erfahrung,
	 * Setzungen, Straßenbau, Sicherheit, Unternehmen — including the ones a
	 * comma or a full stop follows. At and above it are the compounds this rule
	 * exists for, and there is a wide gap rather than a boundary: the shortest of
	 * them in this site's copy is fifteen (Ansprechpartner, Wasserleitungen) and
	 * the ones that genuinely do not fit a 320px card are eighteen and up —
	 * Leistungsübersicht, Böschungsabfangung, Grundstücksentwässerung.
	 *
	 * Ten was the first number and it was chosen against letters alone; eleven
	 * was the obvious correction and it is not enough, because it still cuts
	 * „Sicher-heit." and „Straßen-bau," — both of which are in the copy. Four
	 * either side keeps a break from stranding a fragment.
	 *
	 * Where the property is not supported the declaration is dropped and the
	 * behaviour is the greedy one above, which is what the site had.
	 */
	hyphenate-limit-chars: 12 4 4;
}

/*
 * Not in the controls, and not only the hyphens.
 *
 * Navigation labels and button captions are short, chosen strings: a hyphen
 * inside "Leistungen" reads as a fault rather than as typesetting. `break-word`
 * has to come off them too, and that one is not cosmetic — allowing a word to
 * break lowers an element's MIN-CONTENT width, which let the navigation shrink
 * below what its two labels need. Elementor's own list wraps by default, so it
 * stacked them and the bar went from 72 to 118 tall at every desktop width.
 * Measured: a list 187px wide holding 227px of items.
 */
.valera-header,
.valera-menu,
.elementor-button {
	hyphens: manual;
	overflow-wrap: normal;
}

/*
 * And the navigation never wraps, whatever it is fed. The bar is one line by
 * design at every width it exists at — below the breakpoint it is a burger, not
 * a longer list — so a second line is always a fault, never a layout.
 */
.valera-header .e-n-menu-heading {
	--n-menu-heading-wrap: nowrap;
	flex-wrap: nowrap;
}

/*
 * Media never exceeds its box, whatever intrinsic size it was uploaded at.
 *
 * SVG is deliberately NOT in this list. Elementor sizes an inline icon with
 * `.elementor-icon svg { width: 1em; height: 1em }` inside a shrink-to-fit
 * wrapper, so a `max-width: 100%` resolves against a parent whose width depends
 * on the icon — and the pair collapses to zero. Measured on the dropdown icons:
 * they rendered at 0x24, present in the DOM, correct colour, invisible.
 * Content SVGs arrive through the image widget and are covered by `img`.
 */
img,
video,
iframe {
	max-width: 100%;
	height: auto;
}

/* The default header's site title is one unbreakable domain string; it is
   clipped rather than wrapped until the Theme Builder header replaces it, and
   clipping real content is worse than breaking it. */
.site-branding,
.site-title {
	overflow-wrap: anywhere;
}

/*
 * Smooth scrolling to anchors.
 *
 * Two things have to come with it or it makes the page worse:
 *
 * 1. `scroll-padding-top`. The design's header is sticky (1440x72 desktop,
 *    360x47.77 mobile — measured on 211:3125 / 211:3479), so without padding
 *    an anchor lands with its heading hidden underneath the header. The offset
 *    also has to grow by the admin bar when an editor is logged in.
 * 2. `prefers-reduced-motion`. Animated scrolling triggers nausea for people
 *    with vestibular disorders, and the OS-level setting is how they say so.
 *    Smooth scroll without this guard is an accessibility regression, not a
 *    nicety.
 */
:root {
	--valera-header-h: 72px;

	/*
	 * Motion. One named value per job, and no duration literal anywhere else.
	 *
	 * These are NOT in tokens.css, and that is deliberate: tokens.css carries
	 * only what was measured out of the design file, and the design file holds
	 * zero timing and zero easing data — 0 of 29 prototype/transition fields
	 * across all 18,869 nodes. Motion is ours, so it lives with the rest of our
	 * judgement, here.
	 *
	 * Why a scale and not one number. Serhii's requirement, 2026-08-19, is that
	 * everything which changes an image looks like the same animation. One value
	 * for the whole site would serve that badly: 150ms on a hover colour and a
	 * long fade on a full-bleed photograph are different jobs, and forcing them
	 * equal makes the small things feel sluggish. Unity comes from a named scale
	 * with exactly one value per job.
	 *
	 * Before this existed the site carried FOUR durations — 150, 200, 240 and
	 * 600 — and three easings, authored across two files by four build steps
	 * with nothing making them agree.
	 *
	 *   ui      hover, focus, a colour answering the pointer
	 *   state   something opening, compacting, rotating, sliding into place
	 *   media   an image or a whole content state replacing another
	 *
	 * `media` was 600ms while the hero cross-faded two layers at once — a long
	 * fade to soften a dip that should not have been there. With the outgoing
	 * photograph held opaque underneath, one layer animates and 400ms is enough.
	 */
	--valera-motion-ui: 150ms;
	--valera-motion-state: 200ms;
	--valera-motion-media: 400ms;
	--valera-motion-ease: ease-out;

	/*
	 * THE FOCUS RING — one geometry for the whole site, owned by
	 * build/lib/focus.js, which gates these five lines against its own numbers.
	 *
	 * Ours, like the motion scale: the design draws no focus state anywhere, 0
	 * of 29 interaction fields across all 18,869 nodes. Which is exactly why it
	 * needed an owner — nothing in the source file could contradict a seventh
	 * invention, so six build steps each invented one. Measured on the rendered
	 * page on 2026-08-19 they used two offsets, three colours, and a radius on
	 * two of seven; the button atom, on ten placements, had no ring of ours at
	 * all and was showing Chrome's blue 1px default.
	 *
	 * The geometry never varies. The COLOUR is the one variable, and it is
	 * contrast rather than taste: a ring has to be seen against the ground it
	 * is drawn on, so a dark context sets --valera-focus-color to the paper and
	 * everything else keeps the ink. A context flips one property and never
	 * restates the shape.
	 *
	 * There is deliberately no radius token. `border-radius` is the ELEMENT's
	 * shape, not the outline's, and the ring now also covers `.elementor-button`
	 * — forcing 2px there would square the design's 100px pill on focus. An
	 * outline follows whatever radius the element already has.
	 */
	--valera-focus-width: 2px;
	--valera-focus-offset: 2px;
	--valera-focus-ink: var(--e-global-color-primary, #171717);
	--valera-focus-paper: var(--e-global-color-whitebg, #F6F6F6);
	--valera-focus-color: var(--valera-focus-ink);
}

/*
 * The bar's own height, as a variable, because the hero is pulled up by it and
 * anchor scrolling is padded by it. The BREAKPOINT is not ours to pick: the bar
 * swaps to its mobile row through Elementor's `hidden-mobile` / `hidden-desktop`
 * classes, which resolve against the kit's `viewport_mobile` — so this query has
 * to end on the same pixel the kit does.
 *
 * It said 767 until 2026-08-20, and by then the kit said 893. D17's "one switch,
 * at 767" had been true when it was written; moving the mobile tier to 893 for
 * the tablet-typography fix moved the bar with it and left this behind. The
 * consequence was visible on an iPad in portrait: the bar was 48px tall while
 * this said 72, and `.v-hero` — which pulls itself up by exactly this value —
 * started 24px above the page and ended 24px short of the fold, showing a strip
 * of the next section under a hero that is supposed to be one screen.
 * `generate-header.js` gates both numbers against the kit now.
 */
@media (max-width: 893px) {
	:root {
		--valera-header-h: 48px;
	}
}

html {
	scroll-padding-top: calc(var(--valera-header-h) + 16px);
}

body.admin-bar {
	scroll-padding-top: calc(var(--valera-header-h) + var(--wp-admin--admin-bar--height, 32px) + 16px);
}

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

/* The logo inherits its colour from context — dark header, white footer — which
   is why the SVG ships in currentColor rather than with a fill baked in. */
.valera-logo {
	display: inline-flex;
	color: currentColor;
}

.valera-logo svg {
	display: block;
	width: auto;
	height: var(--valera-logo-h, 32px);
}

.valera-logo-link {
	display: inline-flex;
	text-decoration: none;
	color: inherit;
}

/* ==========================================================================
   Header — the sticky bar and its two states
   ==========================================================================

   The design draws the header twice and the difference is the point:
   `211:1495` on the homepage is `#FEFEFE` at 70% paint opacity, sitting inside
   the hero over a photograph, with a 4px background blur and NO stroke paint;
   `211:3125` on Leistungsübersicht — the one whose layer is named "sticky
   header" — is solid `#F6F6F6` with a 1px `#171717` rule. Four of the five
   mobile headers repeat the translucent pair, the fifth the solid one. Those
   are not two headers. They are one header in two states (02-decisions D9).

   What is ours, and is labelled as ours: the movement between them. There is
   no timing, easing or prototype data anywhere in the file — zero occurrences
   of all 29 prototype fields across 18,869 nodes — so the fade, the 200ms and
   the shrink from 72 to 64 are choices, not measurements.

   Elementor Pro's sticky adds `.elementor-sticky--effects` once the page has
   scrolled past `sticky_effects_offset`. That is the supported hook for a
   changed sticky state, so it is used instead of a scroll listener of our own.

   REVISED 2026-08-18, Serhii's call, and it goes AGAINST two measurements —
   see 02-decisions D27:

     * the glass no longer turns solid on scroll. The design fills the scrolled
       header `#F6F6F6` (`211:3125`), but that page has no photograph behind its
       header, and a sticky bar that stops being glass the moment it starts
       overlapping content is the opposite of what the effect is for.
     * the 1px `#171717` rule is gone. It IS drawn — `211:3125` carries a solid
       `#171717` stroke and so does the mobile header — but as a hard black line
       under a light bar it reads as a seam. A soft shadow separates the header
       from the content underneath instead.

   The shadow is entirely ours and has nothing to reproduce: the file contains
   ZERO `DROP_SHADOW` and `INNER_SHADOW` nodes across all 18,869. It is built
   from the brand black at low alpha rather than from pure black, so it tints
   with the palette instead of greying it.

   The height still shrinks 72 -> 64, which was measured to need the padding to
   come with it: 48px buttons plus 12px of padding cannot fit in 64.
*/

.valera-header {
	/* The bar's FILL is a container setting, so the client can repaint it in
	   the editor. Its frost is on `::after` below — NOT on the bar itself,
	   which it used to be, as the `.valera-blur-4` class the tokens emit. */

	/* 76x32 desktop, 56.78x23.77 mobile — measured on 211:3127 / 211:3481.
	   The variable is the one the logo shortcode already reads; no second one. */
	--valera-logo-h: 32px;

	/* So the ::after below sits between the bar's fill and its content on a
	   plain z-index. `position: fixed` already isolates on the live page —
	   Pro's sticky pins the bar from the first paint — this covers wherever
	   it renders unpinned, the editor canvas included. */
	isolation: isolate;

	/* The bar paints ABOVE the hero from the FIRST frame, not from the first
	   script. Pro's sticky pins the bar with `position: fixed` only when its
	   JS initialises; until then the bar is an ordinary in-flow block, and the
	   hero — a later sibling pulled up under it by its own -72px margin
	   (sections.css, D57) — painted straight over it: measured with the sticky
	   state stripped, `elementFromPoint` at the logo, the nav and mid-bar all
	   returned the hero. So on every reload the header vanished until the
	   scripts landed, which Safari's pull-to-refresh puts on display.
	   99 is not a choice: it is the exact value Pro's own
	   `.elementor-sticky--active` rule pins with (`var(--z-index, 99)`), so
	   the flow state and the pinned state agree and nothing reorders at the
	   moment of pinning. The mobile overlay stays above at its 1000, and it
	   starts below the bar anyway. */
	z-index: 99;
}

/*
 * The bar's frost, on a pseudo-element rather than on the bar itself.
 *
 * An element with `backdrop-filter` is a BACKDROP ROOT: nothing inside it can
 * frost the page any more — a descendant's own backdrop-filter samples only
 * what the bar itself paints, which under the dropdown hanging below the bar
 * is nothing at all. Measured on the dropdown: it carried blur(20) and
 * rendered dead flat, the hero's own copy reading through its 0.7 fill
 * untouched. With the blur moved to `::after` (inset 0, behind the content,
 * same measured r=4 token) the bar looks exactly the same, stops being a
 * backdrop root, and the dropdown's glass works (D56).
 *
 * `::after` and not `::before`, because Elementor owns `.e-con::before` for
 * its background overlay — the footer already paid for that lesson.
 * The mobile sheet never had the problem: it is a second root OUTSIDE the bar
 * (D28), which also means the open-state rule further down that puts blur(20)
 * back on the bar element itself costs nothing — while the sheet is open
 * there is no dropdown, and the sheet is not inside the bar.
 */
.valera-header::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	backdrop-filter: blur(var(--valera-blur-4));
	-webkit-backdrop-filter: blur(var(--valera-blur-4));
}

/*
 * The side gutter RAMPS between the two artboards instead of stepping at a
 * breakpoint.
 *
 * The design gives exactly two points — 64 at 1440 (211:3125) and 20 at 360
 * (211:3479) — and nothing between them, so the shape of the curve is ours.
 * Elementor can only step, and measured, the step behaved badly: from 1440 down
 * to 1025 the gutter stayed frozen at 64 while the content squeezed from 1312 to
 * 897, and at the breakpoint the content jumped BACK UP to 920 because the
 * gutter suddenly halved. 415px of content given up to hold 128px of empty
 * margin, then a jump backwards.
 *
 * The calc names both measured anchors literally rather than a precomputed
 * slope, so the numbers can be read against the design without doing algebra.
 *
 * Three classes plus a type selector, for the same reason as the block below:
 * Elementor writes `--padding-left` at
 * `.elementor-<doc> .elementor-element.elementor-element-<id>` — three classes.
 * A one-class rule loses silently, and did: the gutter sat at 64 at every width
 * including 360.
 *
 * The footer is on the same ramp and on the same two anchors, so it is on the
 * same rule. Its own artboards give 64 at 1440 (211:1763) and 20 at 360
 * (211:2305) — the same pair, measured again.
 *
 * SO IS THE MOBILE SHEET, and it has to be: it hangs off the bar, and anything
 * in it that does not start where the logo starts reads as a crooked column.
 * Its own padding is the measured 20 — right at 360 and nowhere else, because
 * the bar's is on the ramp: measured at 596, the logo sat at 29.61 and every
 * row in the sheet at 20. One rule for all three, so the line cannot drift
 * apart again (D55).
 */
body .valera-header.e-con.e-con-boxed,
body .valera-footer.e-con.e-con-boxed,
body .valera-menu__panel.e-con.e-con-full {
	--padding-left: clamp(20px, calc(20px + (100vw - 360px) * 44 / 1080), 64px);
	--padding-right: clamp(20px, calc(20px + (100vw - 360px) * 44 / 1080), 64px);
}

body .valera-header.elementor-sticky--effects.e-con {
	box-shadow:
		0 1px 0 rgba(23, 23, 23, 0.05),
		0 10px 30px -12px rgba(23, 23, 23, 0.22);
}

/*
 * ...except while the menu is open. Then the bar is not a strip floating over
 * the page, it is the top of one surface that runs down the screen, and its
 * separating shadow would fall ON the sheet — drawing exactly the seam the
 * shared glass exists to remove. The sheet's own shadow goes on doing the
 * separating, at the bottom, where the menu actually ends (D50).
 */
.valera-menu-open .valera-header.elementor-sticky--effects.e-con {
	box-shadow: none;
}

/*
 * ...and while it is open the bar frosts as hard as the sheet does.
 *
 * The two carry the same fill and the sheet carries a heavier blur (r=20
 * against the bar's measured r=4, D50), which is invisible over a plain page
 * and obvious over a photograph: the bar showed the picture almost sharply
 * while the sheet hanging off it showed a smooth wash, and the join between
 * them read as a line. Both radii are the design's own and both come from
 * tokens.css, so this states no new number — only which of the two the bar
 * uses while it is the top of the menu rather than a strip over the page.
 *
 * Deliberately NOT transitioned: the sheet appears at once, so a bar that
 * eased into the same frost over 200ms would be a seam for 200ms.
 */
.valera-menu-open .valera-header {
	backdrop-filter: blur(var(--valera-blur-20));
	-webkit-backdrop-filter: blur(var(--valera-blur-20));
}

/*
 * The 72 -> 64 shrink is a DESKTOP behaviour and has to say so.
 * Unscoped, it set --min-height: 64 at every width — so the mobile bar, which
 * is 48 at rest, GREW to 64 the moment the page was scrolled. The measured
 * mobile header is 48 and there is nothing in it to shrink.
 */
/* 894, not 768: the bar is the mobile row until the kit's mobile tier ends, and
   this rule exists precisely so the 48px row is never told to shrink to 64. At
   768 it did exactly the thing it was written to prevent, for the whole
   768…893 band, from the moment the tier moved. Gated in generate-header.js. */
@media (min-width: 894px) {
	body .valera-header.elementor-sticky--effects.e-con {
		/* Elementor drives height and padding through its own variables, so the
		   shrink goes through the same ones rather than around them.
		   8 + 48 + 8 = 64 is what the measured 48px buttons leave room for. */
		--min-height: 64px;
		--padding-top: 8px;
		--padding-bottom: 8px;
	}

	/* The dropdown hangs on the bar's bottom edge (D22): its offset below the
	   item is the bar's vertical padding, and the shrink just changed that.
	   The resting 12 comes from the widget control (generate-header.js); this
	   is the same 8 as the padding above, moving with it (D56).
	   On `.e-n-menu`, not on the bar: Elementor writes the control's value at
	   WIDGET level, and a custom property is read from the nearest ancestor
	   that defines it — a definition up on the bar never wins, one below the
	   widget always does. */
	body .valera-header.elementor-sticky--effects .e-n-menu {
		--n-menu-title-distance-from-content: 8px;
	}
}

/* A custom property does not animate, but the property that READS it does:
   `min-height` and `padding` are ordinary computed values, so they transition
   when the variable behind them changes. The vertical padding lives on the
   boxed container's inner wrapper, which is why it is addressed separately. */
@media (prefers-reduced-motion: no-preference) {
	.valera-header > .e-con-inner {
		transition: padding var(--valera-motion-state) var(--valera-motion-ease);
	}
}

@media (prefers-reduced-motion: no-preference) {
	.valera-header {
		/* `backdrop-filter` is deliberately NOT in this list — see the rule that
		   changes it when the menu opens. Between the two SCROLL states it never
		   changes at all: one blur, and since D27 one fill. */
		transition: box-shadow var(--valera-motion-state) var(--valera-motion-ease),
		            min-height var(--valera-motion-state) var(--valera-motion-ease);
	}
}

/* Same tie as `--valera-header-h` above: the mobile bar's logo slot ends where
   the kit's mobile tier ends, not at D17's original 767. */
@media (max-width: 893px) {
	.valera-header {
		--valera-logo-h: 24px;
	}
}

/*
 * A container that hugs its content.
 *
 * An Elementor container is `--width: 100%`, and its Width control offers px,
 * %, em, rem and vw but no `auto` — a `custom`-unit `auto` is accepted, stored
 * and never reaches the CSS. So two containers meant to sit side by side both
 * claim the full row and `space-between` has nothing to distribute.
 *
 * Measured with this rule missing, twice: in the header the actions container
 * ran 355..1376 with its two pills at its left edge, 741px short of where they
 * belong, and at 360 a 48px bar rendered 60 tall; in the footer the columns
 * block took the whole row instead of the five grid columns it is drawn at.
 *
 * ONE class for every place that needs it — the header's two groups, the
 * footer's columns and its legal row. A second class carrying the same single
 * declaration is the duplication this exists to avoid, and there was one: the
 * header's own copy outlived the class it was named for and matched nothing at
 * all. It had also been lost once to a careless block replacement in this
 * stylesheet, and the loss was invisible until the buttons were measured
 * against the right edge — so it is worth keeping in one findable place.
 *
 * Two classes, and the second is `.e-con` rather than a parent, because
 * Elementor itself carries `@media (max-width: 767px) { .e-con.e-flex {
 * --width: 100% } }` — (0,2,0) inside a media query. A one-class rule here is
 * correct above 767 and silently loses below it.
 */
.e-con.valera-hug {
	--width: auto;
}

/*
 * The current page, and the current section, in bold.
 *
 * Serhii's call, 2026-08-18: the design draws no active state for a nav item at
 * all, so telling the visitor where they are is ours. Weight rather than colour,
 * because Inter Semi Bold 600 is a weight the design already uses 155 times
 * while any accent colour here would be invented — and the kit's own default for
 * this state is a light blue that appears nowhere in the design.
 *
 * `.e-current` covers both cases: Elementor sets it on an exact match, and
 * functions.php adds it for a section a visitor is inside.
 *
 * `!important` because the weight is bound to the kit at
 * `.elementor-<doc> .elementor-element.elementor-element-<id> > .e-n-menu > .e-n-menu-wrapper >
 *  .e-n-menu-heading > .e-n-menu-item > .e-n-menu-title > .e-n-menu-title-container > span`
 * — EIGHT classes. There is no readable selector that beats that, and no reason
 * to try: Elementor exposes no control for a current item's weight, so nothing
 * is being put out of the client's reach that they could otherwise have set.
 * Measured before it was added: the rule matched, applied, and lost.
 */
body .valera-header .e-n-menu-title.e-current .e-n-menu-title-text {
	font-weight: 600 !important;
}

/*
 * The chevron flips when the panel opens.
 *
 * Not through the widget's own `style_dropdown_indicator_rotate`: that control
 * rotates the indicator in BOTH states, which was measured on the rendered page
 * as a chevron pointing up at a shut menu. `aria-expanded` is the state
 * Elementor actually maintains on the toggle, so the flip hangs off that — and
 * it then agrees with what a screen reader is told.
 */
.valera-header .e-n-menu-dropdown-icon[aria-expanded="true"] {
	/* Setting the widget's own variable, not a `transform` of our own: Elementor
	   applies `transform: var(--n-menu-dropdown-indicator-rotate)` from a rule of
	   equal specificity that loads after the child theme, so an override here
	   would lose on source order — measured as transform:none with the panel
	   open. Feeding its variable wins without a specificity fight. */
	--n-menu-dropdown-indicator-rotate: rotate(180deg);
}

@media (prefers-reduced-motion: no-preference) {
	.valera-header .e-n-menu-dropdown-icon {
		/*
		 * The SAME trick as the rotate above, and for the same reason — found on
		 * 2026-08-19, when the motion scale made the value worth measuring.
		 *
		 * This block used to declare `transition: transform 200ms ease` directly.
		 * It never applied. Elementor Pro declares
		 *   .elementor-widget-n-menu .e-n-menu-title .e-n-menu-dropdown-icon
		 *     { transition: all var(--n-menu-title-transition) }
		 * at (0,3,0) against our (0,2,0), so the chevron always animated at Pro's
		 * default 0.3s — measured on the rendered page as `all 0.3s ease` while
		 * this file said 200ms. A declaration that exists is not a declaration
		 * that wins, and nothing had ever asked this one whether it did.
		 *
		 * Feeding Pro's own variable needs no specificity at all, and it carries
		 * the easing too: the variable is substituted whole into
		 * `transition: all <value>`, so a duration AND a curve both land.
		 */
		--n-menu-title-transition: var(--valera-motion-state) var(--valera-motion-ease);
	}
}

/*
 * The open mobile menu.
 *
 * Entirely ours: the design contains no mobile navigation panel — none of the
 * five 360px page roots holds a frame named Menu, Nav, Overlay or Drawer, and
 * the burger has no target and no open state (02-decisions D18).
 *
 * It is built from what the design does say: the panel's white, padding and gap
 * are the desktop dropdown's (211:3143), the service rows are the same
 * component, the row rules are the 1px `#949597` at 40% that 204 nodes in the
 * file use, and the two buttons are the header's own at full width — which is
 * how the design itself uses `yellow-button` on mobile.
 */

/*
 * The hand is BACK — D33 is reversed, 2026-08-26.
 *
 * A `cursor: default !important` rule used to sit here covering the whole
 * header, the mobile panel and the footer. It was deliberate: Serhii asked for
 * the arrow on 2026-08-18 after seeing the alternative, because the pointer
 * changed nine times on a single sweep across the bar and no local fix reached
 * zero (the controls occupy the middle 48px of a 72px bar, and 658px of empty
 * space sit between the logo and the navigation).
 *
 * Gaby raised it as feedback on 2026-08-25 — "normalerweise verändert sich ja
 * die Maus, wenn ich über einen Link hovere. Im Header tut es das nicht, was ich
 * persönlich als ein bisschen störend empfinde" — and Serhii took her side.
 *
 * With the rule gone, nothing of ours sets a cursor in the header at all: the
 * browser's own stylesheet gives `<a href>` its pointer, Elementor Pro sets it
 * on `.e-n-menu-title-container.e-link`, and Hello Elementor sets it on
 * `button:not(:disabled)`. Measured after removal: pointer on all 8 desktop
 * controls, the 3 dropdown rows, the burger, the 7 mobile panel rows and the 9
 * footer links. The footer went with the header on purpose — two cursor
 * behaviours on one screen is the inconsistency the footer was added to this
 * rule to prevent in the first place.
 *
 * Do not re-add it without a new decision. See 02-decisions D33 and D331.
 */

/*
 * Links still fill the boxes they sit in. This is no longer about the cursor —
 * it is about the target: a nav link was 66x22 inside a 66x24 row and the logo
 * 76x32 in a 72px bar, and both are now the full height of their row.
 */
.valera-header .e-n-menu-title-container {
	display: flex;
	align-items: center;
}

/*
 * The navigation item is ONE continuous pointer, as tall as the buttons beside
 * it. D331, and the measurements are from the live bar at 1440.
 *
 * Elementor builds the item as TWO controls side by side inside a
 * `.e-n-menu-title` row: an `<a class="e-n-menu-title-container">` to the
 * section and a `<button class="e-n-menu-dropdown-icon">` that opens the
 * dropdown. Before this rule the row was 137.3x48 with `padding: 12px` and
 * `gap: 4px`, and both children were 24px tall, `align-self: center`, the button
 * carrying a further 5px inline start margin. Two consequences, and both are
 * what a reader feels as the pointer misbehaving:
 *
 *   - a 9px strip of ARROW between the word „Leistungen" and its own chevron,
 *     which reads as one control and is two;
 *   - the pointer alive only over y 24..48 of the 48px row, so a sweep at y=20
 *     crossed the navigation with an arrow while the two buttons beside it —
 *     real 48px pills — showed the hand throughout.
 *
 * The row's 48 is 24px of content plus its own 12px block padding, so the
 * padding cannot simply be removed: it IS the height. It moves onto the two
 * children instead, which keeps the row at 48 and makes each child fill it.
 * The 9px goes to the LINK, because it sits immediately after the word and that
 * is what a reader aiming at the word expects to hit. The row's 12px INLINE
 * padding stays — that is the space between two different navigation items, and
 * an arrow there is correct.
 *
 * Specificity is part of the claim. Elementor sets the icon's `align-self` and
 * `height` from `.elementor-widget-n-menu .e-n-menu-title .e-n-menu-dropdown-icon`
 * — (0,3,0) — and the widget's stylesheet is enqueued when the widget renders,
 * i.e. AFTER this file, so an equal-specificity rule here loses the tie. Hence
 * the widget class in every selector below.
 *
 * `height: auto` on the icon is required: its own rule computes a height from
 * `--n-menu-title-font-size * --n-menu-title-line-height`, which would keep it
 * 24px however it is aligned.
 */
.valera-header .elementor-widget-n-menu .e-n-menu-title {
	padding-block: 0;
	gap: 0;
	align-items: stretch;
}

.valera-header .elementor-widget-n-menu .e-n-menu-title .e-n-menu-title-container {
	align-self: stretch;
	padding-block: 12px;
	padding-inline-end: 9px;
}

.valera-header .elementor-widget-n-menu .e-n-menu-title .e-n-menu-dropdown-icon {
	align-self: stretch;
	align-items: center;
	height: auto;
	padding-block: 12px;
	margin-inline-start: 0;
	/*
	 * The chevron carries `border-radius: 100px` over a fully transparent
	 * background and no border, so the rounding is invisible — but hit testing
	 * respects it, and the excluded corners left 3-6px slivers of ARROW at the
	 * top and bottom of the item. Squaring it changes nothing anyone can see and
	 * makes the whole 24x48 hit. The focus ring is square anyway: one ring, no
	 * radius, build/lib/focus.js.
	 */
	border-radius: 0;
}

/* The widget box is stretched to the bar's content row by the template; making
   it a flex container is what lets the link fill it. Both levels, because the
   shortcode widget renders an `.elementor-shortcode` div in between and a
   percentage height does not work here — the boxes' own heights are `auto`, so
   there is nothing for a percentage to resolve against, and the link was
   measured staying 32 tall inside a 48 tall box twice over. */
.valera-header .elementor-widget-shortcode,
.valera-header .elementor-shortcode,
.valera-header .elementor-icon-wrapper,
.valera-menu .elementor-icon-wrapper {
	display: flex;
}

.valera-header .valera-logo-link {
	align-items: center;
}

/*
 * The mobile menu.
 *
 * `.valera-menu` is the overlay and `.valera-menu__panel` the sheet inside it;
 * both are ordinary Elementor containers, so the client edits the menu the way
 * they edit anything else. All this stylesheet adds is where they sit and how
 * they come and go — the open/close itself is `assets/menu.js`, twenty lines
 * with one class and one attribute, after Pro's Off-Canvas proved unreliable
 * in use (02-decisions D30).
 *
 * Closed is the DEFAULT here, not a state the script has to reach: if the
 * script never loads, the page has no menu rather than a page trapped behind an
 * invisible overlay.
 */
.valera-menu {
	position: fixed;
	/*
	 * It hangs UNDER the bar rather than covering it, so the header stays put
	 * and the burger — which is now also the close control — stays where the
	 * thumb left it.
	 *
	 * WHERE the bar ends is MEASURED by `menu.js` at the moment the menu opens,
	 * and arrives here as `--valera-menu-top`. It used to be computed —
	 * `--valera-header-h` plus the admin bar's height when `body.admin-bar` was
	 * present — and that formula is right only while the admin bar is above the
	 * header. Below 601px WordPress stops fixing the admin bar, so once the
	 * page is scrolled it is gone and the sticky bar pins to the top: the sheet
	 * went on reserving 46px for it and opened with a 46px strip of the page
	 * showing between the bar and itself. Logged out, the same formula was
	 * right, which is why it survived (D55).
	 *
	 * `--valera-header-h` stays as the fallback — it is the same variable the
	 * anchor scroll-padding uses, so the two cannot disagree.
	 */
	top: var(--valera-menu-top, var(--valera-header-h));
	inset-inline: 0;
	bottom: 0;
	z-index: 1000;
	/*
	 * The dimming is NOT on this element — see `::after` below. `.e-con.e-flex`
	 * already defaults to column and this container sets no direction, so this
	 * only says out loud what the filler depends on.
	 */
	flex-direction: column;
	/*
	 * `visibility`, not `display`, and no transition.
	 *
	 * The first version slid the panel in with `transform` and it got stuck: a
	 * CSSTransition that never finished held `translateX(100%)` and beat even an
	 * `!important` — a running animation outranks the whole cascade, so the menu
	 * reported itself open while nothing moved. An entrance animation moved the
	 * same failure one step, because `animation-fill-mode: both` holds the FROM
	 * frame for good if it never plays. Open and closed are a state now.
	 *
	 * `visibility: hidden` also takes the panel out of the tab order and out of
	 * the accessibility tree while it is closed, which `opacity` would not.
	 * `display` is deliberately avoided: Elementor drives `display` on every
	 * container through `--display`, and that is a fight with no upside.
	 */
	visibility: hidden;
	pointer-events: none;

	/*
	 * THE OVERLAY is the scroll container, not the sheet.
	 *
	 * Making the sheet itself scroll did not work: with `max-height: 100%` the
	 * browser reported `scrollHeight === clientHeight` and simply clipped — on a
	 * 458px viewport the services and both buttons had height, had no scrollbar,
	 * and were painted nowhere. Scrolling the thing that is already sized to the
	 * screen, and letting the sheet be as tall as it likes, is the ordinary way
	 * round and it behaves.
	 *
	 * The bar is hidden because a scrollbar down the side of a menu reads as a
	 * defect; the ABILITY has to stay, or on a short window the last items and
	 * both buttons are unreachable.
	 */
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: none;
}

.valera-menu::-webkit-scrollbar {
	display: none;
}

/*
 * THE SCRIM SITS UNDER THE SHEET, NOT BEHIND IT.
 *
 * It used to be the overlay's own `background-color`, which is behind
 * everything the overlay contains — the sheet included. That was invisible
 * while the sheet was opaque white and wrong the moment it became glass: 50%
 * black read through 70% white, and over a white page the bar came out #FEFEFE
 * and the sheet #DBDBDB. Two different greys, which is the opposite of one
 * menu (D50).
 *
 * So the dimming is a flex filler after the sheet instead. It takes whatever
 * height is left below and none at all when the sheet is taller than the
 * screen; the overlay is still one target that closes the menu; and nothing is
 * painted behind the glass. `::after` and not `::before`, because Elementor
 * owns `.e-con::before` for its background overlay.
 */
.valera-menu::after {
	content: "";
	flex: 1 0 auto;
	background-color: rgba(23, 23, 23, 0.5);
}

/* Full width, and as tall as its own content — the overlay above scrolls it. */
.valera-menu__panel {
	width: 100%;
	/*
	 * `position` is what keeps the shadow below visible. The scrim is a
	 * pseudo-element and therefore paints AFTER this box in tree order, so it
	 * covered the very shadow that separates the sheet from it — measured as a
	 * hard edge where a soft one had been. A positioned box paints above an
	 * unpositioned one, and that is the whole fix.
	 */
	position: relative;
}

/*
 * ONE shadow for both pieces of floating glass. The sheet and the desktop
 * dropdown are the same surface doing the same job — the menu, standing over
 * the page — so they separate from it with the same soft edge (D50, D56).
 * Neither is measured: the file contains zero shadows (see the header block).
 */
.valera-menu__panel,
.valera-header__panel {
	box-shadow: 0 18px 30px -18px rgba(23, 23, 23, 0.35);
}

.valera-menu-open .valera-menu {
	visibility: visible;
	pointer-events: auto;
}

/* The page behind must not scroll under the open sheet. */
.valera-menu-open,
.valera-menu-open body {
	overflow: hidden;
}

/*
 * Above the breakpoint the menu does not exist at all — the bar carries the
 * navigation itself, and a stray overlay there would swallow every click.
 *
 * THE BREAKPOINT IS THE KIT'S, and this rule is why that matters. The burger
 * is shown and hidden by Elementor's `hidden-desktop` class, which resolves
 * against `viewport_mobile`; this display rule was a hand-written 768. When the
 * tier moved to 893 the two came apart, and across 768…893 — an iPad in
 * portrait — the burger was on screen and the panel it opens was
 * `display: none`. Pressing it did nothing at all, with `aria-expanded` going
 * to true and no error anywhere. Reported 2026-08-20. Gated now.
 */
@media (min-width: 894px) {
	.valera-menu {
		display: none;
	}
}

/*
 * The toggle is one control with two marks: the burger turns into the close
 * mark in place. 40x40 is the tap target Serhii asked for; the burger inside it
 * is the measured 18x12 shape drawn at 24 (D32).
 */
.valera-header .valera-menu__toggle {
	text-decoration: none;
	/* The container Width control is set to 40 and does not reach the CSS — the
	   same silence as everywhere else in this build — and `--width` INHERITS, so
	   the box was picking up the `auto` from the group around it and hugging its
	   24px glyph. Stated here, where it works. */
	--width: 40px;
}

.valera-header .valera-menu__icon-close,
.valera-menu-open .valera-header .valera-menu__icon-open {
	display: none;
}

.valera-menu-open .valera-header .valera-menu__icon-close {
	display: block;
}

/* The last row's rule would draw against the panel's own edge. */
.valera-menu .valera-header__mrow:last-of-type {
	border-bottom-width: 0;
}

/*
 * The logo sits on a text baseline unless told not to.
 *
 * `.valera-logo` is inline-flex, so its box reserves descender space from the
 * inherited 22.4px line-height: the widget measured 38px tall around a 32px
 * mark. Harmless while the bar is centred, wrong the moment the logo is the
 * tallest thing in a row.
 */
.valera-logo,
.valera-logo-link {
	vertical-align: middle;
}

/*
 * The dropdown under "Leistungen".
 *
 * The panel's box is measured (368 wide, 24 padding, 16 gap — 211:3143) and it
 * is an ordinary Elementor container so the client can edit it. Its SURFACE is
 * not the measured one any more: the design draws a white, square, bordered
 * card, and Serhii rejected that rendered — the panel now wears the menu's own
 * glass, the content cards' r=12, no border, and the sheet's shadow (all in
 * the template / the rules above; D56 has the whole argument). What is still
 * ours from before: a row hover, because a dropdown row that does not answer
 * the pointer reads as broken (D20).
 */
.valera-header__panel .valera-header__service {
	transition: background-color var(--valera-motion-ui) var(--valera-motion-ease);
	border-radius: 6px;
}

/*
 * SCOPED TO THE DROPDOWN — the same three rows are reused in the mobile sheet,
 * where hover is a state a finger cannot leave anyway; the sheet keeps only
 * the focus ring (D55). The wash is translucent because the panel is now
 * glass and an opaque fill on glass is a slab — the exact D55 fault, back on
 * the desktop. `#333333` at 6% is the design's own tint-on-light recipe, the
 * one the CTA card is filled with (D49); the 6px is the file's small-surface
 * radius, so the wash does not sit square inside a rounded card.
 */
.valera-header__panel .valera-header__service:hover,
.valera-header__panel .valera-header__service:focus-visible {
	background-color: rgba(51, 51, 51, 0.06);
}

/*
 * THE FOCUS RING — the whole site's, in one declaration block.
 *
 * Neither the kit nor Hello Elementor provides one and the design contains no
 * focus state to measure, so this is ours and is recorded as ours (D163).
 * `:focus-visible` rather than `:focus` keeps it off mouse clicks.
 *
 * The button is in this list, and that is the point of the list. Measured on
 * the rendered page on 2026-08-19, `.elementor-button` — the site's principal
 * control, on ten placements — had no ring of ours anywhere and was drawing
 * Chrome's blue `1px auto` default, because six build steps each ringed the
 * thing they were building and none of them owned the thing they shared.
 *
 * Values come from `:root` above, which build/lib/focus.js gates. Nothing here
 * states a length: a ring authored in place is how the site got seven of them.
 */
.elementor-button:focus-visible,
.valera-header a:focus-visible,
.valera-header button:focus-visible,
.valera-header .e-con:focus-visible,
.valera-menu a:focus-visible,
.valera-menu button:focus-visible,
.valera-menu .e-con:focus-visible {
	outline: var(--valera-focus-width) solid var(--valera-focus-color);
	outline-offset: var(--valera-focus-offset);
}

/* ==========================================================================
   Footer — one footer, every page, both breakpoints
   ==========================================================================

   The design draws it ten times: five desktop `Bauzaunbanner 4` at 1440x679
   and five mobile at 360x663.708. All ten were compared node by node — 66 and
   63 nodes, identical to 0.01px, same copy, same layout — so this is one
   template with responsive controls, not a desktop and a mobile version.

   The layer name lies: it is called `Bauzaunbanner 4`, the variant with the
   emblem switched OFF, and its emblem group carries `visible: true`. It is
   variant 3, the one the client picked (02-decisions D4).

   Everything measurable is on the Elementor container. What is here is the
   part a container control cannot express: the artwork's placement, the
   footer's own proportion, and the right-hand inset that follows the site's
   grid rather than a pixel.
*/

.valera-footer {
	position: relative;
	/*
	 * The artwork bleeds off all four edges in the design and the Figma frame
	 * clips it (`frameMaskDisabled: false` on 211:1763 and 211:2305). `clip`
	 * rather than `hidden` for the same reason as `html` above — it cuts
	 * without creating a scroll container.
	 */
	overflow: clip;

	/*
	 * The artwork is placed in fractions of the footer's own width, so a
	 * container query is what the fractions are measured against. Not `vw`:
	 * that counts the scrollbar, and the artwork would sit a scrollbar's width
	 * off at every breakpoint.
	 *
	 * `--art` is DECLARED here and only ever USED on descendants and on this
	 * element's own pseudo-elements. An element is not its own query container,
	 * so a `cqw` used on `.valera-footer` itself would silently resolve against
	 * the viewport instead — the one place this mechanism reads wrong.
	 *
	 * The `min()` caps it at the drawn artboard: above 1440 the site's own
	 * content stops growing too (D26), and a footer that kept scaling would be
	 * 905px tall on a 1920 screen. Capping here caps the width, the height, the
	 * bleed and the min-height at once, because all five come from this one
	 * value.
	 */
	container-type: inline-size;
	/*
	 * `cqw` is one percent of the query container's CONTENT box, not of the
	 * element — so with the gutter on this container `100cqw` is 1312 at a
	 * 1440 viewport, and every fraction below came out 91.1% of the drawn size.
	 * Adding Elementor's own padding variables back gives the footer's real
	 * width, and keeps giving it if the gutter changes.
	 */
	--art-w: calc(100cqw + var(--padding-left) + var(--padding-right));
	--art: min(var(--art-w), 1440px);
}

/*
 * The footer keeps the proportion it is drawn at: 679 of 1440.
 *
 * It sits on the inner wrapper rather than on the footer itself, for the `cqw`
 * reason above, and it subtracts Elementor's own padding variables rather than
 * the measured 80 and 25 — so if the client changes the padding in the editor
 * the footer still comes out at the drawn shape instead of 105px too tall.
 *
 * With `justify-content: space-between` on the same wrapper this is also what
 * puts the columns at the top and the legal row at the bottom, which is how the
 * design places them: 80 below the top edge and 25 above the bottom one.
 */
.valera-footer > .e-con-inner {
	position: relative;
	z-index: 1;
}

/*
 * The vertical rhythm is a STACK, not a height.
 *
 * The design puts three things one under another — the columns at y 80-241,
 * the wordmark at 312-571 and the legal row at 632-654 — so the space between
 * the first and the last is the artwork's, 391 of 1440. Pinning the footer to
 * 679 and pushing the two ends apart with `space-between` gives the same answer
 * at 1440 and a wrong one everywhere else: measured at 1024, the footer shrank
 * to 483 while 16px text did not, and the wordmark rose straight through the
 * addresses and the social marks. As a gap it follows the content instead, and
 * the drawn 679 falls out of the parts: 80 + 161 + 391 + 22 + 25.
 *
 * Four classes and an element, because Elementor writes `--row-gap` at three.
 */
body .valera-footer.e-con.e-con-boxed > .e-con-inner {
	--row-gap: calc(0.2715278 * var(--art));
}

/*
 * The artwork: two layers, two files, both used at both breakpoints.
 *
 * `valera-lockup.svg` is the giant V and the VALERA wordmark as one piece, and
 * they are one piece in the design too — the wordmark sits at 0.3953 / 0.1590
 * of the V's width on the 1440 artboard and on the 360 one, to five decimals.
 * So the breakpoints differ in the six numbers below, not in the artwork.
 *
 * Both are decoded straight out of the `.fig` by `build/generate-icons.js`,
 * which asserts every outline against the size stored on its node. Nothing was
 * exported from Figma, so no export preset was written into the client's file.
 *
 * Every fraction is `drawn position / artboard width`. `build/generate-footer.js`
 * re-derives all of them from the file and fails if this stylesheet has drifted.
 */
.valera-footer::before,
.valera-footer::after {
	content: "";
	position: absolute;
	z-index: 0;
	pointer-events: none;
	background-repeat: no-repeat;
	background-size: 100% 100%;
	/*
	 * `top: auto` is not tidiness. Elementor's own `.e-con::before` sets
	 * `inset: 0` for the container's background overlay, so a rule that gives
	 * only `bottom` leaves `top: 0` standing and the box is STRETCHED between
	 * the two instead of taking its aspect ratio. Measured exactly that way:
	 * the emblem came out 513px tall against the 826 its own aspect asks for,
	 * while `::after` — which Elementor's rule does not match — was correct.
	 * The two looked like the same rule and behaved differently.
	 *
	 * `height: auto` for the same reason and it is the one that mattered most:
	 * that rule also sets `height: max(100% + …, 100%)`, and an explicit height
	 * beats `aspect-ratio`. Overriding `width` alone is not enough — the emblem
	 * then took its width from here and its height from Elementor, i.e. the
	 * footer's own 679, and rendered squashed to the pixel with no error
	 * anywhere. Both dimensions have to be reclaimed, not one.
	 */
	top: auto;
	right: auto;
	height: auto;
}

/* The emblem, first, because the lockup covers it where they overlap — which
   is the order the design stacks them in, and why the V is opaque here. */
.valera-footer::before {
	background-image: url(assets/img/valera-emblem.svg);
	aspect-ratio: 978.3758 / 906.2703;
	width: calc(0.6794276 * var(--art));
	left: calc(0.2749505 * var(--art));
	bottom: calc(-0.0789122 * var(--art));
}

.valera-footer::after {
	background-image: url(assets/img/valera-lockup.svg);
	aspect-ratio: 1200.8971 / 757.7079;
	width: calc(0.8339563 * var(--art));
	left: calc(-0.0159362 * var(--art));
	bottom: calc(-0.0196946 * var(--art));
}

/*
 * The right-hand inset, and the width of the block that carries the columns.
 *
 * The design places both blocks with their right edge at 1263 of 1440, which is
 * not where anything else on the site ends — every other right-aligned block
 * measures 1374 to 1377. It is not a stray number either: on the artboard's own
 * grid (12 columns, 64 offset, 24 gutter — `design-data.json .grid`, identical
 * on all five desktop roots) the block is five columns plus five gutters wide
 * and its right edge is the end of column eleven. Drawn 557 and 1263 against
 * 556.67 and 1264.67 — 0.33px and 1.67px.
 *
 * So it is written as the grid rather than as the pixel, and it then holds at
 * any container width instead of only at 1440. See 02-decisions D38.
 */
.valera-footer > .e-con-inner {
	--valera-grid-col: calc((100% - 11 * 24px) / 12);
	/*
	 * The inset is the drawn one at the drawn width and eases to nothing at the
	 * breakpoint. It has to: held at a full column all the way down, the block
	 * is pushed left into the giant V — measured, "Seite" sat 21px inside it,
	 * white on `#B1AA4D`, 2.2:1. Below 1440 the artwork takes the room the inset
	 * was only decorating. Same shape as the gutter ramp (D26): a straight line
	 * between the two widths the design gives.
	 */
	--valera-footer-inset: clamp(0px, (var(--art-w) - 1200px) * 111.3333 / 240, 111.3333px);
}

/*
 * The columns block hugs its content, with the grid width as its floor.
 *
 * Sized to the grid alone it shrinks with the container and the German breaks:
 * measured at 1024, "Leistungen" hyphenated to "Leistun-gen", the address split
 * "53902 Bad / Münstereifel" and the e-mail broke across two lines. The floor
 * is what the design draws — five of the twelve columns — and the hug is what
 * keeps the words whole below it.
 */
/* Both blocks share the right edge the design draws them at, so they share the
   rule that puts them there. */
.valera-footer__cols,
.valera-footer__legal {
	margin-inline-end: var(--valera-footer-inset);
}

/* Two classes, because the site-wide `min-width: 0` above — which exists so one
   long German word cannot push a column past the viewport — matches this
   element at `.e-con > .e-con-inner > *` and would otherwise win. Measured: the
   block hugged at 421 where the design draws 557. */
.valera-footer .valera-footer__cols {
	min-width: calc(var(--valera-grid-col) * 5 + 5 * 24px);
}

/*
 * The mobile artboard.
 *
 * The lockup is flush to the bottom edge and to the 20px gutters, and the
 * emblem bleeds off the right and the bottom. The space the lockup needs is
 * held by a margin on the inner wrapper rather than by the container's own
 * padding, again so the fraction is measured against the footer's width and not
 * against the viewport.
 *
 * The 32 is the measured gap between the content block and the lockup
 * (211:2306, 211:2344).
 */
/*
 * Below 1200 the footer takes the mobile artboard's composition.
 *
 * NOT the header's 767 (D17), and not Elementor's 1024 either. Three columns
 * beside the wordmark need about **1170px** once the measured 64 between them
 * is in place — at 1024 the block would sit 67px inside the giant V — and there
 * is no artboard between 1440 and 360 to say otherwise. Elementor has no active
 * breakpoint at 1200, so the whole switch is this one query rather than half
 * here and half in `_tablet` values firing at 1024. One switch, one width, one
 * place. See 02-decisions D43.
 */
@media (max-width: 1200px) {
	/* What Elementor would have written from `padding_tablet`: the stacked
	   artboard's 40 above and nothing below, the lockup being flush to the
	   bottom edge. The sides come from the ramp above, at every width. */
	body .valera-footer.e-con.e-con-boxed {
		--padding-top: 40px;
		--padding-bottom: 0px;
	}

	/* The three columns become two plus one, and the column gap goes with them:
	   in the stacked composition the 45.3125% boundary IS the spacing, and a 64
	   beside it would push the pair past the row and wrap them apart. */
	.valera-footer .e-con.valera-footer__cols {
		--flex-wrap: wrap;
		--column-gap: 0px;
	}

	/* Social left, Kontakt right — the order 211:2323 draws. */
	.valera-footer .e-con.valera-footer__kontakt {
		--flex-direction: row-reverse;
	}

	.valera-footer {
		/* No cap below the breakpoint: the mobile artboard is 360 and this range
		   runs to 1024, so the artwork scales with it as the design's own does. */
		--art: var(--art-w);
	}

	body .valera-footer.e-con.e-con-boxed > .e-con-inner {
		/* the measured stack gap of the mobile artboard (211:2306) */
		--row-gap: 32px;
	}

	.valera-footer > .e-con-inner {
		min-height: 0;
		margin-bottom: calc(32px + 0.5608457 * var(--art));
		/* Both blocks are flush to the gutters here; the desktop inset is a
		   desktop composition. */
		--valera-footer-inset: 0px;
	}

	.e-con.valera-footer__cols {
		--width: 100%;
	}

	.valera-footer .valera-footer__cols {
		min-width: 0;
	}

	/*
	 * The stacked composition is a two-column GRID, not a wrapping row.
	 *
	 * The mobile artboard says so plainly: the right column of all three of its
	 * rows starts at the same x — Adresse (211:2316), Kontakt (211:2327) and the
	 * legal links (211:2333) are every one of them at 145 of the 320 content
	 * box. Reading it as "two hugging columns pushed apart" reproduces the 63px
	 * gap at 360 and nothing sensible above it: measured at 1024, "Seite" and
	 * "Adresse" sat 673px apart with the whole middle of the footer empty.
	 *
	 * As a boundary at 45.3125% it is the drawn layout at 360 and still a
	 * layout at 1024.
	 */
	.valera-footer__cols > .e-con {
		--width: 54.6875%;
	}

	.valera-footer__cols > .e-con:first-child {
		--width: 45.3125%;
	}

	/* The Kontakt column wraps onto its own line and fills it — the two above it
	   share the line, this one has it to itself (211:2323). AFTER the boundary
	   rule above and not before it: both are two classes, so the later one wins,
	   and written first this row came out 175 wide instead of 320. */
	.valera-footer__cols > .e-con.valera-footer__kontakt {
		--width: 100%;
	}

	/* Inside the Kontakt row the order is reversed — social left, Kontakt right
	   — so the boundary lands on the same 145. */
	.valera-footer__kontakt > .e-con:first-child {
		--width: 54.6875%;
	}

	/* The legal links sit at the LEFT edge of the right column here, not at the
	   container's edge: 211:2333 puts them at x 147 of 320 and left-aligns them
	   inside their own block, which is the same 145 boundary as everything else
	   in that column. */
	.e-con.valera-footer__legal {
		--width: 54.6875%;
		justify-content: flex-start;
		/* Measured on 211:2332, the mobile row that carries the legal links: 16
		   below them before the artwork begins. Without it the footer came out
		   650 against the drawn 664. */
		padding-bottom: 16px;
	}

	.valera-footer::after {
		width: calc(0.88889 * var(--art));
		left: calc(0.0555556 * var(--art));
		bottom: 0;
	}

	.valera-footer::before {
		width: calc(0.8083333 * var(--art));
		left: calc(0.2555556 * var(--art));
		bottom: calc(-0.1217934 * var(--art));
	}

	/*
	 * The legal links stack. Elementor's `view` control is not responsive, so
	 * the inline list is turned upright here rather than built a second time as
	 * a vertical list that is hidden on the desktop.
	 */
	.valera-footer__legal .elementor-inline-items {
		flex-direction: column;
		gap: 4px;
	}

	.valera-footer__legal .elementor-inline-item {
		margin: 0;
	}
}

/*
 * Links look like links when the pointer is on them.
 *
 * The design draws no hover state anywhere — zero prototype, transition and
 * easing fields across all 18,869 nodes — so this is ours and is recorded as
 * ours (D20). An underline rather than a colour, because the footer has exactly
 * one ink and a second one would be invented.
 */
.valera-footer a {
	text-decoration: none;
}

.valera-footer a:hover,
.valera-footer a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 3px;
}

@media (prefers-reduced-motion: no-preference) {
	.valera-footer .elementor-social-icon svg {
		transition: opacity var(--valera-motion-ui) var(--valera-motion-ease);
	}
}

.valera-footer .elementor-social-icon[href]:hover svg,
.valera-footer .elementor-social-icon[href]:focus-visible svg {
	opacity: 0.7;
}

/*
 * D348 — Facebook has no profile to point at yet, so its mark carries no href
 * and is not a link. Elementor dresses every social mark as one regardless:
 * `.elementor-social-icon` sets `cursor: pointer` and its own `:hover` drops
 * the opacity, so a mark with nowhere to go still answers the pointer and
 * promises something it cannot do. Both are reclaimed here, keyed on the
 * absence of the attribute — the day a URL is entered in the widget the href
 * appears, these rules stop matching, and nothing has to be undone.
 */
.valera-footer .elementor-social-icon:not([href]) {
	cursor: default;
}

.valera-footer .elementor-social-icon:not([href]):hover {
	opacity: 1;
}

/*
 * The footer's ground is dark, so the ring flips to the paper colour — and
 * flips ONLY the colour. Before this the footer restated the whole ring to
 * change one value, which is how the site ended up with two offsets and three
 * colours saying the same thing. The declaration lives once, in the header's
 * block above; this sets the variable it reads, and any button inside the
 * footer picks the light ring up without a rule of its own.
 */
.valera-footer {
	--valera-focus-color: var(--valera-focus-paper);
}

.valera-footer a:focus-visible {
	outline: var(--valera-focus-width) solid var(--valera-focus-color);
	outline-offset: var(--valera-focus-offset);
}

/* The link fills its row, which is about the TARGET rather than the cursor —
   the defect D29 closed in the header. Scanned across the footer at 2px before
   the arrow was forced: the hand stood over every link and both marks, and the
   arrow only in the gaps the design draws. */
.valera-footer .elementor-icon-list-item > a {
	display: inline-flex;
	align-items: center;
}
