/* Hero section */
.hero-section {
    position: relative;

    /* The theme's own height cascade is kept as `--hero-base` and stays authoritative: the editor's
       height step either uses it ("Standard"), scales it, or replaces it outright. Nothing changes
       for a page nobody has touched, because `--hh-*` then resolves to `--hero-base` itself. */
    --hero-base: 790px;
    height: var(--hh-d, var(--hero-base));
    /* Deliberately NOT clipped. The badge is meant to be pushed past the hero's edge - a negative
       offset is how an editor makes it overlap the section below - and a clip here would cut that
       off instead. What used to need clipping is clipped where it happens now: the text blocks by
       `.title-subtitle` below, the slides by `.hero-slides` themselves. The page keeps its promise
       either way, because `#page-wrapper` carries `overflow-x: clip`, so nothing here can produce a
       horizontal scrollbar. */
}
/* The slide track fills the hero; the text layer and the controls sit above it via z-index. */
.hero-slides {
    position: absolute;
    inset: 0;
    z-index: 1;
    overflow: hidden;
}
.hero-slides .swiper-wrapper,
.hero-slide {
    height: 100%;
}
.hero-section-image {
    position: absolute;
    inset: 0;
    /* `#page-content img { height: auto }` (specificity 1,0,1) outranks any class selector here and
       would let the image keep its own aspect ratio instead of filling the hero box. Same reason
       `.header-slider-item img` carries !important. */
    width: 100% !important;
    height: 100% !important;
    object-fit: cover;

    /* Focus point comes from hero-section.php as custom properties rather than as an inline
       `object-position`, because the narrow breakpoint below has to override it and nothing in a
       stylesheet can outrank an inline declaration. */
    object-position: var(--hero-focus-x, 50%) var(--hero-focus-y, 50%);
    z-index: 1;
}

/* The optional portrait variant is wrapped in <picture>. That wrapper must not become a box of its
   own: the image is absolutely positioned, so an inline wrapper would contribute only a stray line
   box inside the slide. */
.hero-slide picture {
    display: contents;
}
/* Readability overlays are per slide AND per viewport now: the full-area overlay is a
   .hero-slide-overlay div (--ovfull-*), the content-box overlay + text colour are
   --boxbg/--boxrad/--boxpad/--tc on .hero-text, all switched per breakpoint in the media
   blocks. The old page-wide gradient scrim (::after) was removed earlier. */
.hero-wrapper {
    position: relative;
    max-width: 1577px;
    margin: 0 auto;
    padding: 0 16px;
    box-sizing: border-box;
    z-index: 5;
    overflow: visible;
}
/* Text layer = a full-hero overlay. The text block is PLACED by the per-slide
   position settings (data-pos-d/t/m below), not pushed around by a fixed padding,
   so it no longer collides with the image subject and there is no more automatic
   centring at <=920px. pointer-events:none lets clicks fall through to the video /
   slider controls; the text block itself re-enables them. */
/* Text layer = full-hero overlay; the gutters are shared by every hero-text and the
   overlay-bar. --gx centres text to the 1577px content column, --gy-top clears the
   header. Clicks fall through (pointer-events) to the video/controls. */
.title-logo-container {
    position: absolute;
    inset: 0;
    max-width: none;
    margin: 0;
    padding: 0;
    pointer-events: none;
    --gx: max(16px, calc((100% - 1577px) / 2));
    --gy-top: calc(var(--header-nav-height, 100px) + 40px);
    --gy-bot: 40px;

    /* The middle of what the VISITOR sees, not of the box. The header is fixed on top of the hero's
       first --header-nav-height pixels - measured on schichtarbeit: a 1440x100 bar at z-index 9999
       over a hero that starts at y=0, with the hero image only becoming the topmost element at
       y=110. A block centred on the box therefore sits half the header's height too high, 50px on
       that page, which is what "Mitte links" looked wrong by. The top anchor already subtracts the
       header via --gy-top; this is the same correction for the middle one. */
    --gy-mid: calc((var(--header-nav-height, 100px) + 100%) / 2);
}

/* Positioning context for the absolutely-placed text blocks - full hero, no padding
   (so the inset percentages below are measured against the whole hero).
   Clips them too: an extreme spacing value can push a block past the edge. The clip sits here
   rather than on `.hero-section` so that it reaches the text WITHOUT also cutting off the badge,
   which is a sibling and is meant to be able to overhang. */
