/* Fastpage CMS — frontend. Industrial/blueprint look (steel blue, square,
 * hairline objects, transparent line drawings), built entirely on Open Props —
 * system fonts by default (system-ui plus the Open Props token --font-industrial
 * for headings), a webfont pairing only from /assets/fonts/ (ADR-0011), no
 * build. Colours, spacing and shadows come from Open Props tokens. */

/* The Open Props tokens the frontend uses, inlined — the library file is no
 * longer loaded (ADR-0017). Values and names are Open Props' own, and so is the
 * zero-specificity `:where(html)`: any rule that set a token before still wins.
 * ⚠️ A token not listed here is UNDEFINED, and an undefined custom property
 * falls back to the initial value silently. A stylesheet that starts using
 * another Open Props name copies its value in here first. */
:where(html) {
    --font-system-ui: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
    --font-transitional: Charter, Bitstream Charter, Sitka Text, Cambria, serif;
    --font-humanist: Seravek, Gill Sans Nova, Ubuntu, Calibri, DejaVu Sans, source-sans-pro, sans-serif;
    --font-industrial: Bahnschrift, DIN Alternate, Franklin Gothic Medium, Nimbus Sans Narrow, sans-serif-condensed, sans-serif;
    --font-antique: Superclarendon, Bookman Old Style, URW Bookman, URW Bookman L, Georgia Pro, Georgia, serif;
    --font-sans: var(--font-system-ui);
    --font-weight-4: 400; --font-weight-6: 600; --font-weight-7: 700; --font-weight-8: 800;
    --font-size-0: .75rem; --font-size-1: 1rem; --font-size-2: 1.1rem; --font-size-3: 1.25rem;
    --font-size-4: 1.5rem; --font-size-5: 2rem; --font-size-6: 2.5rem;
    --size-1: .25rem; --size-2: .5rem; --size-3: 1rem; --size-4: 1.25rem;
    --size-5: 1.5rem; --size-6: 1.75rem; --size-7: 2rem; --size-8: 3rem;
    --size-9: 4rem; --size-10: 5rem; --size-11: 7.5rem;
    --radius-2: 5px; --radius-3: 1rem;
    --shadow-color: 220 3% 15%;
    --shadow-strength: 1%;
    --shadow-strength-3: calc(var(--shadow-strength) + 2%);
    --shadow-strength-4: calc(var(--shadow-strength) + 3%);
    --shadow-strength-5: calc(var(--shadow-strength) + 4%);
    --shadow-strength-6: calc(var(--shadow-strength) + 5%);
    --shadow-strength-7: calc(var(--shadow-strength) + 6%);
    --shadow-strength-8: calc(var(--shadow-strength) + 7%);
    --shadow-3:
        0 -1px 3px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)),
        0 1px 2px -5px hsl(var(--shadow-color)/var(--shadow-strength-3)),
        0 2px 5px -5px hsl(var(--shadow-color)/var(--shadow-strength-5)),
        0 4px 12px -5px hsl(var(--shadow-color)/var(--shadow-strength-6)),
        0 12px 15px -5px hsl(var(--shadow-color)/var(--shadow-strength-8));
    --shadow-4:
        0 -2px 5px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)),
        0 1px 1px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
        0 2px 2px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
        0 5px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)),
        0 9px 9px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)),
        0 16px 16px -2px hsl(var(--shadow-color)/var(--shadow-strength-7));
    --shadow-5:
        0 -1px 2px 0 hsl(var(--shadow-color)/var(--shadow-strength-3)),
        0 2px 1px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
        0 5px 5px -2px hsl(var(--shadow-color)/var(--shadow-strength-4)),
        0 10px 10px -2px hsl(var(--shadow-color)/var(--shadow-strength-5)),
        0 20px 20px -2px hsl(var(--shadow-color)/var(--shadow-strength-6)),
        0 40px 40px -2px hsl(var(--shadow-color)/var(--shadow-strength-8));
    --gray-0: #f8f9fa; --gray-1: #f1f3f5; --gray-2: #e9ecef; --gray-3: #dee2e6;
    --gray-4: #ced4da; --gray-5: #adb5bd; --gray-6: #868e96; --gray-7: #495057;
    --gray-9: #212529; --gray-10: #16191d; --gray-11: #0d0f12; --gray-12: #030507;
    --red-0: #fff5f5; --red-1: #ffe3e3; --red-3: #ffa8a8; --red-7: #f03e3e;
    --red-9: #c92a2a; --red-10: #b02525; --red-11: #962020; --red-12: #7d1a1a;
    --violet-9: #5f3dc4; --violet-10: #5235ab; --violet-11: #462d91; --violet-12: #3a2578;
    --indigo-9: #364fc7; --indigo-10: #2f44ad; --indigo-11: #283a94; --indigo-12: #21307a;
    --blue-0: #e7f5ff; --blue-3: #74c0fc; --blue-8: #1971c2; --blue-9: #1864ab;
    --blue-10: #145591; --blue-12: #0d375e;
    --teal-10: #066649; --teal-11: #054d37; --teal-12: #033325;
    --green-10: #237032; --green-11: #1b5727; --green-12: #133d1b;
    --yellow-2: #ffec99; --yellow-5: #fcc419;
    --orange-10: #bf400d; --orange-11: #99330b; --orange-12: #802b09;
}
/* Open Props deepens its shadows under a dark OS scheme; kept as it was. */
@media (prefers-color-scheme: dark) {
    :where(html) { --shadow-color: 220 40% 2%; --shadow-strength: 25%; }
}