.title-subtitle {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

/* Each text block is absolutely placed by the per-breakpoint rules. The base is
   middle-left; --tx/--ty carry the centring translate only for the centred axes.
   max-width never exceeds the content column, so it cannot span into the subject.
   A centred block is centred with auto margins, NOT with `left: 50%` and a translate: an absolutely
   positioned box takes its available width from `left`, before any transform runs, so half the hero
   was all `max-width` had to work with - measured, "Inhaltebox-Breite" stopped at 720px of a 1440px
   hero however far the slider went, while the same values reached 288/576/864px on a left anchor. */
.hero-text {
    position: absolute;
    max-width: min(620px, calc(100% - 2 * var(--gx)));
    box-sizing: border-box;

    /* The BLOCK lets clicks through; only what is actually interactive inside it catches them.
       `.hero-slides` is a stacking context at z-index 1 and this layer sits at 5, so nothing inside
       a slide - the play button above all - can ever be raised over this box. Measured with the
       block clickable: at a 1024px window the text covered the play button completely, 0 of 81 probe
       points reached it and a real mouse click opened no video, while the same click with this line
       in place opens it. A headline is not a click target, so it has nothing to lose here. */
    pointer-events: none;
    left: var(--gx);
    top: 50%;
    text-align: left;
    --ty: -50%;
    transform: translate(var(--tx, 0px), var(--ty, 0px));
}

/* The Inhalt-Overlay box (background/radius/padding), the text-colour override and the
   full-area overlay are all PER VIEWPORT now - their rules live in the three media blocks
   below, driven by the --boxbg/--boxrad/--boxpad/--tc/--ovfull custom properties the
   template emits per breakpoint. */

/* Anything a visitor can actually use takes its clicks back. */
.hero-text :is(a, button, input, select, textarea, [tabindex]) {
    pointer-events: auto;
}

/* Subtle legibility aid for white hero text on a busy image - invisible on dark text. */
.hero-text .color-white {
    text-shadow: 0 1px 18px rgb(0 0 0 / 35%);
}

.title-subtitle h1 {
    margin-bottom: 3rem;
}
.hero-text > *:last-child {
    margin-bottom: 0;
}

/* Per-slide text scaling. --ts is a multiplier set per viewport in the media blocks below
   (from the slide's Textgrösse field); default 1 leaves the fluid sizes untouched. */
.hero-text { --ts: 1; }
.hero-text :is(h1, .h1) { font-size: calc(var(--fs-h1, var(--font-size-h1, 62px)) * var(--ts)); line-height: calc(var(--fs-h1, var(--font-size-h1, 62px)) * var(--ts) + 6px); }
.hero-text :is(h2, .h2) { font-size: calc(var(--fs-h2, var(--font-size-h2, 62px)) * var(--ts)); line-height: calc(var(--fs-h2, var(--font-size-h2, 62px)) * var(--ts) + 6px); }
.hero-text :is(h3, .h3) { font-size: calc(var(--fs-h3, var(--font-size-h3, 52px)) * var(--ts)); line-height: calc(var(--fs-h3, var(--font-size-h3, 52px)) * var(--ts) + 6px); }
.hero-text :is(h4, .h4) { font-size: calc(var(--fs-h4, var(--font-size-h4, 42px)) * var(--ts)); line-height: calc(var(--fs-h4, var(--font-size-h4, 42px)) * var(--ts) + 6px); }
.hero-text :is(h5, .h5) { font-size: calc(var(--fs-h5, var(--font-size-h5, 32px)) * var(--ts)); line-height: calc(var(--fs-h5, var(--font-size-h5, 32px)) * var(--ts) + 6px); }
.hero-text :is(h6, .h6) { font-size: calc(var(--fs-h6, var(--font-size-h6, 28px)) * var(--ts)); line-height: calc(var(--fs-h6, var(--font-size-h6, 28px)) * var(--ts) + 10px); }
.hero-text :is(p, li) { font-size: calc(var(--font-size-regular, 20px) * var(--ts)); }

/* Full-area overlay (per slide + viewport): covers the whole image, below the text layer.
   Base = desktop (--ovfull-d); the tablet/mobile media blocks swap it. */
.hero-slide-overlay {
    position: absolute;
    inset: 0;
    z-index: 3;
    pointer-events: none;
    background: var(--ovfull-d, transparent);
}

/* Slider crossfade: only the active slide's text is visible. */
.hero-section.has-slider .hero-text {
    opacity: 0%;
    visibility: hidden;
    transition: opacity var(--hero-transition, 1000ms) ease;
}
.hero-section.has-slider .hero-text.is-active {
    opacity: 100%;
    visibility: visible;
}

/* ---- Per-breakpoint text position + spacing (data-pos-d desktop / -t tablet / -m mobile) ----
   The anchor sets which edge the block hugs; the spacing slider (--sp-h/-v as a 0..1
   fraction) pushes it from that edge toward the centre, reaching the centre line at 100%
   (e.g. bottom + 100% = the block's bottom edge sits at mid-height, i.e. pushed up to the
   middle). Substring selectors: $= horizontal side, ^= vertical band. */
@media (min-width: 1025px) {
    .hero-text[data-pos-d$="left"] { left: calc(var(--gx) + var(--sp-h-d, 0) * (50% - var(--gx))); right: auto; text-align: left; --tx: 0px; }
    .hero-text[data-pos-d$="right"] { right: calc(var(--gx) + var(--sp-h-d, 0) * (50% - var(--gx))); left: auto; text-align: right; --tx: 0px; }
    .hero-text[data-pos-d$="center"] { inset-inline: 0; margin-inline: auto; width: fit-content; text-align: center; --tx: 0px; }
    .hero-text[data-pos-d^="top"] { top: calc(var(--gy-top) + var(--sp-v-d, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --ty: 0px; }
    .hero-text[data-pos-d^="bottom"] { bottom: calc(var(--gy-bot) + var(--sp-v-d, 0) * (50% - var(--gy-bot))); top: auto; --ty: 0px; }
    .hero-text[data-pos-d^="middle"],
    .hero-text[data-pos-d="center"] { top: var(--gy-mid); bottom: auto; --ty: -50%; }
    .hero-text[data-pos-d="hide"] { display: none; }
    .hero-text[data-pos-d="overlay-bar"] { inset: auto 0 0; max-width: none; text-align: left; --tx: 0px; --ty: 0px; padding: clamp(20px, 3vw, 32px) max(16px, calc((100% - 1577px) / 2)); background: var(--boxbg-d, rgb(0 0 0 / 55%)); }
    /* Inhalt-Overlay box + text colour - desktop values. */
    .hero-text { text-shadow: var(--tsh-d, none); background: var(--boxbg-d, transparent); border-radius: var(--boxrad-d, 0); padding: var(--boxpad-d, 0px); max-width: var(--boxw-d, min(620px, calc(100% - 2 * var(--gx)))); --ts: var(--ts-d, 1); }
    .hero-text[data-tc-d] { color: var(--tc-d); }
    .hero-text[data-tc-d] :is(h1, h2, h3, h4, h5, h6, p, li, span, a):not(.button) { color: var(--tc-d); }
    /* Badge: the text block's anchor grid and its exact spacing formula, with one difference - the
       fraction may be NEGATIVE. The anchor names a quadrant, the gutter is its outer edge and the
       centre line its inner one, so +1 lands the badge's near edge on the centre line and -1 pushes
       it a full quadrant the other way, past the hero. Nothing clips it there (see .hero-section),
       which is what lets an editor make the badge overhang the section below.
       The centred axes have no outward direction to speak of, so there the same fraction simply
       reads as right / down, measured against the same quadrant. */
    .logo[data-bpos-d$="left"] { left: calc(var(--gx) + var(--bx-d, 0) * (50% - var(--gx))); right: auto; --btx: 0px; }
    .logo[data-bpos-d$="right"] { right: calc(var(--gx) + var(--bx-d, 0) * (50% - var(--gx))); left: auto; --btx: 0px; }
    .logo[data-bpos-d$="center"] { left: calc(50% + var(--bx-d, 0) * (50% - var(--gx))); right: auto; --btx: -50%; }
    .logo[data-bpos-d^="top"] { top: calc(var(--gy-top) + var(--by-d, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --bty: 0px; }
    .logo[data-bpos-d^="bottom"] { bottom: calc(var(--gy-bot) + var(--by-d, 0) * (50% - var(--gy-bot))); top: auto; --bty: 0px; }
    .logo[data-bpos-d^="middle"],
    .logo[data-bpos-d="center"] { top: calc(var(--gy-mid) + var(--by-d, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --bty: -50%; }
    .logo[data-bpos-d="hide"] { display: none; }
    .logo { --bs: var(--bs-d, 1); }
    /* Navigation: the badge's anchor grid against its OWN gutters (--cx / --cy-*), the same signed
       quadrant fraction, and --ns sizing the buttons. Video-Aufleger: a flow element, so it takes a
       width share of the page and is aligned with auto margins; its two offsets are a share of the
       CARD itself, applied as a transform - that keeps its flow box where it was, so nudging the
       card never moves the content underneath it. */
    .hero-controls[data-npos-d$="left"] { left: calc(var(--cx) + var(--nx-d, 0) * (50% - var(--cx))); right: auto; --ntx: 0px; }
    .hero-controls[data-npos-d$="right"] { right: calc(var(--cx) + var(--nx-d, 0) * (50% - var(--cx))); left: auto; --ntx: 0px; }
    .hero-controls[data-npos-d$="center"] { left: calc(50% + var(--nx-d, 0) * (50% - var(--cx))); right: auto; --ntx: -50%; }
    .hero-controls[data-npos-d^="top"] { top: calc(var(--cy-top) + var(--ny-d, 0) * max(0px, 50% - var(--cy-top))); bottom: auto; --nty: 0px; }
    .hero-controls[data-npos-d^="bottom"] { bottom: calc(var(--cy-bot) + var(--ny-d, 0) * (50% - var(--cy-bot))); top: auto; --nty: 0px; }
    .hero-controls[data-npos-d^="middle"],
    .hero-controls[data-npos-d="center"] { top: calc(var(--cy-mid) + var(--ny-d, 0) * max(0px, 50% - var(--cy-top))); bottom: auto; --nty: -50%; }
    .hero-controls[data-npos-d="hide"] { display: none; }
    .hero-controls { --ns: var(--ns-d, 1); }
    .hero-overlay-video { --or: var(--or-d, 16 / 9); }
    .hero-scrollhint[data-spos-d$="left"] { left: var(--gx); right: auto; --stx: 0px; }
    .hero-scrollhint[data-spos-d$="right"] { right: var(--gx); left: auto; --stx: 0px; }
    .hero-scrollhint[data-spos-d$="center"] { left: 50%; right: auto; --stx: -50%; }
    .hero-scrollhint[data-spos-d^="top"] { top: var(--gy-top); bottom: auto; --sty: 0px; }
    .hero-scrollhint[data-spos-d^="bottom"] { bottom: var(--gy-bot); top: auto; --sty: 0px; }
    .hero-scrollhint[data-spos-d^="middle"],
    .hero-scrollhint[data-spos-d="center"] { top: var(--gy-mid); bottom: auto; --sty: -50%; }
    .hero-scrollhint[data-spos-d="hide"] { display: none; }
    .hero-controls button { filter: var(--nsh-d, none); }
    .hero-overlay-video { width: var(--ow-d, 100%); transform: translate(calc(var(--ox-d, 0) * 100%), calc(var(--oy-d, 0) * 100%)); }
    .hero-overlay-video[data-opos-d="left"] { margin-right: auto; margin-left: 0; }
    .hero-overlay-video[data-opos-d="center"] { margin-right: auto; margin-left: auto; }
    .hero-overlay-video[data-opos-d="right"] { margin-right: 0; margin-left: auto; }
    .hero-overlay-video[data-opos-d="hide"] { display: none; }
}

@media (min-width: 768px) and (max-width: 1024px) {
    .hero-slide-overlay { background: var(--ovfull-t, transparent); }
    .hero-text[data-pos-t$="left"] { left: calc(var(--gx) + var(--sp-h-t, 0) * (50% - var(--gx))); right: auto; text-align: left; --tx: 0px; }
    .hero-text[data-pos-t$="right"] { right: calc(var(--gx) + var(--sp-h-t, 0) * (50% - var(--gx))); left: auto; text-align: right; --tx: 0px; }
    .hero-text[data-pos-t$="center"] { inset-inline: 0; margin-inline: auto; width: fit-content; text-align: center; --tx: 0px; }
    .hero-text[data-pos-t^="top"] { top: calc(var(--gy-top) + var(--sp-v-t, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --ty: 0px; }
    .hero-text[data-pos-t^="bottom"] { bottom: calc(var(--gy-bot) + var(--sp-v-t, 0) * (50% - var(--gy-bot))); top: auto; --ty: 0px; }
    .hero-text[data-pos-t^="middle"],
    .hero-text[data-pos-t="center"] { top: var(--gy-mid); bottom: auto; --ty: -50%; }
    .hero-text[data-pos-t="hide"] { display: none; }
    .hero-text[data-pos-t="overlay-bar"] { inset: auto 0 0; max-width: none; text-align: left; --tx: 0px; --ty: 0px; padding: clamp(20px, 3vw, 32px) max(16px, calc((100% - 1577px) / 2)); background: var(--boxbg-t, rgb(0 0 0 / 55%)); }
    .hero-text { text-shadow: var(--tsh-t, none); background: var(--boxbg-t, transparent); border-radius: var(--boxrad-t, 0); padding: var(--boxpad-t, 0px); max-width: var(--boxw-t, min(620px, calc(100% - 2 * var(--gx)))); --ts: var(--ts-t, 1); }
    .hero-text[data-tc-t] { color: var(--tc-t); }
    .hero-text[data-tc-t] :is(h1, h2, h3, h4, h5, h6, p, li, span, a):not(.button) { color: var(--tc-t); }
    .logo[data-bpos-t$="left"] { left: calc(var(--gx) + var(--bx-t, 0) * (50% - var(--gx))); right: auto; --btx: 0px; }
    .logo[data-bpos-t$="right"] { right: calc(var(--gx) + var(--bx-t, 0) * (50% - var(--gx))); left: auto; --btx: 0px; }
    .logo[data-bpos-t$="center"] { left: calc(50% + var(--bx-t, 0) * (50% - var(--gx))); right: auto; --btx: -50%; }
    .logo[data-bpos-t^="top"] { top: calc(var(--gy-top) + var(--by-t, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --bty: 0px; }
    .logo[data-bpos-t^="bottom"] { bottom: calc(var(--gy-bot) + var(--by-t, 0) * (50% - var(--gy-bot))); top: auto; --bty: 0px; }
    .logo[data-bpos-t^="middle"],
    .logo[data-bpos-t="center"] { top: calc(var(--gy-mid) + var(--by-t, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --bty: -50%; }
    .logo[data-bpos-t="hide"] { display: none; }
    .logo { --bs: var(--bs-t, 1); }
    /* Navigation: the badge's anchor grid against its OWN gutters (--cx / --cy-*), the same signed
       quadrant fraction, and --ns sizing the buttons. Video-Aufleger: a flow element, so it takes a
       width share of the page and is aligned with auto margins; its two offsets are a share of the
       CARD itself, applied as a transform - that keeps its flow box where it was, so nudging the
       card never moves the content underneath it. */
    .hero-controls[data-npos-t$="left"] { left: calc(var(--cx) + var(--nx-t, 0) * (50% - var(--cx))); right: auto; --ntx: 0px; }
    .hero-controls[data-npos-t$="right"] { right: calc(var(--cx) + var(--nx-t, 0) * (50% - var(--cx))); left: auto; --ntx: 0px; }
    .hero-controls[data-npos-t$="center"] { left: calc(50% + var(--nx-t, 0) * (50% - var(--cx))); right: auto; --ntx: -50%; }
    .hero-controls[data-npos-t^="top"] { top: calc(var(--cy-top) + var(--ny-t, 0) * max(0px, 50% - var(--cy-top))); bottom: auto; --nty: 0px; }
    .hero-controls[data-npos-t^="bottom"] { bottom: calc(var(--cy-bot) + var(--ny-t, 0) * (50% - var(--cy-bot))); top: auto; --nty: 0px; }
    .hero-controls[data-npos-t^="middle"],
    .hero-controls[data-npos-t="center"] { top: calc(var(--cy-mid) + var(--ny-t, 0) * max(0px, 50% - var(--cy-top))); bottom: auto; --nty: -50%; }
    .hero-controls[data-npos-t="hide"] { display: none; }
    .hero-controls { --ns: var(--ns-t, 1); }
    .hero-section { height: var(--hh-t, var(--hero-base)); }
    .hero-overlay-video { --or: var(--or-t, 16 / 9); }
    .hero-scrollhint[data-spos-t$="left"] { left: var(--gx); right: auto; --stx: 0px; }
    .hero-scrollhint[data-spos-t$="right"] { right: var(--gx); left: auto; --stx: 0px; }
    .hero-scrollhint[data-spos-t$="center"] { left: 50%; right: auto; --stx: -50%; }
    .hero-scrollhint[data-spos-t^="top"] { top: var(--gy-top); bottom: auto; --sty: 0px; }
    .hero-scrollhint[data-spos-t^="bottom"] { bottom: var(--gy-bot); top: auto; --sty: 0px; }
    .hero-scrollhint[data-spos-t^="middle"],
    .hero-scrollhint[data-spos-t="center"] { top: var(--gy-mid); bottom: auto; --sty: -50%; }
    .hero-scrollhint[data-spos-t="hide"] { display: none; }
    .hero-controls button { filter: var(--nsh-t, none); }
    .hero-overlay-video { width: var(--ow-t, 100%); transform: translate(calc(var(--ox-t, 0) * 100%), calc(var(--oy-t, 0) * 100%)); }
    .hero-overlay-video[data-opos-t="left"] { margin-right: auto; margin-left: 0; }
    .hero-overlay-video[data-opos-t="center"] { margin-right: auto; margin-left: auto; }
    .hero-overlay-video[data-opos-t="right"] { margin-right: 0; margin-left: auto; }
    .hero-overlay-video[data-opos-t="hide"] { display: none; }
}

@media (max-width: 767px) {
    .hero-slide-overlay { background: var(--ovfull-m, transparent); }
    .hero-text[data-pos-m$="left"] { left: calc(var(--gx) + var(--sp-h-m, 0) * (50% - var(--gx))); right: auto; text-align: left; --tx: 0px; }
    .hero-text[data-pos-m$="right"] { right: calc(var(--gx) + var(--sp-h-m, 0) * (50% - var(--gx))); left: auto; text-align: right; --tx: 0px; }
    .hero-text[data-pos-m$="center"] { inset-inline: 0; margin-inline: auto; width: fit-content; text-align: center; --tx: 0px; }
    .hero-text[data-pos-m^="top"] { top: calc(var(--gy-top) + var(--sp-v-m, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --ty: 0px; }
    .hero-text[data-pos-m^="bottom"] { bottom: calc(var(--gy-bot) + var(--sp-v-m, 0) * (50% - var(--gy-bot))); top: auto; --ty: 0px; }
    .hero-text[data-pos-m^="middle"],
    .hero-text[data-pos-m="center"] { top: var(--gy-mid); bottom: auto; --ty: -50%; }
    .hero-text[data-pos-m="hide"] { display: none; }
    .hero-text[data-pos-m="overlay-bar"] { inset: auto 0 0; max-width: none; text-align: left; --tx: 0px; --ty: 0px; padding: clamp(16px, 4vw, 24px) 16px; background: var(--boxbg-m, rgb(0 0 0 / 55%)); }
    .hero-text { text-shadow: var(--tsh-m, none); background: var(--boxbg-m, transparent); border-radius: var(--boxrad-m, 0); padding: var(--boxpad-m, 0px); max-width: var(--boxw-m, min(620px, calc(100% - 2 * var(--gx)))); --ts: var(--ts-m, 1); }
    .hero-text[data-tc-m] { color: var(--tc-m); }
    .hero-text[data-tc-m] :is(h1, h2, h3, h4, h5, h6, p, li, span, a):not(.button) { color: var(--tc-m); }
    .logo[data-bpos-m$="left"] { left: calc(var(--gx) + var(--bx-m, 0) * (50% - var(--gx))); right: auto; --btx: 0px; }
    .logo[data-bpos-m$="right"] { right: calc(var(--gx) + var(--bx-m, 0) * (50% - var(--gx))); left: auto; --btx: 0px; }
    .logo[data-bpos-m$="center"] { left: calc(50% + var(--bx-m, 0) * (50% - var(--gx))); right: auto; --btx: -50%; }
    .logo[data-bpos-m^="top"] { top: calc(var(--gy-top) + var(--by-m, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --bty: 0px; }
    .logo[data-bpos-m^="bottom"] { bottom: calc(var(--gy-bot) + var(--by-m, 0) * (50% - var(--gy-bot))); top: auto; --bty: 0px; }
    .logo[data-bpos-m^="middle"],
    .logo[data-bpos-m="center"] { top: calc(var(--gy-mid) + var(--by-m, 0) * max(0px, 50% - var(--gy-top))); bottom: auto; --bty: -50%; }
    .logo[data-bpos-m="hide"] { display: none; }
    .logo { --bs: var(--bs-m, 1); }
    /* Navigation: the badge's anchor grid against its OWN gutters (--cx / --cy-*), the same signed
       quadrant fraction, and --ns sizing the buttons. Video-Aufleger: a flow element, so it takes a
       width share of the page and is aligned with auto margins; its two offsets are a share of the
       CARD itself, applied as a transform - that keeps its flow box where it was, so nudging the
       card never moves the content underneath it. */
    .hero-controls[data-npos-m$="left"] { left: calc(var(--cx) + var(--nx-m, 0) * (50% - var(--cx))); right: auto; --ntx: 0px; }
    .hero-controls[data-npos-m$="right"] { right: calc(var(--cx) + var(--nx-m, 0) * (50% - var(--cx))); left: auto; --ntx: 0px; }
    .hero-controls[data-npos-m$="center"] { left: calc(50% + var(--nx-m, 0) * (50% - var(--cx))); right: auto; --ntx: -50%; }
    .hero-controls[data-npos-m^="top"] { top: calc(var(--cy-top) + var(--ny-m, 0) * max(0px, 50% - var(--cy-top))); bottom: auto; --nty: 0px; }
    .hero-controls[data-npos-m^="bottom"] { bottom: calc(var(--cy-bot) + var(--ny-m, 0) * (50% - var(--cy-bot))); top: auto; --nty: 0px; }
    .hero-controls[data-npos-m^="middle"],
    .hero-controls[data-npos-m="center"] { top: calc(var(--cy-mid) + var(--ny-m, 0) * max(0px, 50% - var(--cy-top))); bottom: auto; --nty: -50%; }
    .hero-controls[data-npos-m="hide"] { display: none; }
    .hero-controls { --ns: var(--ns-m, 1); }
    .hero-section { height: var(--hh-m, var(--hero-base)); }
    .hero-overlay-video { --or: var(--or-m, 16 / 9); }
    .hero-scrollhint[data-spos-m$="left"] { left: var(--gx); right: auto; --stx: 0px; }
    .hero-scrollhint[data-spos-m$="right"] { right: var(--gx); left: auto; --stx: 0px; }
    .hero-scrollhint[data-spos-m$="center"] { left: 50%; right: auto; --stx: -50%; }
    .hero-scrollhint[data-spos-m^="top"] { top: var(--gy-top); bottom: auto; --sty: 0px; }
    .hero-scrollhint[data-spos-m^="bottom"] { bottom: var(--gy-bot); top: auto; --sty: 0px; }
    .hero-scrollhint[data-spos-m^="middle"],
    .hero-scrollhint[data-spos-m="center"] { top: var(--gy-mid); bottom: auto; --sty: -50%; }
    .hero-scrollhint[data-spos-m="hide"] { display: none; }
    .hero-controls button { filter: var(--nsh-m, none); }
    .hero-overlay-video { width: var(--ow-m, 100%); transform: translate(calc(var(--ox-m, 0) * 100%), calc(var(--oy-m, 0) * 100%)); }
    .hero-overlay-video[data-opos-m="left"] { margin-right: auto; margin-left: 0; }
    .hero-overlay-video[data-opos-m="center"] { margin-right: auto; margin-left: auto; }
    .hero-overlay-video[data-opos-m="right"] { margin-right: 0; margin-left: auto; }
    .hero-overlay-video[data-opos-m="hide"] { display: none; }
}

/* An MP4 slide covers its poster image once it decodes; the image stays underneath as the frame
   shown before that and whenever the video is paused. */
.hero-slide-video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 2;
}

.hero-slide-video.is-idle {
    opacity: 0%;
    pointer-events: none;
}

.hero-slide-video.is-playing {
    opacity: 100%;
    pointer-events: auto;
    z-index: 5;
}

.hero-slide-play {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 50%;
    left: 50%;
    width: 88px;
    height: 88px;

    /* The theme's `button, .button` sets `padding: 20px 67px 16px 68px`; 135px of horizontal
       padding outgrows the 88px width and the circle renders as an oval. */
    padding: 0;
    transform: translate(-50%, -50%);
    z-index: 4;
    border: 0;
    border-radius: 50%;
    background: rgb(0 0 0 / 45%);
    cursor: pointer;
    transition: background 0.2s ease, transform 0.2s ease;
}

.hero-slide-play:is(:hover, :focus-visible) {
    background: rgb(0 0 0 / 70%);
    transform: translate(-50%, -50%) scale(1.06);
}

/* Light variant, for a poster that is dark where the button lands - the dark default disappears
   there. Only the two colours swap; size, position and behaviour are the base rule's. */
.hero-slide-play.is-light {
    background: rgb(255 255 255 / 80%);
}

.hero-slide-play.is-light:is(:hover, :focus-visible) {
    background: rgb(255 255 255 / 95%);
}

.hero-slide-play.is-light::before {
    border-left-color: #1d1d1b;
}

/* button.hidden must actually hide it: the base rule sets display:flex, which outranks normalize's
   [hidden] (equal specificity, later in the cascade). A background video (an autoplay viewport) is
   z-index 2 - below the button - so without this the play button would sit on top of it. */
.hero-slide-play[hidden] { display: none; }

/* Play triangle, drawn rather than shipped as an icon so it inherits the button colour. */
.hero-slide-play::before {
    content: '';
    width: 0;
    height: 0;
    margin-left: 6px;
    border-top: 14px solid transparent;
    border-bottom: 14px solid transparent;
    border-left: 22px solid #fff;
}

/* ---- Video-Aufleger: one page-wide video card laid over the hero's lower edge ----
   The negative margin sits on the INNER element on purpose: a percentage margin resolves against
   the parent's width, so putting it on .hero-overlay-video (whose parent is the full-width page)
   would overlap by a share of the viewport instead of the card. -28.125% is half of the card's own
   height at 16:9 (56.25% of its width), so it overlaps the hero by exactly half. */
.hero-overlay-video {
    margin-bottom: 60px;
}

.hero-overlay-frame {
    position: relative;

    /* The ratio comes from an alias the breakpoint blocks set, not from `--or-d/-t/-m` directly:
       this rule sits BELOW those blocks, so reading the per-viewport property here would let this
       line win over them at equal specificity - measured, the card stayed 16:9 whatever the editor
       picked. The overlap follows the ratio rather than staying at the 16:9 constant: it is half the
       card's own height, which is its width divided by the ratio. */
    margin-top: calc(-50% / var(--or, 16 / 9));
    aspect-ratio: var(--or, 16 / 9);
    border-radius: 6px;
    overflow: hidden;
}

/* Same reason as .hero-slide picture: the wrapper must not become a box of its own. */
.hero-overlay-frame picture {
    display: contents;
}

/* Same !important as .hero-section-image, and for the same reason: `#page-content img { height: auto }`
   (specificity 1,0,1) outranks any class selector here. `inset: 0` alone does NOT size an <img> -
   for an absolutely positioned REPLACED element, `height: auto` resolves from the intrinsic aspect
   ratio, not from the offsets. Measured without it: a 1408x938 poster rendered 939px tall in the
   792px frame, so 147px were cropped by overflow and the editor's focus point did nothing. */
.hero-overlay-still {
    position: absolute;
    inset: 0;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover;
    object-position: var(--hero-focus-x, 50%) var(--hero-focus-y, 50%);
}

@media (max-width: 650px) {
    /* Same breakpoint as the <source media> in hero-section.php - file and focus point switch together. */
    .hero-overlay-still {
        object-position:
            var(--hero-focus-x-mobile, var(--hero-focus-x, 50%))
            var(--hero-focus-y-mobile, var(--hero-focus-y, 50%));
    }
}

.hero-slide-frame {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    z-index: 5;
}
/* Full-width band above the video frame (z-index 6 vs 5): without pointer-events none on the
   wrapper it swallows the YouTube scrub bar, whose hit area crosses the whole hero width. */
/* The navigation is placed by the same anchor grid as the badge, so it is a shrink-to-fit box rather
   than the full-width bar it used to be - a row spanning the hero cannot be anchored to its right
   edge. Its gutters are its OWN (85/48px, and 15/24px below 920px, which is where they have always
   sat) rather than the text column's: matching --gx would have moved the controls of every existing
   site inward by 69px. --ns scales the buttons; every size below is a multiple of it. */
.hero-controls {
    display: flex;
    align-items: center;
    gap: calc(14px * var(--ns, 1));
    position: absolute;
    bottom: 48px;
    left: 85px;
    z-index: 6;
    max-width: none;
    margin: 0;
    padding: 0;
    pointer-events: none;
    transform: translate(var(--ntx, 0px), var(--nty, 0px));
    --cx: 85px;
    --cy-bot: 48px;
    --cy-top: calc(var(--header-nav-height, 100px) + 40px);

    /* Its own copy: the controls are a sibling of the text layer, so they inherit nothing from it. */
    --cy-mid: calc((var(--header-nav-height, 100px) + 100%) / 2);
}
.hero-controls button {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: none;
    color: #fff;
    cursor: pointer;
    pointer-events: auto;
    opacity: 65%;
    transition: opacity 0.2s ease;
}
/* `[hidden]` must win here: normalize's `[hidden] { display: none }` scores (0,1,0) against the
   rule above at (0,1,1), so a hidden control stayed visible - and dead, because the script that
   hides it also stops listening. Measured under `prefers-reduced-motion: reduce`: the pause button
   rendered at 65 % opacity, took the pointer, and a real click changed nothing. */
.hero-controls button[hidden] {
    display: none;
}

.hero-controls button:is(:hover, :focus-visible) {
    opacity: 100%;
}
.hero-controls .hero-prev,
.hero-controls .hero-next {
    width: calc(32px * var(--ns, 1));
    height: calc(32px * var(--ns, 1));
}

/* Chevrons are drawn here, not taken from the theme's icon font. The icomoon CLASS NAMES are
   identical across LEXO themes but map to different glyphs per site - measured: `icon-icon3`/
   `icon-icon5` are arrows on schichtarbeit and render as `0` and `x` on the boilerplate. Drawing
   them removes that coupling and inherits the button colour, same as the play triangle above. */
.hero-controls .hero-prev::before,
.hero-controls .hero-next::before {
    content: '';
    width: calc(11px * var(--ns, 1));
    height: calc(11px * var(--ns, 1));
    border-left: calc(2px * var(--ns, 1)) solid currentColor;
    border-bottom: calc(2px * var(--ns, 1)) solid currentColor;
}

.hero-controls .hero-prev::before {
    transform: rotate(45deg);
    margin-left: 4px;
}

.hero-controls .hero-next::before {
    transform: rotate(-135deg);
    margin-right: 4px;
}
.hero-ring {
    display: block;
    position: relative;
    width: calc(44px * var(--ns, 1));
    height: calc(44px * var(--ns, 1));
}
.hero-ring svg {
    width: calc(44px * var(--ns, 1));
    height: calc(44px * var(--ns, 1));
    transform: rotate(-90deg); /* start the sweep at 12 o'clock */
}
.hero-ring circle {
    fill: none;
    stroke-width: 2;
    /* 2 * pi * r, r = 20 */
    stroke-dasharray: 125.66;
}
.hero-ring-track {
    stroke: rgb(255 255 255 / 30%);
}
.hero-ring-progress {
    stroke: #fff;
    stroke-linecap: round;
    /* --hero-progress runs 0 -> 1 across one interval, fed by Swiper's autoplayTimeLeft. */
    stroke-dashoffset: calc(125.66 * (1 - var(--hero-progress, 0)));
}
.hero-ring-icon {
    position: absolute;
    top: 50%;
    left: 50%;
    width: calc(10px * var(--ns, 1));
    height: calc(12px * var(--ns, 1));
    transform: translate(-50%, -50%);
    /* Two bars = pause. The play triangle replaces them while autoplay is stopped. */
    border-right: calc(3px * var(--ns, 1)) solid currentcolor;
    border-left: calc(3px * var(--ns, 1)) solid currentcolor;
}
.hero-autoplay.is-paused .hero-ring-icon {
    width: 0;
    height: 0;
    border-top: calc(6px * var(--ns, 1)) solid transparent;
    border-right: 0;
    border-bottom: calc(6px * var(--ns, 1)) solid transparent;
    border-left: calc(10px * var(--ns, 1)) solid currentcolor;
    transform: translate(-40%, -50%);
}
@media (prefers-reduced-motion: reduce) {
    .hero-section.has-slider .hero-text {
        transition: none;
    }
}
/* The subtitle is a wysiwyg field, so its content arrives as <p> elements inside the .ts_26 box.
   rte.css sets font-size/line-height on bare `p`, and the class sits on the wrapper - without this
   the paragraph never inherits the 26px and drops to body size. */
.title-subtitle .ts_26 p {
    font-size: inherit;
    line-height: inherit;
    letter-spacing: inherit;
    margin-bottom: 0;
}
.title-subtitle .ts_26 p + p {
    margin-top: 2.5rem;
}
/* The optional logo badge is placed on its own, independent of the text position (the text layer is
   a full-hero absolute grid, so it cannot share a flex row). WHERE it sits comes from the
   per-breakpoint anchor rules in the media blocks above - nothing but the shared behaviour lives
   here, or the base rule would outrank them: `.title-logo-container .logo` and
   `.logo[data-bpos-d^="top"]` carry the same specificity, and this rule sits further down the file. */
.title-logo-container .logo {
    position: absolute;

    /* Sized by its content, never by what is left of the row. A centred anchor puts `left` at
       `50% + offset`, and the available width is what remains to the right of that - at a full
       offset the badge had 16px to live in and shrank to 16 x 25px instead of 161 x 250px (measured
       at 1440 and reproduced through a save). The centring translate only runs after layout, so it
       cannot give the space back. The navigation escapes this only because its flex children carry
       a min-content width; an <img> has none. */
    width: max-content;
    z-index: 1;
    pointer-events: auto;
    transform: translate(var(--btx, 0px), var(--bty, 0px));
}

/* The badge keeps whatever proportions it was uploaded with - portrait, square or landscape - so it
   is bounded by a BOX rather than resized: whichever edge of that box the image reaches first stops
   it. The box is the largest badge the field ever accepted (550x350 was the old upload limit) paired
   with the 250px height cap that has always applied here, so a size of 100 % renders every existing
   badge exactly as before. `min(100%, ...)` keeps it inside the hero once the slider goes past 100 %. */
.hero-logo {
    display: block;
    /* The logo carries intrinsic width/height attributes - without auto both, max-height
       squashes it instead of scaling it. */
    width: auto;
    height: auto;
    max-width: min(100%, calc(550px * var(--bs, 1)));
    max-height: calc(250px * var(--bs, 1));
}

@media (max-width: 1280px) {

    .hero-section {
        --hero-base: 650px;
    }
}

@media (max-width: 920px) {

    .hero-section {
        --hero-base: 550px;
    }

    /* Text position is now editor-controlled per breakpoint (data-pos-t), so the old
       forced column-centre here is gone. Controls stay put, independent of the text. */
    /* The controls' own gutters, narrower here as they have always been. Setting the variables
       rather than the properties keeps every anchor - left, right, top, bottom - in step. */
    .hero-controls {
        --cx: 15px;
        --cy-bot: 24px;
    }
}

@media (max-width: 767px) {
}

@media (max-width: 650px) {

    /* Same breakpoint as the <source media> in hero-section.php - the file and the focus point have
       to switch together. Measured at 390x450: the hero box is 0.87 wide-to-high while a 16:9 image
       covers it at 800px wide, so 51 % of the picture is outside the frame and the desktop focus
       point decides which half survives. Above 650px the box is 1.18 or wider and the landscape
       original still holds up.
       Falls back to the desktop pair, so a slide without a portrait variant is unaffected. */
    .hero-section-image {
        object-position:
            var(--hero-focus-x-mobile, var(--hero-focus-x, 50%))
            var(--hero-focus-y-mobile, var(--hero-focus-y, 50%));
    }
}

@media (max-width: 500px) {

    .hero-section {
        --hero-base: 450px;
    }
}

/* Single Blog */

.no-heroes + .single-blog-container {
    padding-top: 135px;
}

.blog-fc-content .two-columns .column {
    flex: 1;
}

.blog-fc-content .two-columns {
    gap: 30px;
}

.single-blog-container {
    padding-top: 64px;
}

.tax-blog-container {
    padding-top: 140px;
    padding-bottom: 140px;
    gap: 27px;
}

.tax-blog-container #blog-results {
    flex: 1 0 859px;
}

.no-heroes + .blog-wrapper .single-blog-container {
    padding-top: calc(150px + (var(--header-nav-height, 100px) - var(--header-mini-nav-height, 69px)));
}

.blog-wrapper {
    position: relative;
}

.blog-wrapper .blog-background {
    position: absolute;
    top: 0;
    right: 0;
    width: auto;
    height: auto;
    z-index: -1;
    overflow: hidden;
}

/* ---- Swiper fallback ----------------------------------------------------------------------
   Swiper is an external dependency (README). Until it initialises, every slide is a plain block
   in the track: they would stack vertically and the controls would sit there doing nothing. Show
   the first slide only and drop the controls, so a missing library costs the extra slides - not
   the page. Also covers the moments before Swiper boots on a slow connection. */
.hero-slides:not(.swiper-initialized) .hero-slide:not(:first-child) { display: none; }
.hero-slides:not(.swiper-initialized) ~ .hero-controls { display: none; }
/* ---- Scroll hint -------------------------------------------------------------------------------
   A drawn chevron in a ring, on the same visual footing as the play button and the slider arrows -
   no icon font, because the icomoon class names map to different glyphs on different LEXO sites and
   a hint that renders as a stray letter is worse than none.
   Two animations, deliberately separate: `heroHintIn` runs ONCE after the editor's delay and is
   what gives the hint its moment of attention, `heroHintBob` loops forever and is what says
   "there is more below". Both are suppressed under `prefers-reduced-motion`, where the hint simply
   sits there - still clickable, which is the part that matters. */
.hero-scrollhint {
    position: absolute;
    z-index: 6;

    /* Its OWN gutters. The hint is a child of `.hero-section`, so it is a SIBLING of the text layer
       and inherits nothing from it - measured before this: `--gx` and `--gy-bot` read empty at the
       element, which made `bottom` invalid and left the icon pinned to the hero's top edge, 940px
       from where "Unten Mitte" put it. Same values as the text layer's, restated where they are read. */
    --gx: max(16px, calc((100% - 1577px) / 2));
    --gy-top: calc(var(--header-nav-height, 100px) + 40px);
    --gy-bot: 40px;
    --gy-mid: calc((var(--header-nav-height, 100px) + 100%) / 2);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 0;
    border: 2px solid rgb(255 255 255 / 70%);
    border-radius: 50%;
    background: rgb(0 0 0 / 25%);
    color: #fff;
    cursor: pointer;
    opacity: 0%;
    transform: translate(var(--stx, 0px), var(--sty, 0px));
    transition: background 0.2s ease, border-color 0.2s ease;
    animation: heroHintIn 950ms cubic-bezier(0.22, 1.2, 0.36, 1) var(--sh-delay, 1500ms) both;
}

.hero-scrollhint:is(:hover, :focus-visible) {
    border-color: #fff;
    background: rgb(0 0 0 / 45%);
}

/* The chevron. Same construction as the prev/next arrows: two borders of a rotated empty box. */
.hero-scrollhint-arrow {
    width: 12px;
    height: 12px;
    margin-top: -4px;
    border-right: 2px solid currentcolor;
    border-bottom: 2px solid currentcolor;
    transform: rotate(45deg);
    animation: heroHintBob 1.8s ease-in-out var(--sh-delay, 1500ms) infinite;
}

/* Pops in slightly oversized and settles - the overshoot is what reads as "look here" rather than
   as an element that was simply late. The translate is restated because a transform on the element
   replaces the one that positions it. */
/* Ten entrances, one per editor choice. Every frame restates `translate(--stx, --sty)` because a
   transform on this element REPLACES the one that positions it - a keyframe that forgets it yanks
   the icon to the hero's corner for the duration of the animation. */
.hero-scrollhint[data-seffect="fade"] { animation-name: heroHintFade; }
.hero-scrollhint[data-seffect="fade-up"] { animation-name: heroHintFadeUp; }
.hero-scrollhint[data-seffect="fade-down"] { animation-name: heroHintFadeDown; }
.hero-scrollhint[data-seffect="zoom-in"] { animation-name: heroHintZoomIn; }
.hero-scrollhint[data-seffect="zoom-out"] { animation-name: heroHintZoomOut; }
.hero-scrollhint[data-seffect="bounce"] { animation-name: heroHintBounce; }
.hero-scrollhint[data-seffect="slide-up"] { animation-name: heroHintSlideUp; }
.hero-scrollhint[data-seffect="flip"] { animation-name: heroHintFlip; }
.hero-scrollhint[data-seffect="blur"] { animation-name: heroHintBlur; }

@keyframes heroHintFade {
    from { opacity: 0%; }
    to { opacity: 100%; }
}

@keyframes heroHintFadeUp {
    from { opacity: 0%; transform: translate(var(--stx, 0px), calc(var(--sty, 0px) + 24px)); }
    to { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)); }
}

@keyframes heroHintFadeDown {
    from { opacity: 0%; transform: translate(var(--stx, 0px), calc(var(--sty, 0px) - 24px)); }
    to { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)); }
}

@keyframes heroHintZoomIn {
    from { opacity: 0%; transform: translate(var(--stx, 0px), var(--sty, 0px)) scale(0.4); }
    to { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)) scale(1); }
}

@keyframes heroHintZoomOut {
    from { opacity: 0%; transform: translate(var(--stx, 0px), var(--sty, 0px)) scale(1.6); }
    to { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)) scale(1); }
}

@keyframes heroHintBounce {
    0% { opacity: 0%; transform: translate(var(--stx, 0px), calc(var(--sty, 0px) - 30px)); }
    55% { opacity: 100%; transform: translate(var(--stx, 0px), calc(var(--sty, 0px) + 8px)); }
    75% { transform: translate(var(--stx, 0px), calc(var(--sty, 0px) - 4px)); }
    100% { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)); }
}

/* The first frame has to be INVISIBLE, whatever the effect does afterwards: `animation-fill-mode:
   both` applies it during the delay too, so a frame starting at full opacity puts the hint on screen
   from the first paint and the delay does nothing. Measured 500ms into a 1000ms delay: opacity 1
   here against 0 for every other effect. It becomes visible one hundredth in - too fast to read as
   a fade, late enough to respect the delay. */
@keyframes heroHintSlideUp {
    0% { opacity: 0%; transform: translate(var(--stx, 0px), calc(var(--sty, 0px) + 70px)); }
    1% { opacity: 100%; transform: translate(var(--stx, 0px), calc(var(--sty, 0px) + 69px)); }
    100% { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)); }
}

@keyframes heroHintFlip {
    from { opacity: 0%; transform: translate(var(--stx, 0px), var(--sty, 0px)) rotateX(90deg); }
    to { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)) rotateX(0deg); }
}

@keyframes heroHintBlur {
    from { opacity: 0%; filter: blur(10px); }
    to { opacity: 100%; filter: blur(0); }
}

@keyframes heroHintIn {
    from { opacity: 0%; transform: translate(var(--stx, 0px), calc(var(--sty, 0px) + 14px)) scale(0.7); }
    60% { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)) scale(1.12); }
    to { opacity: 100%; transform: translate(var(--stx, 0px), var(--sty, 0px)) scale(1); }
}

@keyframes heroHintBob {
    0%, 60%, 100% { transform: rotate(45deg) translate(0, 0); }
    30% { transform: rotate(45deg) translate(3px, 3px); }
}

@media (prefers-reduced-motion: reduce) {
    .hero-scrollhint { opacity: 100%; animation: none; }
    .hero-scrollhint-arrow { animation: none; }
}

/* ---- Image animation (Ken Burns) --------------------------------------------------------
   Only the hero emits `data-animation-effect` (grepped: no other template does), so these
   rules and their keyframes belong to the module rather than the theme stylesheet.
   The class lands on the IMAGE alone - a play button sits beside it and stays put. */
[data-animation-effect] {
    animation-duration: var(--animation-duration);
    animation-timing-function: linear;
    animation-fill-mode: forwards;
    --image-scale: 1.05;
    --edge-translation: calc(
        (var(--image-scale) - 1) / (var(--image-scale) * 2) * 100%
    );
}

[data-animation-effect="left-to-right"].animate {
    animation-name: leftToRight;
}

[data-animation-effect="right-to-left"].animate {
    animation-name: rightToLeft;
}

[data-animation-effect="top-to-bottom"].animate {
    animation-name: topToBottom;
}

[data-animation-effect="bottom-to-top"].animate {
    animation-name: bottomToTop;
}