:root {
    --ink: var(--gray-9);
    --muted: var(--gray-7);          /* 7.35:1 auf --bg, 6.90:1 auf --surface */
    --accent: var(--blue-8);
    --accent-hover: var(--blue-9);
    --accent-active: var(--blue-10); /* the pressed .btn-primary, one step below hover */
    --accent-deep: var(--blue-12);   /* volles Feld (Kennzahlen-Band) */
    /* The accent in its FOREGROUND role: an icon, a marker, a hairline, a focus
     * ring, a link that wants to stand out. On a light ground it is the accent's
     * own tone, or the next darker one where that tone misses 4.5:1 as text on
     * --surface: the default blue (4.23:1) and Terrakotta (4.48:1). Every accent
     * names it in the palette below. A dark variant remaps THIS one and every
     * icon, rule and outline inside it follows, while --accent stays what a
     * filled button paints with.
     *
     * ⚠️ Without the split there is no answer that works. On --gray-12 the
     * «Graphit» accent (--gray-9) is 1.32:1, so a checkmark or a hairline is in
     * the markup and not on the screen; lightening --accent itself would make
     * .btn-primary a white fill under white text. The hero learnt this first and
     * carried its own --hero-accent for it — this is that token, made general, so
     * a section can do the same for the arbitrary blocks it wraps. */
    --accent-fg: var(--blue-9);      /* 5.47:1 auf --bg, 5.13:1 auf --surface */
    --bg: var(--gray-1);
    --surface: var(--gray-2);
    --border: var(--gray-4);         /* Haarlinie / Wireframe-Kante */
    --ok-bg: var(--blue-0);  --ok-ink: var(--blue-9);  --ok-border: var(--blue-3);
    --err-bg: var(--red-0);  --err-ink: var(--red-9);  --err-border: var(--red-3);
    /* Error text standing on the GROUND (a form's field error, the required
     * marker) — not on the --err-bg plate, which keeps --err-ink. A dark
     * variant remaps this one, as it does --accent-fg; #8a0000 keeps the form's
     * ≥7:1 on a light ground (9.07:1 on --bg). */
    --err-fg: #8a0000;
    --radius: 0;                     /* eckig — Blueprint */
    --pop: var(--shadow-3);
    /* Content width — ONE source for header, body and footer. The three thereby
     * align on the same edges; making the site wider or narrower means changing
     * exactly this value, not four scattered numbers. The gutter is part of it:
     * the width includes the padding (box-sizing: border-box), so the content is
     * 1400px minus two margins. */
    --container: 1400px;
    --gutter: var(--size-4);
    /* Logo size in the header. On mobile the width shrinks (rule in the offcanvas
     * breakpoint) so brand and burger fit side by side. */
    --logo-width: 200px;
    --logo-max-height: 96px;
    /* Two semantic font roles; the branding pairings (.font-<key>) remap both onto
     * other Open Props stacks. `--font-body` rather than `--font-sans` directly, so
     * the Open Props token stays untouched. Default = pure system font in BOTH
     * roles; the condensed industrial heading is the selectable pairing
     * `.font-industrial`. */
    --font-head: var(--font-sans);
    --font-body: var(--font-sans);
}

/* Branding: curated accent palette. The body class `.accent-<key>` remaps the
 * accent tokens onto another Open Props tone — every downstream `var(--accent…)`
 * follows automatically, except a token `:root` DERIVES from one (see the `body`
 * rule below the palette). CSP-safe (a class instead of inline), every colour checked
 * for WCAG AA against white text AND a light background. Default = steel blue (no
 * class). Swatch mirror in the admin: assets/admin/design.css. */
.accent-indigo     { --accent: var(--indigo-9);  --accent-hover: var(--indigo-10); --accent-active: var(--indigo-11); --accent-deep: var(--indigo-12); --accent-fg: var(--indigo-9); }
.accent-violett    { --accent: var(--violet-9);  --accent-hover: var(--violet-10); --accent-active: var(--violet-11); --accent-deep: var(--violet-12); --accent-fg: var(--violet-9); }
.accent-petrol     { --accent: var(--teal-10);   --accent-hover: var(--teal-11);   --accent-active: var(--teal-12);   --accent-deep: var(--teal-12);   --accent-fg: var(--teal-10); }
.accent-gruen      { --accent: var(--green-10);  --accent-hover: var(--green-11);  --accent-active: var(--green-12);  --accent-deep: var(--green-12);  --accent-fg: var(--green-10); }
.accent-rot        { --accent: var(--red-10);    --accent-hover: var(--red-11);    --accent-active: var(--red-12);    --accent-deep: var(--red-12);    --accent-fg: var(--red-10); }
.accent-terrakotta { --accent: var(--orange-10); --accent-hover: var(--orange-11); --accent-active: var(--orange-12); --accent-deep: var(--orange-12); --accent-fg: var(--orange-11); }
.accent-graphit    { --accent: var(--gray-9);    --accent-hover: var(--gray-10);   --accent-active: var(--gray-11);   --accent-deep: var(--gray-12);   --accent-fg: var(--gray-9); }
/* Marineblau: a deep brand blue (hsl 208 100% 32%, the first customer's), not
 * an Open Props tone — hence the hex values. White on it is 7.3:1. */
.accent-marine     { --accent: #0057a3;          --accent-hover: #00417a;          --accent-active: #003666;          --accent-deep: #002c52;          --accent-fg: #0057a3; }
/* ⚠️ Each accent NAMES its `--accent-fg`; `:root` must not derive it from
 * `--accent`. A custom property resolves its `var()` where it is declared: a
 * `var(--accent)` in `:root` is resolved on <html>, and <body> would inherit the
 * finished default blue under every accent class — links, focus rings and
 * icons stayed blue while the buttons turned red. A dark variant further down
 * remaps it as before (`DarkVariantContrastTest`). */

/* Branding: curated font pairings. The body class `.font-<key>` remaps the two
 * roles --font-head (headings) and --font-body (body text) onto other font
 * stacks — every downstream `var(--font-head|body)` follows automatically.
 * Default = pure system font (no class, base in :root). The system pairings are
 * Open Props stacks with a fallback chain; a webfont pairing ships its files
 * under /assets/fonts/ and is served from here — never from a font CDN
 * (ADR-0011). Specimen mirror in the admin: assets/admin/design.css — a change
 * here means a change there (SettingsRepositoryTest enforces the parity). */
.font-humanist     { --font-head: var(--font-humanist);   --font-body: var(--font-humanist); }
.font-industrial   { --font-head: var(--font-industrial); --font-body: var(--font-sans); }
.font-redaktionell { --font-head: var(--font-antique);    --font-body: var(--font-transitional); }
.font-raleway      { --font-head: "Raleway", var(--font-sans); --font-body: "Public Sans", var(--font-sans); }

/* The webfont pairing «Raleway & Public Sans» (ADR-0011): self-hosted, Latin
 * subset, only the cuts the pages use. A browser fetches a face only when an
 * element is set in it, so these rules cost the sites without the pairing
 * nothing. The layout preloads the two faces the first paint needs while the
 * pairing is chosen — spelled exactly like the `src` here (AssetVersionTest).
 * Raleway © The Raleway Project Authors, Public Sans © The Public Sans Project
 * Authors — both SIL Open Font License 1.1. Each file carries its copyright and
 * a link to the licence, not the licence itself: that text stands beside the
 * files (fonts/raleway-OFL.txt, fonts/publicsans-OFL.txt, FontLicenceTest). */
@font-face { font-family: "Public Sans"; font-weight: 400; font-style: normal; font-display: swap; src: url("fonts/publicsans-400.woff2") format("woff2"); }
@font-face { font-family: "Public Sans"; font-weight: 600; font-style: normal; font-display: swap; src: url("fonts/publicsans-600.woff2") format("woff2"); }
@font-face { font-family: "Public Sans"; font-weight: 700; font-style: normal; font-display: swap; src: url("fonts/publicsans-700.woff2") format("woff2"); }
@font-face { font-family: "Raleway"; font-weight: 600; font-style: normal; font-display: swap; src: url("fonts/raleway-600.woff2") format("woff2"); }
@font-face { font-family: "Raleway"; font-weight: 700; font-style: normal; font-display: swap; src: url("fonts/raleway-700.woff2") format("woff2"); }

/* The industrial headings are condensed and take the tight tracking of the h1–h4
 * base rule; the serif pairing does not — neutralize it there. */
.font-redaktionell :is(h1, h2, h3, h4) { letter-spacing: 0; }

* { box-sizing: border-box; }
/* A block's jump target (emitted by the BlockRenderer from the «Sprungziel»
   field). Layout-neutral: no box, no height, just a point to jump to — `main` is
   normal block flow, so the element creates no gap there. `scroll-margin-top`
   gives the target air instead of gluing it to the window edge.
   Here and not in `blocks/jumplinks.css`: EVERY block can carry an anchor, even
   when no jumplinks block is on the page (a link from rich text, from a campaign,
   from another page). */
.anchor { display:block; height:0; scroll-margin-top:var(--size-8); }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}
body { margin:0; color:var(--ink); background:var(--bg);
    font-family:var(--font-body); font-size:1.0625rem; line-height:1.6;
    display:flex; flex-direction:column; min-height:100vh; }