[data-animation-effect="zoom-in"].animate {
    animation-name: zoomIn;
}

[data-animation-effect="zoom-out"].animate {
    animation-name: zoomOut;
}

[data-animation-effect="top-left-to-bottom-right"].animate {
    animation-name: topLeftToBottomRight;
}

[data-animation-effect="top-right-to-bottom-left"].animate {
    animation-name: topRightToBottomLeft;
}

[data-animation-effect="bottom-left-to-top-right"].animate {
    animation-name: bottomLeftToTopRight;
}

[data-animation-effect="bottom-right-to-top-left"].animate {
    animation-name: bottomRightToTopLeft;
}

@keyframes leftToRight {
    0% {
        transform: scale(var(--image-scale))
        translate(calc(-1 * var(--edge-translation)), calc(-1 * var(--edge-translation)));
    }
    100% {
        transform: scale(var(--image-scale)) translate(var(--edge-translation), var(--edge-translation));
    }
}

@keyframes rightToLeft {
    0% {
        transform: scale(var(--image-scale)) translateX(var(--edge-translation));
    }
    100% {
        transform: scale(var(--image-scale))
        translateX(calc(-1 * var(--edge-translation)));
    }
}

@keyframes topToBottom {
    0% {
        transform: scale(var(--image-scale))
        translateY(calc(-1 * var(--edge-translation)));
    }
    100% {
        transform: scale(var(--image-scale)) translateY(var(--edge-translation));
    }
}

@keyframes bottomToTop {
    0% {
        transform: scale(var(--image-scale)) translateY(var(--edge-translation));
    }
    100% {
        transform: scale(var(--image-scale))
        translateY(calc(-1 * var(--edge-translation)));
    }
}

@keyframes zoomIn {
    0% {
        transform: scale(1);
    }
    100% {
        transform: scale(var(--image-scale));
    }
}

@keyframes zoomOut {
    0% {
        transform: scale(var(--image-scale));
    }
    100% {
        transform: scale(1);
    }
}

@keyframes topLeftToBottomRight {
    0% {
        transform: scale(var(--image-scale))
        translate(calc(-1 * var(--edge-translation)), calc(-1 * var(--edge-translation)));
    }
    100% {
        transform: scale(var(--image-scale)) translate(var(--edge-translation), var(--edge-translation));
    }
}

@keyframes topRightToBottomLeft {
    0% {
        transform: scale(var(--image-scale)) translate(var(--edge-translation), calc(-1 * var(--edge-translation)));
    }
    100% {
        transform: scale(var(--image-scale)) translate(calc(-1 * var(--edge-translation)), var(--edge-translation));
    }
}

@keyframes bottomLeftToTopRight {
    0% {
        transform: scale(var(--image-scale)) translate(calc(-1 * var(--edge-translation)), var(--edge-translation));
    }
    100% {
        transform: scale(var(--image-scale)) translate(var(--edge-translation), calc(-1 * var(--edge-translation)));
    }
}

@keyframes bottomRightToTopLeft {
    0% {
        transform: scale(var(--image-scale)) translate(var(--edge-translation), var(--edge-translation));
    }
    100% {
        transform: scale(var(--image-scale)) translate(calc(-1 * var(--edge-translation)), calc(-1 * var(--edge-translation)));
    }
}