:is(h1, h2, h3, h4) { font-family:var(--font-head); font-weight:var(--font-weight-7); line-height:1.12; letter-spacing:.01em; }
h1 { font-size:var(--font-size-6); margin:0 0 var(--size-2); }
a { color: var(--accent-fg); text-underline-offset:3px; }
/* Images carry width/height as attributes (the browser reserves the space in
   advance → no layout shift, CLS). For the attributes not to distort the image
   once CSS determines the width, the height must stay free.
   ⚠️ `:where()`, specificity zero: as `img[width][height]` (0,2,1) it beat every
   block rule that gives an image a height — `.hero-backdrop img { height:100% }`
   is (0,1,1), the video poster's (0,2,0) — so a cover image kept its aspect
   ratio and was clipped instead of cropped, and the focal point never moved
   it. FocalPointCssTest. */
:where(img[width][height]) { height:auto; }
/* Focal point from the media library (Media\FocalPoint): the class on the <img>
   sets the coordinate, and every rule that crops with `object-fit: cover` reads
   it as `object-position: var(--focal-x, 50%) var(--focal-y, 50%)` —
   FocalPointCssTest. The centre (50) has no class; it is the fallback. */
.focal-x0 { --focal-x:0%; } .focal-x10 { --focal-x:10%; } .focal-x20 { --focal-x:20%; }
.focal-x30 { --focal-x:30%; } .focal-x40 { --focal-x:40%; } .focal-x60 { --focal-x:60%; }
.focal-x70 { --focal-x:70%; } .focal-x80 { --focal-x:80%; } .focal-x90 { --focal-x:90%; }
.focal-x100 { --focal-x:100%; }
.focal-y0 { --focal-y:0%; } .focal-y10 { --focal-y:10%; } .focal-y20 { --focal-y:20%; }
.focal-y30 { --focal-y:30%; } .focal-y40 { --focal-y:40%; } .focal-y60 { --focal-y:60%; }
.focal-y70 { --focal-y:70%; } .focal-y80 { --focal-y:80%; } .focal-y90 { --focal-y:90%; }
.focal-y100 { --focal-y:100%; }
a:focus-visible, button:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--accent) 28%, transparent); }
.skip { position:absolute; left:-999px; top:0; z-index:60; background:var(--accent); color:#fff; padding:var(--size-2) var(--size-3); }
.skip:focus { left:0; }
/* <main> takes the focus only as a starting point (skip link, «Nach oben»); it
 * is no control, so no ring around the whole content. */
#inhalt:focus { outline:none; }
.hp { position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }
/* Gone for the eye, present for screen readers. Used by the design tab's
 * «Seitentitel anzeigen» switch when no block heading can take the <h1>
 * (ADR-0088): it stays in the document (docs/a11y.md demands exactly one per
 * page) but disappears from view. Deliberately NOT
 * `display:none` or `visibility:hidden` — both would take the text from the
 * screen reader too. `clip-path` instead of the old `clip`, and
 * `white-space:nowrap` so the text does not wrap into single letters in the 1px
 * cell (some screen readers would then spell it out). */
.visually-hidden { position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0; }
/* GTM-Fallback-iframe unsichtbar halten — CSS-Klasse statt Inline-Style (CSP style-src 'self'). */
.gtm-noscript { display:none; visibility:hidden; }

/* Admin bar top left — visible only to logged-in editors: back to the admin plus
   a jump into the backend editor (both icon-only, name via aria-label), plus a
   draft notice where applicable. It sits above the page and deliberately reserves
   no space: the header should sit the same for editors as for visitors. The
   colours are the backend sidebar's (app.css --sidebar*), as literals here —
   app.css is not loaded in the frontend (ADR-0011). */
.admin-tools { position:fixed; top:0; left:0; z-index:50; display:flex; align-items:stretch; max-width:100vw; }
.admin-return, .admin-edit { display:inline-flex; align-items:center; justify-content:center;
    background:#1c1917; color:#ede9e6; text-decoration:none; border:1px solid #1c1917; padding:.5rem; }
/* A hairline between the two icons, otherwise they merge into one surface. */
.admin-edit { border-left-color:#332e2a; }
.admin-return:hover, .admin-edit:hover { background:#332e2a; border-color:#332e2a; color:#fff; }
.admin-return:focus-visible, .admin-edit:focus-visible { outline:2px solid #2dbca6; outline-offset:2px; }
.admin-return svg, .admin-edit svg { display:block; }

/* Draft preview: a notice bar next to back/edit (a flex child of the admin bar).
   Visible only when logged in — for visitors a draft is a 404. */
.draft-note { margin:0; display:inline-flex; align-items:center; padding:0 .75rem;
    min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    background:var(--yellow-2); color:var(--gray-9); border:1px solid var(--yellow-5); border-left:0;
    font-family:var(--font-head); font-weight:var(--font-weight-6); font-size:13px; letter-spacing:.02em; }

/* ── Blueprint-Primitive (eckig, Haarlinie) ── */
/* The duotone wash was deliberately removed: images in real colours, no accent
   filter. The .duotone wrappers remain as layout containers (crop, image width)
   but carry no colour overlay. */
.blueprint { position:relative; border:1px solid var(--border); border-radius:0; }
.blueprint.duotone { overflow:visible; }
.duotone { position:relative; overflow:hidden; display:block; }
.duotone img { width:100%; display:block; }

/* Button — the primary button is the one filled object */
.btn { display:inline-flex; align-items:center; justify-content:center; gap:6px; cursor:pointer; text-decoration:none;
    font-family:var(--font-head); font-weight:var(--font-weight-6); font-size:15px; line-height:1.2; letter-spacing:.02em;
    color:var(--ink); background:transparent; border:1px solid var(--border); border-radius:0; padding:.6rem 1.1rem; }
.btn-primary { background:var(--accent); color:#fff; border-color:var(--accent); }
.btn-primary:hover { background:var(--accent-hover); }
.btn-primary:active { background:var(--accent-active); }
/* The phone icon a button to a tel: target carries (hero). */
.btn-icon { flex:none; width:1.1em; height:1.1em; }

/* Google rating (part «rating») — the hero's proof line, the footer and the
 * contact box print the same markup. The star is decorative; the score beside
 * it is the information (role=img names it on the five-star row). */
.rating { display:inline-flex; align-items:center; gap:.3rem; color:inherit; text-decoration:none; }
a.rating:hover, a.rating:focus-visible { text-decoration:underline; }
.rating-star { flex:none; width:1em; height:1em; color:#c08a00; }
.rating-star.is-filled { fill:currentColor; }
.rating-stars { display:inline-flex; gap:2px; }
.rating--stars { gap:var(--size-3); }
.rating--stars .rating-text { display:flex; flex-direction:column; line-height:1.2; }
.rating--stars .rating-count { font-size:.85rem; color:var(--muted); }

/* — Kopf / Marke — */
.site-header { border-bottom:1px solid var(--border); position:relative; }
/* Die Kopfleiste ist EINE Flex-Zeile, die umbrechen darf: Marke, Navigation,
 * Kontakt-Aktionen. Im DOM steht die Navigation vor den Aktionen (Tab-Reihenfolge),
 * visuell schickt `order` sie hier in eine zweite Zeile — die einzeilige Form
 * (header-eins.css) lässt sie stattdessen mittig in derselben Zeile stehen. */
.site-header .bar { max-width:var(--container); margin:0 auto; padding:var(--size-3) var(--gutter);
    display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center; gap:var(--size-3); }
.brand { order:1; display:inline-flex; align-items:center; gap:.7rem; line-height:1.1; text-decoration:none; color:var(--ink); }
.brand-text { display:flex; flex-direction:column; line-height:1.1; }
/* Site-Icon als Marke neben dem Wortzeichen — nur ohne Logo (siehe Part). Feste
 * Kantenlänge: ein Favicon ist quadratisch gedacht, `contain` rettet den Rest. */
.brand-icon { flex:none; width:40px; height:40px; object-fit:contain; }
.brand-title { font-family:var(--font-head); font-weight:var(--font-weight-7); font-size:1.4rem; letter-spacing:.01em; }
.brand-tagline { font-size:.8rem; font-weight:var(--font-weight-4); color:var(--muted); }
/* Logo: 200px width is the default, not the upper bound of a frame — a landscape
 * tradesperson logo should carry the header. `object-fit:contain` is the guard
 * against a portrait or square logo distorting at that width: it keeps its aspect
 * ratio and is capped by `max-height` instead of being squeezed into the box. */
.brand-logo { display:block; width:var(--logo-width); max-width:100%; height:auto;
    max-height:var(--logo-max-height); object-fit:contain; object-position:left center; }
.header-right { order:2; display:flex; align-items:center; gap:var(--size-3); }

/* — Kontakt-Aktionen in der Kopfleiste —
 * The header buttons come from Design → Kopfzeile alone (Site\HeaderButtons,
 * ADR-0108): the first filled with the accent, the second outlined. A tel: or
 * mailto: target carries its icon. The same pair stands at the foot of the open
 * menu (`.menu-btn`).
 *
 * `.header-contact` is the contact block («Telefon und E-Mail → In der
 * Kopfzeile») — number and e-mail as text, which header-kopf.css stacks. No
 * other choice puts the contact info into the bar. */
.header-contact { display:inline-flex; align-items:center; }
.header-tel { text-decoration:none; color:var(--ink);
    font-family:var(--font-head); font-weight:var(--font-weight-7); font-size:1.05rem;
    letter-spacing:.01em; white-space:nowrap; }
.header-tel:hover { color:var(--accent-fg); }
/* `wrap`: where the row is tight (the one-row form), the two buttons stack instead of
 * pushing the group under the brand — the group counts with one button's width. */
.header-buttons { display:flex; flex-wrap:wrap; justify-content:flex-end; align-items:center; gap:var(--size-2); }
.header-btn, .menu-btn { display:inline-flex; align-items:center; justify-content:center; gap:.5rem; text-decoration:none;
    font-family:var(--font-head); font-weight:var(--font-weight-6); font-size:15px; line-height:1.2;
    letter-spacing:.02em; white-space:nowrap;
    border:1px solid var(--accent); border-radius:var(--radius); padding:.7rem 1.2rem; }
.header-btn--primary, .menu-btn--primary { background:var(--accent); color:#fff; }
.header-btn--primary:hover, .menu-btn--primary:hover { background:var(--accent-hover); border-color:var(--accent-hover); color:#fff; }
/* Outlined: the accent as TEXT reads --accent-fg, which clears AA on the page
 * ground under every accent; the fill on hover is the primary pair. */
.header-btn--secondary, .menu-btn--secondary { background:transparent; color:var(--accent-fg); border-color:var(--accent-fg); }
.header-btn--secondary:hover, .menu-btn--secondary:hover { background:var(--accent); border-color:var(--accent); color:#fff; }
.header-btn-icon, .menu-btn-icon { width:1.1em; height:1.1em; flex:none; }

/* Sprach-Dropdown */
.lang-dropdown { position:relative; }
.lang-current { display:inline-flex; align-items:center; gap:.35rem; font-weight:var(--font-weight-6); font-size:.85rem;
    color:var(--ink); background:var(--surface); border:1px solid var(--border); border-radius:0; padding:.35rem .6rem; cursor:pointer; }
.lang-current .caret { color:var(--muted); font-size:.75rem; }
.lang-current[aria-expanded="true"] .caret { transform:rotate(180deg); }
.lang-menu { list-style:none; margin:.35rem 0 0; padding:.3rem; position:absolute; top:100%; right:0;
    min-width:150px; background:#fff; border:1px solid var(--border); box-shadow:var(--pop); z-index:25; }
.lang-menu a { display:block; text-decoration:none; color:var(--ink); padding:var(--size-2) .7rem; }
.lang-menu a:hover { background:var(--surface); }
.lang-menu a[aria-current] { font-weight:var(--font-weight-7); color:var(--accent-fg); }

/* — Navigation (Desktop-Leiste + Offcanvas teilen die Listen-Styles) — */
.menu { list-style:none; display:flex; gap:var(--size-1); margin:0; padding:0; align-items:center; }
.main-desktop { order:3; display:none; }
.menu-item { position:relative; }
.menu-item > a { text-decoration:none; font-weight:var(--font-weight-6); color:var(--ink); padding:var(--size-2) .6rem; display:inline-block; }
.menu-item > a:hover { color:var(--accent-fg); }
.menu-item > a[aria-current] { color:var(--accent-fg); text-decoration:underline; text-underline-offset:5px; }
.sub-toggle { background:none; border:0; cursor:pointer; font-size:.8rem; color:var(--muted); padding:.4rem .35rem; line-height:1; }
.sub-toggle[aria-expanded="true"] span { display:inline-block; transform:rotate(180deg); }
.submenu { list-style:none; margin:0; padding:.4rem; }
.submenu a { text-decoration:none; color:var(--ink); display:block; padding:var(--size-2) .7rem; white-space:nowrap; }
.submenu a:hover { background:var(--surface); }
@media (min-width:1200px) {
    .submenu:not([hidden]) { position:absolute; top:100%; left:0; min-width:200px;
        background:#fff; border:1px solid var(--border); box-shadow:var(--pop); z-index:20; }
    .burger { display:none; }
    .site-menu { display:none; }
    /* Eigene Zeile unter der Marke: eine Basis über die volle Breite erzwingt den
     * Umbruch, der Zeilenabstand ist der `gap` der Leiste. Die negativen Ränder
     * heben das Innenmass der Leiste wieder auf, damit die Trennlinie an der
     * Container-Kante steht und die Menüpunkte auf der Fluchtlinie der Marke —
     * die Basis zählt die beiden Ränder mit, bei `100%` endete die Linie rechts
     * eine Randbreite zu früh. */
    .main-desktop { display:block; flex-basis:calc(100% + 2 * var(--gutter)); border-top:1px solid var(--border);
        margin:0 calc(-1 * var(--gutter)) calc(-1 * var(--size-3)); }
    .main-desktop .menu { padding:var(--size-1) var(--gutter) var(--size-2); }
}

/* Burger */
.burger { display:none; width:44px; height:44px; background:none; border:1px solid var(--border); cursor:pointer; position:relative; }
.burger-box, .burger-box::before, .burger-box::after { content:""; position:absolute; left:50%; width:22px;
    height:2px; background:var(--ink); transform:translateX(-50%); transition:transform .2s, opacity .2s; }
.burger-box { top:50%; margin-top:-1px; }
.burger-box::before { top:-7px; } .burger-box::after { top:7px; }
.burger[aria-expanded="true"] .burger-box { background:transparent; }
.burger[aria-expanded="true"] .burger-box::before { transform:translateX(-50%) translateY(7px) rotate(45deg); }
.burger[aria-expanded="true"] .burger-box::after { transform:translateX(-50%) translateY(-7px) rotate(-45deg); }

/* Mobile: Offcanvas */
@media (max-width:1199px) {
    .burger { display:block; }
    /* Brand and burger share a 375px row — a 200px logo no longer fits next to it
     * without crowding. */
    :root { --logo-width: 150px; --logo-max-height: 64px; }
    .offcanvas-backdrop { position:fixed; inset:0; background:color-mix(in srgb, var(--gray-9) 45%, transparent); z-index:30; }
    .site-menu { position:fixed; top:0; right:0; bottom:0; width:min(80vw,320px); background:#fff;
        z-index:40; padding:5rem var(--size-4) var(--size-6); overflow-y:auto; box-shadow:var(--shadow-5);
        transform:translateX(100%); transition:transform .25s ease; visibility:hidden; border-left:1px solid var(--border);
        display:flex; flex-direction:column; }
    body.nav-open .site-menu { transform:translateX(0); visibility:visible; }
    body.nav-open { overflow:hidden; }
    .site-menu .menu { flex-direction:column; align-items:stretch; gap:.15rem; }
    /* Contact data and buttons stand at the BOTTOM of the open menu:
     * `margin-top:auto` presses them to the lower edge while the list is shorter
     * than the panel, and lets them scroll along with a long one. */
    .site-menu-foot { margin-top:auto; padding-top:var(--size-4); border-top:1px solid var(--border);
        display:flex; flex-direction:column; gap:var(--size-2); }
    .menu-tel { text-decoration:none; color:var(--ink); text-align:center;
        font-family:var(--font-head); font-weight:var(--font-weight-7); font-size:1.1rem;
        border:1px solid var(--border); padding:.7rem 1.2rem; }
    .menu-tel:hover { color:var(--accent-fg); border-color:var(--accent-fg); }
    /* Beside the burger there is room for icons, not for words: a button with a
     * tel: or mailto: target stays as a 44px square, its label kept for screen
     * readers (the accessible name); one without an icon stands in the open menu
     * only. */
    .header-buttons { flex-wrap:nowrap; }
    .header-btn:not(.header-btn--icon) { display:none; }
    .header-btn--icon { width:44px; height:44px; padding:0; }
    .header-btn--icon .header-btn-icon { width:22px; height:22px; }
    .header-btn--icon .header-btn-label { position:absolute; width:1px; height:1px; margin:-1px;
        overflow:hidden; clip-path:inset(50%); white-space:nowrap; }
    .menu-item { display:flex; flex-wrap:wrap; align-items:center; }
    .menu-item > a { flex:1; padding:var(--size-3) var(--size-2); font-size:1.05rem; }
    .sub-toggle { width:44px; height:44px; font-size:1rem; border:1px solid var(--border); }
    .submenu { flex-basis:100%; padding-left:var(--size-3); }
    .submenu a { padding:.6rem var(--size-2); }
}
@media (prefers-reduced-motion:reduce) {
    .site-menu, .burger-box, .burger-box::before, .burger-box::after { transition:none; }
}

/* — Rumpf — */
/* `main` is the outermost query container: a block's `@container` rule asks the
   width it actually has — the content box here, a column or a section band when
   it stands in one (columns.css, section.css). Its content box is the viewport
   minus two gutters (40px): a viewport line of 800px is `@container
   (min-width: 760px)` for a block at the top level. */
main { flex:1; width:100%; max-width:var(--container); margin:0 auto; padding:var(--size-8) var(--gutter);
    container-type:inline-size; }
.lead { font-size:1.15rem; color:var(--muted); margin:0 0 var(--size-7); }

/* — Breadcrumbs — the visible path belonging to the BreadcrumbList schema (Google
   only shows it in the snippet when it is on the page too). Hairline look like the
   rest of the blueprint theme. */
.breadcrumbs { margin:0 0 var(--size-6); font-size:.875rem; color:var(--muted); }
.breadcrumbs ol { list-style:none; display:flex; flex-wrap:wrap; align-items:center;
    gap:var(--size-2); margin:0; padding:0; }
.breadcrumbs li { display:flex; align-items:center; gap:var(--size-2); }
.breadcrumbs li + li::before { content:"›"; color:var(--border); }
.breadcrumbs a { color:inherit; text-decoration:none; border-bottom:1px solid var(--border); }
.breadcrumbs a:hover, .breadcrumbs a:focus-visible { color:var(--ink); border-bottom-color:var(--accent-fg); }
.breadcrumbs [aria-current="page"] { color:var(--ink); }
/* — «Zurück zur Auswahl» — what a sub-page shows instead of the path when its
   design tab says so (site/_back-link.twig, ADR-0092): below the opening hero,
   in the flow of the blocks. */
.back-link { display:flex; margin:0 0 var(--size-6); }
.back-link a { display:inline-flex; align-items:center; gap:var(--size-2);
    font-weight:var(--font-weight-6); color:var(--accent-fg); text-decoration:none; }
.back-link a:hover { text-decoration:underline; text-underline-offset:.2em; }
.back-link-icon { width:20px; height:20px; flex:none; }
.site-footer { border-top:1px solid var(--border); color:var(--muted); font-size:.9rem; }
.site-footer .bar { max-width:var(--container); margin:0 auto; padding:var(--size-6) var(--gutter); }
.footer-nav { margin-bottom:var(--size-3); }
.footer-menu { list-style:none; display:flex; flex-wrap:wrap; gap:var(--size-2) var(--size-6); margin:0; padding:0; }
.footer-menu > li > a { color:var(--ink); text-decoration:none; font-weight:var(--font-weight-6); }
.footer-menu a:hover { color:var(--accent-fg); }
.footer-menu ul { list-style:none; margin:.35rem 0 0; padding:0; display:flex; flex-direction:column; gap:.2rem; }
.footer-menu ul a { color:var(--muted); text-decoration:none; }
.footer-note { display:block; }

/* Bottom row: copyright, legal texts, cookie link. It wraps on narrow screens
   instead of overflowing — the legal texts are the one part whose length the
   business determines itself. */
.footer-bottom { display:flex; flex-wrap:wrap; align-items:baseline;
    gap:var(--size-2) var(--size-5); margin-top:var(--size-3); }
.footer-bottom .footer-note { display:inline; }
.footer-copyright { color:var(--ink); }
/* The list order = the order in the «Rechtstexte» area (top → left). */
.footer-legal ul { list-style:none; display:flex; flex-wrap:wrap;
    gap:var(--size-2) var(--size-4); margin:0; padding:0; }
.footer-legal a { color:var(--muted); text-decoration:none; }
.footer-legal a:hover { color:var(--accent-fg); text-decoration:underline; }

/* Footer contact block from the central company store. */
.footer-contact { margin-bottom:var(--size-3); }
.company-contact { font-style:normal; line-height:1.6; color:var(--muted); }
.company-contact strong { color:var(--ink); font-weight:var(--font-weight-6); }
.company-contact a { color:var(--ink); text-decoration:none; }
.company-contact a:hover { color:var(--accent-fg); }

/* Social channels from the company profile. Deliberately in site.css and not in
   the block CSS: the same row appears in the footer AND in rich text through the
   `%social%` / `%…_link%` placeholders — those have no block whose stylesheet
   could be loaded. blocks/social.css only adds the block's own arrangement. */
.footer-social { margin-bottom:var(--size-3); }
.social-links { list-style:none; display:flex; flex-wrap:wrap; align-items:center;
    gap:var(--size-2) var(--size-3); margin:0; padding:0; }
.social-link { display:inline-flex; align-items:center; gap:.4rem; color:var(--muted);
    text-decoration:none; line-height:1; }
.social-link svg { width:1.35rem; height:1.35rem; flex:none; }
.social-link:hover, .social-link:focus-visible { color:var(--accent-fg); }
.social-link span { font-weight:var(--font-weight-6); }
/* In running text the icon sits on the baseline, not on its own line. */
p .social-link svg { width:1.1em; height:1.1em; }
p .social-links { display:inline-flex; vertical-align:middle; }

/* ── Blocks ── */
/* Shared base only. Block-specific styles are self-contained in the block
   components (blocks/<type>/ → public/assets/blocks/<type>.css) and are loaded by
   the layout only when that block occurs on the page. */
.block { margin: 0 0 var(--size-7); }
.block :is(h1, h2) { font-size:var(--font-size-4); margin:0 0 var(--size-4); }

/* Rich-text output — ONE rule set for every carrier of sanitized HTML: text
   block, page intro (.lead), image+text body and FAQ answer. New rich-text fields
   belong in this list, not in a copy of their own. */
.block-text > :last-child, .lead > :last-child,
.it-body > :last-child, .faq-answer > :last-child { margin-bottom:0; }
.block-text :is(h1, h2), .lead :is(h1, h2), .it-body :is(h1, h2), .faq-answer :is(h1, h2) { font-size:var(--font-size-4); margin:var(--size-5) 0 var(--size-3); }
.block-text h3, .lead h3, .it-body h3, .faq-answer h3 { font-size:var(--font-size-3); margin:var(--size-4) 0 var(--size-2); }
.block-text ul, .block-text ol, .lead ul, .lead ol,
.it-body ul, .it-body ol, .faq-answer ul, .faq-answer ol { margin:0 0 var(--size-3); padding-left:1.5rem; }
.block-text li, .lead li, .it-body li, .faq-answer li { margin:0 0 .35rem; }
.block-text blockquote, .lead blockquote, .it-body blockquote, .faq-answer blockquote {
    margin:0 0 var(--size-3); padding-left:var(--size-4);
    border-left:2px solid var(--accent-fg); color:var(--muted); }

/* Password gate for protected pages (status=protected) */
.page-protected { max-width: 30rem; margin: var(--size-6) auto; }
.pw-form { display: flex; flex-direction: column; gap: var(--size-2); margin-top: var(--size-4); }
.pw-form label { font-weight: var(--font-weight-6); }
.pw-form input[type=password] { padding: .6rem .7rem; font: inherit; border: 1px solid var(--border); background: #fff; border-radius: 0; }
.pw-form button { align-self: flex-start; background: var(--accent); color: #fff; border: 1px solid var(--accent);
    padding: .55rem var(--size-4); font: inherit; font-weight: var(--font-weight-6); cursor: pointer; border-radius: 0; }
.pw-form button:hover { background: var(--blue-9); }
.pw-error { color: var(--red-7); font-weight: var(--font-weight-6); }

/* Kontaktformular (form-Block) — geteilte Formular-Grundstyles inkl. Honeypot. */
.block-form { padding:var(--size-6); }
.block-form :is(h1, h2) { margin:0 0 var(--size-3); }
.form-field { margin-bottom:var(--size-3); }
.block-form label { display:block; font-size:.8rem; margin-bottom:5px; color:var(--ink); }
.block-form input[type=text], .block-form input[type=email], .block-form textarea {
    width:100%; min-height:38px; padding:.5rem .7rem; font:inherit; color:var(--ink);
    caret-color:var(--accent-fg); background:var(--surface); border:1px solid var(--border); border-radius:0; }
.block-form textarea { min-height:110px; resize:vertical; }
.block-form input:focus-visible, .block-form textarea:focus-visible { border-color:var(--accent-fg); outline:2px solid var(--accent-fg); outline-offset:0; }
.form-ok { background:var(--ok-bg); color:var(--ok-ink); border:1px solid var(--ok-border); padding:.75rem var(--size-3); margin-bottom:var(--size-3); }
.form-err { background:var(--err-bg); color:var(--err-ink); border:1px solid var(--err-border); padding:.75rem var(--size-3); margin-bottom:var(--size-3); }

/* Card base — services and team share .cards/.card. */
.cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:var(--size-5); }
.card { padding:var(--size-4); }
/* Optional card icon (icon field → Icons::svg): inherits the accent colour
   through currentColor, scales sharply, costs no extra request. */
.card-icon { display:block; width:26px; height:26px; margin-bottom:.5rem; color:var(--accent-fg); }
.card h3 { margin:0 0 .4rem; font-size:1.15rem; }
.card p { margin:0; color:var(--muted); font-size:.95rem; }
/* Bullet list plus read-more link in service cards. */
.card-list { margin:.6rem 0 0; padding:0 0 0 1.1rem; color:var(--muted); font-size:.95rem; }
.card-list li { margin:.2rem 0; }
.card-list li::marker { color:var(--accent-fg); }

/* «Bildkacheln» — the page teaser and the «Aktuelles» block share them: image
   with the title as a bar underneath, the whole tile the link. The bar takes
   the accent on hover and focus. */
.teaser-tiles { display:grid; grid-template-columns:repeat(auto-fit,minmax(16rem,1fr)); gap:var(--size-6); }
.teaser-tile { display:block; color:var(--ink); text-decoration:none; }
.teaser-tile-media { aspect-ratio:4 / 3; background:var(--surface); }
.teaser-tile-media img { width:100%; height:100%; object-fit:cover; object-position:var(--focal-x, 50%) var(--focal-y, 50%); display:block; }
.teaser-tile h3 { margin:0; padding:var(--size-3) var(--size-4); font-size:1.1rem;
    background:var(--surface); border:1px solid var(--border); border-top:0; }
.teaser-tile:hover h3, .teaser-tile:focus-visible h3 { color:var(--accent-fg); text-decoration:underline; }
.teaser-tile:focus-visible { outline:2px solid var(--accent-fg); outline-offset:3px; }
.card-link { display:inline-block; margin-top:.7rem; color:var(--accent-fg);
    font-weight:var(--font-weight-6); font-size:.95rem; text-decoration:none; }
.card-link:hover, .card-link:focus-visible { text-decoration:underline; }

/* ── News ── */
/* Einzelbeitrag */
.news-back { margin:0 0 var(--size-3); font-size:.9rem; }
.news-back a { color:var(--muted); text-decoration:none; }
.news-back a:hover { color:var(--accent-fg); }
.news-single-head { margin-bottom:var(--size-6); }
.news-date { color:var(--muted); font-size:.9rem; margin:var(--size-2) 0 0; }

/* Index — post grid */
.news-grid { list-style:none; margin:0; padding:0; display:grid;
    grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); gap:var(--size-6); }
.news-card-link { display:flex; flex-direction:column; height:100%; text-decoration:none; color:inherit;
    border:1px solid var(--border); border-radius:0; background:var(--surface); }
.news-card-link:hover { border-color:var(--accent-fg); }
.news-card-body { display:flex; flex-direction:column; gap:.4rem; padding:var(--size-4); }
.news-card-date { color:var(--muted); font-size:.78rem; text-transform:uppercase; letter-spacing:.05em; }
.news-card-title { font-family:var(--font-head); font-weight:var(--font-weight-7); font-size:1.15rem; line-height:1.2; color:var(--ink); }
.news-card-link:hover .news-card-title { color:var(--accent-fg); }
.news-card-teaser { color:var(--muted); font-size:.92rem; }

/* ── Referenzen ── */
/* Einzelseite */
.ref-back { margin:0 0 var(--size-3); font-size:.9rem; }
.ref-back a { color:var(--muted); text-decoration:none; }
.ref-back a:hover { color:var(--accent-fg); }
.ref-single-head { margin-bottom:var(--size-6); }
.ref-cat { color:var(--accent-fg); font-size:.8rem; text-transform:uppercase; letter-spacing:.06em; font-weight:var(--font-weight-7); margin:0 0 var(--size-1); }
.ref-meta { color:var(--muted); font-size:.9rem; margin:var(--size-2) 0 0; }
.ref-website { margin:var(--size-3) 0 0; }
.ref-website a { color:var(--accent-fg); font-weight:var(--font-weight-6); text-decoration:none; }
.ref-website a:hover, .ref-website a:focus-visible { text-decoration:underline; }
.ref-gallery { display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); gap:var(--size-4); margin-bottom:var(--size-6); }
.ref-gallery img { display:block; width:100%; height:auto; }

/* Index — project tiles */
.ref-grid { list-style:none; margin:0; padding:0; display:grid;
    grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); gap:var(--size-6); }
.ref-card-link { display:flex; flex-direction:column; height:100%; text-decoration:none; color:inherit;
    border:1px solid var(--border); border-radius:0; background:var(--surface); }
.ref-card-link:hover { border-color:var(--accent-fg); }
.ref-card-media { display:block; }
.ref-card-media img { display:block; width:100%; height:auto; aspect-ratio:3/2; object-fit:cover;
    object-position:var(--focal-x, 50%) var(--focal-y, 50%); }
.ref-card-body { display:flex; flex-direction:column; gap:.4rem; padding:var(--size-4); }
.ref-card-cat { color:var(--accent-fg); font-size:.72rem; text-transform:uppercase; letter-spacing:.06em; font-weight:var(--font-weight-7); }
.ref-card-title { font-family:var(--font-head); font-weight:var(--font-weight-7); font-size:1.15rem; line-height:1.2; color:var(--ink); }
.ref-card-link:hover .ref-card-title { color:var(--accent-fg); }
.ref-card-meta { color:var(--muted); font-size:.85rem; }

/* ── Icon size floor against 300×150 ────────────────────────────────────────
   The counterpart to the rule in app.css, for the frontend. `Icons::svg()` (the
   icon field, card icons, block templates) emits only a `viewBox` — a block that
   renders an icon and sets no size in blocks/<type>.css would otherwise get
   300×150 in the middle of the page. Specificity 0 through `:where()` so every
   block rule wins; `:not([width])` leaves SVGs with their own size alone. */
:where(svg:not([width])) { width: 1em; height: auto; }


/* ── Beim Scrollen (Design → Kopfzeile) ──
 * «Kopfzeile oben halten»: the header sticks to the top; past 24px nav.js adds
 * `.is-scrolled` and the bar tightens, the logo shrinks. `--header-h` (set by
 * nav.js) lets a sticky jump-link bar sit below it. The header paints its own
 * ground so the page does not show through. */
.header-sticky .site-header { position:sticky; top:0; z-index:30; background:var(--bg); }
/* A jump to an anchor (a hero button to `#kontakt`, the skip link) and the
 * browser scrolling a focused element into view stop BELOW the sticky header
 * instead of under it. `--header-h` comes from nav.js and exists only while the
 * header sticks — 0 otherwise. The jump-link bar adds only its own height
 * (jumplinks.js). */
html { scroll-padding-top:var(--header-h, 0px); }
.header-sticky .site-header .bar { transition:padding .2s ease; }
.header-sticky .brand-logo { transition:max-height .2s ease, width .2s ease; }
.header-sticky.is-scrolled .site-header .bar { padding-block:var(--size-2); }
.header-sticky.is-scrolled .brand-logo { max-height:calc(var(--logo-max-height) * .65); }
.header-sticky.is-scrolled .main-desktop .menu { padding-block:0 var(--size-1); }

/* «Nach oben»: bottom right, shown past 100px of scrolling (nav.js). */
.to-top { position:fixed; right:var(--size-4); bottom:var(--size-4); z-index:35;
    display:grid; place-items:center; width:44px; height:44px; padding:0; cursor:pointer;
    background:var(--accent); color:#fff; border:0; opacity:0; visibility:hidden;
    transition:opacity .2s ease, visibility .2s; }
.to-top.is-visible { opacity:1; visibility:visible; }
.to-top:hover { background:var(--accent-hover); color:#fff; }
.to-top:focus-visible { outline:3px solid var(--accent-fg); outline-offset:2px; }
/* The open menu owns the screen. With a sticky header it sits in the header's
 * stacking context (z-index 30), below this button's 35 — the button stood on
 * top of the menu. */
.nav-open .to-top { visibility:hidden; }

/* ── «Aktuell»-Hinweis (Site\SiteNotice, ADR-0089) ──
 * The badge in the hero that opens the page, the tab docked at the right edge
 * once that hero has scrolled away (on phones always, instead of the badge),
 * and the dialog both open. Badge and tab are plates with their own ink — the
 * badge stands on the hero's photo, the tab on whatever scrolls past. The live
 * green says «current»; white on it is 5.3:1, it on white 6.3:1. Without JS
 * both are hidden: they would open nothing (notice.js).
 * Preview mirror in the admin: assets/admin/hinweis.css — its plates copy the
 * colours below by hand, so a change here is a change there. */
.notice-badge { position:absolute; top:var(--size-6); right:var(--size-6); z-index:2;
    display:flex; flex-direction:column; align-items:flex-start; gap:var(--size-1);
    padding:var(--size-3) var(--size-5); border:0; background:#fff; color:#1d2633;
    font:inherit; text-align:left; white-space:nowrap; cursor:pointer; }
.notice-badge:hover { background:#eef8f2; color:#1d2633; }
.notice-badge:focus-visible { outline:3px solid #fff; outline-offset:3px; }
.notice-badge-kicker { display:inline-flex; align-items:center; gap:var(--size-2);
    font-size:.75rem; font-weight:var(--font-weight-7); letter-spacing:.08em; text-transform:uppercase; color:#1b6a43; }
.notice-badge-title { display:inline-flex; align-items:center; gap:var(--size-2);
    font-family:var(--font-head); font-size:1.05rem; font-weight:var(--font-weight-7); }
.notice-arrow { width:18px; height:18px; color:#1b6a43; transition:transform .2s ease; }
.notice-badge:hover .notice-arrow { transform:translateX(3px); }
.notice-dot { flex:none; width:8px; height:8px; border-radius:50%; background:#23884f;
    animation:notice-pulse 2s ease-out infinite; }
@keyframes notice-pulse { 0% { box-shadow:0 0 0 0 rgba(35, 136, 79, .5); } 70% { box-shadow:0 0 0 7px rgba(35, 136, 79, 0); } 100% { box-shadow:0 0 0 0 rgba(35, 136, 79, 0); } }

/* The tab: turned 90° at the right edge, reading bottom-up. */
.notice-dock { position:fixed; right:0; top:50%; z-index:40; writing-mode:vertical-rl;
    display:flex; align-items:center; gap:var(--size-2); padding:var(--size-4) var(--size-2);
    border:0; background:#1f7a4d; color:#fff; font:inherit; font-family:var(--font-head);
    white-space:nowrap; cursor:pointer; transform:translate(100%, -50%) rotate(180deg);
    visibility:hidden; transition:transform .3s ease, visibility .3s; }
.notice-dock.is-docked { transform:translate(0, -50%) rotate(180deg); visibility:visible; }
.notice-dock:hover { background:#17613c; color:#fff; }
.notice-dock:focus-visible { outline:3px solid var(--accent-fg); outline-offset:2px; }
/* The open menu covers the page; the tab would stand on top of it. */
.nav-open .notice-dock { visibility:hidden; }
.notice-dock .notice-dot { background:#fff; }
@media (max-width: 767px) {
    .notice-badge { display:none; }
    .notice-dock { transform:translate(0, -50%) rotate(180deg); visibility:visible; padding:var(--size-3) 6px; font-size:.85rem; }
}
@media (scripting: none) { .notice-badge, .notice-dock { display:none; } }

/* The dialog. `margin:auto` restores the centring a modal <dialog> relies on. */
.notice-dialog { position:fixed; inset:0; margin:auto; height:fit-content;
    width:min(36rem, calc(100% - 2 * var(--size-4))); max-height:calc(100% - 2 * var(--size-4));
    padding:var(--size-7) var(--size-6) var(--size-6); border:0; background:#fff; color:#1d2633; }
.notice-dialog::backdrop { background:rgba(10, 20, 30, .6); }
.notice-dialog-title { font-size:var(--font-size-4); margin:var(--size-3) 0 var(--size-2); }
/* The text is rich text on the dialog's own white plate: its ink is fixed
   (#4a5563, 7.5:1 on #fff), a link takes it too and is told apart by its
   underline. */
.notice-text { color:#4a5563; margin:0 0 var(--size-5); }
.notice-text :is(p, ul, ol) { margin:0 0 var(--size-3); }
/* The editor leaves an empty paragraph where a list starts or ends. */
.notice-text p:empty { display:none; }
.notice-text > :last-child { margin-bottom:0; }
.notice-text a { color:inherit; text-decoration:underline; }
.notice-badge-label { display:inline-block; padding:var(--size-1) var(--size-3); background:#1f7a4d; color:#fff;
    font-size:.75rem; font-weight:var(--font-weight-7); text-transform:uppercase; letter-spacing:.08em; }
.notice-dialog-close { position:absolute; top:var(--size-2); right:var(--size-2);
    display:inline-flex; align-items:center; justify-content:center; width:44px; height:44px; padding:0;
    border:0; background:transparent; color:#1d2633; cursor:pointer; }
.notice-dialog-close:hover { color:var(--accent); }
.notice-close-icon { width:24px; height:24px; }
.notice-actions { display:flex; flex-wrap:wrap; gap:var(--size-3); }
.notice-secondary { color:#1d2633; border-color:#8a94a3; }
.notice-secondary:hover, .notice-secondary:focus-visible { color:#1d2633; border-color:#1d2633; }

/* ── `hidden` holds again, whatever else is set ─────────────────────────────
   The counterpart to the rule at the end of app.css, for the frontend. Scattered
   `.foo[hidden] { display: none }` patches exist here too (blocks/form.css five
   times, gallery.css, cookie-banner.css) — each for a place where an own
   `display` rule beat the UA `[hidden]`. `!important`, because block CSS
   (public/assets/blocks/<type>.css) loads conditionally AFTER site.css and an
   equally specific class there would otherwise win by order. */
[hidden] { display: none !important; }
