/* ===== _tokens.css ===== */
/* ============================================================================
   frankfurt-schloss24.de — THE SHARED DESIGN LANGUAGE
   Style: stahl-kupfer, re-themed from gunmetal steel-blue to the site's own
   TEAL brand hue (#1F7A6A — the favicon field and the webmanifest theme_color),
   with the copper CTA family and the brushed-metal neutrals kept.

   Every pair below is machine-measured, not eyeballed — see DESIGN-LANGUAGE.md
   for the full fg/bg matrix. Gates: 4.5:1 for text, 3:1 for a load-bearing
   border or a focus ring.

   DO NOT introduce a colour, font, radius, shadow or duration outside this
   block. A pattern's own <style> is layout only.
   ========================================================================== */
:root{
  /* --- surfaces (cool, faintly TEAL-cast: "brushed metal", never pure grey) --- */
  --bg:#f5faf8; --surface:#ffffff; --surface-alt:#eaf3f0; --steel:#dceae6;
  --line:#cbdcd7;          /* decorative hairline — 1.15:1 on --steel, never load-bearing */
  --line-strong:#627a73;   /* 4.61:1 on white, 3.73:1 on --steel, 3.62:1 on --tint-2 —
                              use whenever a border is the ONLY signal */
  --dark:#0c1a17; --dark-2:#12251f; --dark-line:#23423a; --dark-muted:#a6c1ba;

  /* --- ink --- */
  --text:#14231f;          /* 16.28:1 on --surface */
  --muted:#42564f;         /*  7.85:1 on --surface — "muted" is chroma, never contrast */
  --head:#0d1c18;

  /* --- brand: the teal from the favicon --- */
  --primary:#165C50;       /* deep teal · 7.83:1 on --surface · white on it 7.83:1 */
  --primary-hover:#0F473E; /* 10.55:1 on --surface */
  --primary-2:#1F7A6A;     /* THE brand hue (favicon field, theme_color) · 5.18:1 on white.
                              As a fill it takes WHITE text only (5.18) — never --on-dark
                              (4.16) and never copper text (2.39). */
  --primary-subtle:#e2efeb;
  --on-primary:#ffffff;

  /* --- the ONE reserved accent: the phone CTA --- */
  --cta:#b8541c;           /* copper · white on it 4.86:1 · a FILL token */
  --cta-hover:#9c4415;
  --cta-light:#e8a06a;     /* copper for TEXT on --dark (8.22:1). NOT valid on --primary
                              (3.61) — see --cta-on-primary. */
  --cta-on-primary:#f2c4a2;/* copper for TEXT on a --primary fill (4.92:1). The teal primary
                              is lighter than the style's original steel-blue, so the
                              original --cta-light does not clear AA there. */
  --cta-ink:#a04716;       /* copper for TEXT on any LIGHT band. --cta itself is 4.86:1 on
                              white but only 4.30 on --surface-alt, 4.11 on --primary-subtle
                              and 3.92 on --steel, so it is a FILL token, not a text token.
                              --cta-ink clears 4.5:1 on all light grounds (worst 4.83). */
  --on-cta:#ffffff;
  --cta-edge:#66290a;      /* the button RIM. --cta-hover is only 1.33:1 against the copper
                              fill — a hover state, not an edge: as a border it is invisible,
                              which is the opposite of what a border is for. This is 2.29:1
                              against the fill (a rim you can actually see) and 11.13:1 on
                              white, where on the inverted call button the border IS the
                              boundary. */
  --primary-edge:#082b25;  /* the same for the teal fill: 1.94:1 against --primary. */

  --dark-3:#17322b;        /* raised surface ON a dark band (icon badge, ledger key) */
  --dark-4:#1b3a32;        /* hover surface / ghost graphic on dark */
  --dark-line-2:#3a5f55;   /* hover border on dark */
  --on-dark:#d9eae5;       /* body copy on --dark (14.33:1) or a --primary fill (6.29:1) */
  --tint-2:#d5e8e2;        /* pressed/hover tint on light */
  --cta-tint:#fdf4ef;      /* ground of the info notice + accent hover tint */
  --focus:#08201c;         /* ALWAYS paired with a white inner ring — alone it is 2.17:1
                              on --primary, which is the trap this dual ring exists for */
  --danger:#b3261e; --danger-tint:#fdf1f0; --danger-light:#f0a09a;
  --success:#1c6b3f;

  /* --- personality --- */
  --r-xs:2px; --r-sm:4px; --r:8px; --r-lg:14px; --pill:999px;
  --notch:14px;                                   /* the key-bow corner cut */
  --sh-sm:0 1px 2px rgba(12,26,23,.06), 0 2px 6px rgba(12,26,23,.05);
  --sh:0 6px 18px rgba(12,26,23,.08);
  --sh-lg:0 18px 44px rgba(12,26,23,.13);
  --sh-dark:0 18px 44px rgba(0,0,0,.34);

  --c:1320px; --gut:26px;      /* content width — widened from the style default 1180 */
  --ease:cubic-bezier(.22,.61,.36,1);
  --dur:.22s; --dur-slow:.58s;
  --tap:44px;
  --f:'Segoe UI',system-ui,-apple-system,'Helvetica Neue',Arial,sans-serif;
  --f-num:ui-monospace,'SF Mono','Cascadia Mono','Segoe UI Mono',Menlo,Consolas,monospace;
}

/* ===== _core-body.css ===== */
*,*::before,*::after{box-sizing:border-box}
*{margin:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  font-family:var(--f);
  font-size:18px;              /* A10 floor — never drop to 16px */
  line-height:1.7;
  color:var(--text);
  background:var(--bg);
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  text-underline-offset:.18em;
}
img{max-width:100%;height:auto;display:block}
svg{display:block}
a{color:var(--primary);text-decoration-thickness:1px}
/* THE BUG THIS FIXES: a bare `a:hover` is (0,1,1) and beats `.btn{color:var(--btn-fg)}`
   (0,1,0), so on hover every button label turned copper — and on .btn--cta that is
   copper text on a darker-copper wipe, i.e. the label disappears. A link that IS a
   component owns its own colour; only prose links take the copper hover.
   (Same specificity trap as `.section--dark a` further down — attribute/state selectors
   quietly outrank plain class rules.) */
a:not(.btn):not(.callbtn):not(.calltab):not(.chip):not(.brand):not(.ftel):not(.telbig):hover{
  color:var(--cta-ink)}
/* hyphens:auto on a DISPLAY heading produces mid-word breaks that read as a defect
   ("Schließzylin-" / "der" at 48px). Headings get hyphens:manual with overflow-wrap
   as the safety net for a compound too long for its column; body copy — where German
   hyphenation genuinely helps the ragged edge — keeps hyphens:auto. */
h1,h2,h3,h4{color:var(--head);line-height:1.14;letter-spacing:-.018em;font-weight:800;
  overflow-wrap:break-word;hyphens:manual}
p{max-width:68ch;hyphens:auto}
ul,ol{margin:0;padding:0}
strong{color:var(--head);font-weight:700}
::selection{background:var(--cta);color:#fff}

/* one focus treatment that survives EVERY band: white inner ring + dark outer */
:where(a,button,summary,[tabindex]):focus-visible{
  outline:none;
  box-shadow:0 0 0 3px var(--surface),0 0 0 6px var(--focus);
  border-radius:var(--r-sm);
}
.section--dark :where(a,button,summary):focus-visible{
  box-shadow:0 0 0 3px var(--dark),0 0 0 6px var(--cta-light);
}

/* ------------------------------------------------------------ structure --- */
.wrap{max-width:var(--c);margin-inline:auto;padding-inline:var(--gut)}
.wrap--narrow{max-width:860px}
.wrap--wide{max-width:1480px}
.section{padding:clamp(58px,7.2vw,104px) 0;position:relative}
.section--tight{padding:clamp(40px,4.6vw,64px) 0}
.section--alt{background:var(--surface-alt)}
.section--surface{background:var(--surface)}
.section--dark{background:var(--dark);color:var(--dark-muted)}
.section--dark h1,.section--dark h2,.section--dark h3,.section--dark h4{color:#fff}
/* ROOT-CAUSE FIX: a bare `.section--dark a` is (0,1,1) and silently beat every
   button's own (0,1,0) colour, so .btn/.callbtn inside a dark band rendered their
   label in --cta-light — copper-on-copper at ~1.4:1 on the CTA fill. A link that is
   a BUTTON carries its own colour; only prose links take the dark-band link colour. */
.section--dark a:not(.btn):not(.callbtn):not(.calltab):not(.chip):not(.brand):not(.ftel){
  color:var(--cta-light)}
.section--dark .lead{color:var(--on-dark)}

/* the SIGNATURE band: brushed steel. A material texture (german-design.md §4.3),
   not a decorative gradient — fine vertical hairlines at 2.5% over a flat tint. */
.section--steel{
  background:
    repeating-linear-gradient(90deg,
      rgba(34,65,92,.022) 0 1px, rgba(255,255,255,0) 1px 6px),
    repeating-linear-gradient(90deg,
      rgba(255,255,255,.42) 0 1px, rgba(255,255,255,0) 1px 14px),
    var(--steel);
}
.section--dark.section--steel{
  background:
    repeating-linear-gradient(90deg,
      rgba(255,255,255,.035) 0 1px, rgba(255,255,255,0) 1px 4px),
    var(--dark);
}

/* --------------------------------------------------------- typography ----- */
.display{
  font-size:clamp(34px,5.6vw,62px);font-weight:800;line-height:1.05;
  letter-spacing:-.026em;color:var(--head);
}
.display .l2{display:block;color:var(--primary)}
.section--dark .display,.hero--dark .display{color:#fff}
.section--dark .display .l2,.hero--dark .display .l2{color:var(--cta-light)}
.display--long{font-size:clamp(30px,4.4vw,48px)}

.stitle{font-size:clamp(27px,3.3vw,42px);font-weight:800;letter-spacing:-.022em}
.h3{font-size:clamp(19px,1.5vw,22px);font-weight:700;letter-spacing:-.012em;line-height:1.28}
.lead{font-size:clamp(18px,1.35vw,20.5px);line-height:1.66;color:var(--muted)}
.small{font-size:16.5px;line-height:1.6}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* section head — the eyebrow + title + optional lead, three rhythms */
.shead{margin-bottom:clamp(30px,3.4vw,50px);max-width:760px}
.shead--center{margin-inline:auto;text-align:center}
.shead--split{display:grid;gap:22px clamp(28px,4vw,64px);align-items:end;max-width:none;
  grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){.shead--split{grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr)}}
.shead .stitle+.lead{margin-top:15px}

/* eyebrow — variant A: copper key-bit mark + label */
.eyebrow{display:inline-flex;align-items:center;gap:10px;font-size:13px;font-weight:800;
  letter-spacing:.15em;text-transform:uppercase;color:var(--cta-ink);margin-bottom:15px}
.section--dark .eyebrow{color:var(--cta-light)}
/* the key-bitting mark — 4 teeth at 30x12. Measured: a 7-tooth profile at 26x9 renders
   as an illegible smear, so the tooth count is capped at what survives at this size. */
.eyebrow::before{content:"";width:30px;height:12px;flex:none;background:currentColor;
  clip-path:polygon(0 100%,0 42%,20% 42%,20% 8%,40% 8%,40% 58%,60% 58%,60% 22%,80% 22%,80% 70%,100% 70%,100% 100%)}
/* eyebrow — variant B: boxed, for use where a rule would be too quiet */
.eyebrow--box{border:1.5px solid var(--line-strong);color:var(--primary);
  padding:7px 14px;letter-spacing:.13em;font-size:12px}
.eyebrow--box::before{display:none}
.section--dark .eyebrow--box{border-color:var(--dark-line);color:var(--cta-light)}
/* eyebrow — variant C: the pin-tumbler rule. Pins HANG from a shear line rather than
   standing on a baseline — bottom-anchored bars read as signal-strength bars, which is
   the wrong object entirely. Top-anchored under a rule reads as a lock cross-section. */
.pinrule{display:flex;align-items:flex-start;gap:5px;height:24px;margin-bottom:16px;
  padding-top:4px;border-top:2px solid var(--cta);width:45px}
.pinrule i{width:4px;background:var(--cta);display:block;border-radius:0 0 2px 2px}
.pinrule i:nth-child(1){height:13px}.pinrule i:nth-child(2){height:7px}
.pinrule i:nth-child(3){height:17px}.pinrule i:nth-child(4){height:10px}
.pinrule i:nth-child(5){height:15px}
.section--dark .pinrule{border-top-color:var(--cta-light)}
.section--dark .pinrule i{background:var(--cta-light)}

/* ------------------------------------------------------------- buttons ---- */
.btn{
  --btn-bg:var(--primary); --btn-fg:var(--on-primary); --btn-bg-h:var(--primary-hover);
  position:relative;display:inline-flex;align-items:center;justify-content:center;gap:11px;
  min-height:var(--tap);padding:14px 26px;border:0;border-radius:var(--r-sm);
  background:var(--btn-bg);color:var(--btn-fg);
  font:700 17px/1.2 var(--f);letter-spacing:.005em;text-decoration:none;cursor:pointer;
  transition:background var(--dur) var(--ease),transform var(--dur) var(--ease),
             box-shadow var(--dur) var(--ease);
  overflow:hidden;isolation:isolate;
}
/* The wipe MUST be z-index:-1, not 0. CSS paint order puts a positioned descendant at
   z-index:0 ABOVE the element's own inline content — so a button whose label is a bare
   text node (most of them) had its text painted over by the wipe on hover. `.btn>*`
   cannot rescue it: a text node is not an element and can never be lifted.
   At -1, and with `isolation:isolate` on .btn keeping it inside this stacking context,
   the wipe paints above the button's background and below the label, wrapped or not. */
.btn>*{position:relative;z-index:1}
.btn::before{content:"";position:absolute;inset:0;z-index:-1;background:var(--btn-bg-h);
  transform:scaleX(0);transform-origin:left;transition:transform .3s var(--ease)}
.btn:hover::before{transform:scaleX(1)}
.btn:hover{box-shadow:var(--sh)}
.btn:active{transform:translateY(1px)}
.btn--cta{--btn-bg:var(--cta);--btn-fg:var(--on-cta);--btn-bg-h:var(--cta-hover)}
.btn--ghost{--btn-bg:transparent;--btn-fg:var(--primary);--btn-bg-h:var(--primary);
  box-shadow:inset 0 0 0 1.5px var(--line-strong)}
.btn--ghost:hover{color:#fff}
.section--dark .btn--ghost{--btn-fg:#fff;--btn-bg-h:#fff;box-shadow:inset 0 0 0 1.5px var(--dark-line)}
.section--dark .btn--ghost:hover{color:var(--dark)}
.btn--lg{padding:18px 32px;font-size:18.5px}
.btn--block{display:flex;width:100%}
.btn svg{width:20px;height:20px;flex:none}

/* the call button: label above the number, icon disc at the left.
   Lifted from the Dexon hero treatment, re-coloured to copper. */
.callbtn{
  display:inline-flex;align-items:center;gap:14px;min-height:var(--tap);
  padding:11px 26px 11px 11px;border-radius:var(--pill);
  background:var(--cta);color:#fff;text-decoration:none;
  transition:background var(--dur) var(--ease),transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
.callbtn:hover{background:var(--cta-hover);color:#fff;transform:translateY(-2px);box-shadow:var(--sh-lg)}
.callbtn .disc{width:46px;height:46px;flex:none;border-radius:50%;background:rgba(255,255,255,.19);
  display:grid;place-items:center}
.callbtn .disc svg{width:21px;height:21px;stroke:#fff}
/* opacity:.86 white over copper blends to #f5e7df = 4.02:1 — under AA for 12px text.
   Full white is 4.86:1. Never dim text on the accent fill to "soften" it. */
.callbtn .cap{display:block;font-size:12px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;line-height:1}
.callbtn .num{display:block;font-size:21px;font-weight:800;letter-spacing:-.01em;margin-top:3px;
  font-variant-numeric:tabular-nums}
/* Specificity note: `.section--dark a` is (0,1,1) and would otherwise win over a
   plain `.callbtn--dark` (0,1,0), painting this white-ground button in --cta-light
   at ~1.9:1. Doubling the class to (0,2,0) is what keeps it readable on a dark band. */
.callbtn.callbtn--dark{background:var(--surface);color:var(--primary)}
.callbtn.callbtn--dark:hover{background:var(--surface);color:var(--primary)}
.callbtn--dark .disc{background:var(--cta)}
.callbtn--dark .cap{color:var(--muted);opacity:1}

/* ------------------------------------------------------- icon badges ------ */
.ibadge{width:54px;height:54px;flex:none;border-radius:var(--r);display:grid;place-items:center;
  background:var(--primary-subtle);color:var(--primary);
  transition:background var(--dur) var(--ease),color var(--dur) var(--ease),transform var(--dur) var(--ease)}
.ibadge svg{width:26px;height:26px;stroke-width:1.7}
.ibadge--cta{background:var(--cta);color:#fff}
.ibadge--dark{background:var(--primary);color:#fff}
.ibadge--outline{background:transparent;box-shadow:inset 0 0 0 1.5px var(--line-strong)}
.ibadge--round{border-radius:50%}
.ibadge--lg{width:70px;height:70px}.ibadge--lg svg{width:32px;height:32px}
.ibadge--sm{width:40px;height:40px;border-radius:var(--r-sm)}.ibadge--sm svg{width:20px;height:20px}
.section--dark .ibadge{background:var(--dark-3);color:var(--cta-light)}

/* --------------------------------------------------------------- cards ---- */
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:clamp(22px,2.2vw,30px);position:relative;
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease),
             transform var(--dur) var(--ease)}
.card:hover{border-color:var(--line-strong);box-shadow:var(--sh);transform:translateY(-3px)}
.card h3,.card .h3{margin-bottom:9px}
.card p{color:var(--muted);font-size:17px}
.section--dark .card{background:var(--dark-2);border-color:var(--dark-line);color:var(--dark-muted)}
.section--dark .card:hover{border-color:var(--dark-line-2)}
.section--dark .card p{color:var(--dark-muted)}

/* the key-bow NOTCH — the style's structural signature: one cut top-right corner.
   NOTE: clip-path also clips border AND box-shadow, so a notched box can never carry
   either. Two forms exist, and both are honest about that:
     .card--notch  — a FILLED notched card (no edge). Only legible on a band that
                     contrasts its fill, so it ships on tint/steel/dark bands.
     .notch/.notch__in — a two-element notched card WITH a hairline edge: the outer
                     box is the line colour, the inner is inset 1px and re-clipped.
   Elevation on a clipped box must use filter:drop-shadow(), which is applied after
   clipping and therefore traces the notch; box-shadow would be cut away. */
.card--notch{
  clip-path:polygon(0 0,calc(100% - var(--notch)) 0,100% var(--notch),100% 100%,0 100%);
  border:0;background:var(--primary-subtle);box-shadow:none;
  transition:background var(--dur) var(--ease),transform var(--dur) var(--ease),filter var(--dur) var(--ease)}
.card--notch:hover{background:var(--tint-2);filter:drop-shadow(0 10px 20px rgba(16,25,35,.13))}
.section--dark .card--notch{background:var(--dark-2)}
.section--dark .card--notch:hover{background:var(--dark-4);filter:none}

.notch{--notch-line:var(--line);
  clip-path:polygon(0 0,calc(100% - var(--notch)) 0,100% var(--notch),100% 100%,0 100%);
  background:var(--notch-line);padding:1px;
  transition:background var(--dur) var(--ease),filter var(--dur) var(--ease)}
.notch:hover{--notch-line:var(--line-strong);filter:drop-shadow(0 10px 20px rgba(16,25,35,.12))}
.notch__in{
  clip-path:polygon(0 0,calc(100% - var(--notch)) 0,100% calc(var(--notch) - 1px),100% 100%,0 100%);
  background:var(--surface);padding:clamp(22px,2.2vw,30px);height:100%}
.section--dark .notch{--notch-line:var(--dark-line)}
.section--dark .notch:hover{--notch-line:var(--dark-line-2);filter:none}
.section--dark .notch__in{background:var(--dark-2)}

/* the COPPER INLAY — a 3px leading edge marking the emphasis card in a set */
.card--inlay{border-top:3px solid var(--cta)}
.card--lead{background:var(--primary);border-color:var(--primary);color:var(--on-dark)}
.card--lead h3,.card--lead .h3{color:#fff}
.card--lead p{color:var(--on-dark)}

/* -------------------------------------------------------------- grids ----- */
.grid{display:grid;gap:clamp(16px,1.7vw,24px)}
.g2{grid-template-columns:minmax(0,1fr)}
.g3{grid-template-columns:minmax(0,1fr)}
.g4{grid-template-columns:minmax(0,1fr)}
@media(min-width:640px){.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.g3{grid-template-columns:repeat(2,minmax(0,1fr))}.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:980px){.g3{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}}
.split{display:grid;gap:clamp(28px,4vw,60px);align-items:center;grid-template-columns:1fr}
@media(min-width:900px){
  .split{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .split--wide-l{grid-template-columns:minmax(0,1.22fr) minmax(0,.78fr)}
  .split--wide-r{grid-template-columns:minmax(0,.78fr) minmax(0,1.22fr)}
  .split--flip>*:first-child{order:2}
}

/* --------------------------------------------------------------- chips ---- */
.chips{display:flex;flex-wrap:wrap;gap:9px;padding:0;list-style:none}
.chip{display:inline-flex;align-items:center;gap:8px;padding:9px 15px;border-radius:var(--pill);
  background:var(--surface);border:1px solid var(--line);font-size:16px;font-weight:600;color:var(--text);
  transition:border-color var(--dur) var(--ease),background var(--dur) var(--ease)}
.chip:hover{border-color:var(--cta);background:var(--cta-tint)}
.chip--solid{background:var(--primary-subtle);border-color:transparent;color:var(--primary)}
.section--dark .chip{background:var(--dark-3);border-color:var(--dark-line);color:var(--on-dark)}

/* ------------------------------------------------------- image slots ------ */
.imgph{position:relative;display:block;width:100%;background:var(--steel);border-radius:var(--r);
  overflow:hidden;color:var(--muted)}
.imgph img{width:100%;height:100%;object-fit:cover}
.imgph--notch{clip-path:polygon(0 0,calc(100% - 22px) 0,100% 22px,100% 100%,0 100%);border-radius:0}
.ratio-16x9{aspect-ratio:16/9}.ratio-4x3{aspect-ratio:4/3}.ratio-1x1{aspect-ratio:1/1}
.ratio-3x4{aspect-ratio:3/4}.ratio-3x2{aspect-ratio:3/2}

/* ------------------------------------------------------------- motion ----- */
.reveal{opacity:0;transform:translateY(18px);
  transition:opacity var(--dur-slow) var(--ease),transform var(--dur-slow) var(--ease)}
.reveal.in{opacity:1;transform:none}
.reveal.d1{transition-delay:.08s}.reveal.d2{transition-delay:.16s}
.reveal.d3{transition-delay:.24s}.reveal.d4{transition-delay:.32s}
/* one-shot clip reveal for images — fires from the same observer */
.wipe{clip-path:inset(0 0 100% 0);transition:clip-path .8s var(--ease)}
.wipe.in{clip-path:inset(0 0 0 0)}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition:none!important}
  .reveal{opacity:1;transform:none}
  .wipe{clip-path:none}
}

/* ======================================================================== */
/*  SHARED CHROME — identical on every page (navbar, call bar, to-top,      */
/*  footer). Built once here so the four pages cannot drift.                */
/* ======================================================================== */

/* --- topbar: the number is ALWAYS visible, never behind the hamburger --- */
.topbar{background:var(--dark);color:var(--dark-muted);font-size:15.5px}
.topbar .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;
  min-height:var(--tap);flex-wrap:wrap}
.topbar a{color:#fff;text-decoration:none;display:inline-flex;align-items:center;gap:9px;
  min-height:var(--tap);font-weight:600}
.topbar a:hover{color:var(--cta-light)}
.topbar svg{width:17px;height:17px;stroke:var(--cta-light);flex:none}
.topbar .tb-note{display:none}
@media(min-width:760px){.topbar .tb-note{display:inline-flex;align-items:center;gap:9px}}

/* --- header --- */
header{position:sticky;top:0;z-index:120;background:var(--surface);
  border-bottom:1px solid var(--line);transition:box-shadow var(--dur) var(--ease)}
header.stuck{box-shadow:0 6px 20px rgba(16,25,35,.09)}
.navrow{display:flex;align-items:center;gap:18px;min-height:74px}
.brand{display:inline-flex;align-items:center;gap:11px;text-decoration:none;margin-right:auto}
.brand .bmark{width:40px;height:40px;flex:none;border-radius:var(--r-sm);background:var(--primary);
  display:grid;place-items:center}
.brand .bmark svg{width:23px;height:23px;stroke:var(--cta-light)}
.brand b{font-size:20px;font-weight:800;color:var(--head);letter-spacing:-.02em;line-height:1.1}
@media(max-width:400px){
  /* the lockup alone was 229px, overflowing a 320px viewport once the burger
     and gutters were counted. */
  .brand b{font-size:16.5px}.brand span{font-size:11px}
  .brand .bmark{width:34px;height:34px}.brand .bmark svg{width:19px;height:19px}
  .navrow{gap:10px}
}
.brand span{display:block;font-size:12.5px;font-weight:600;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.nav{display:none;gap:4px;list-style:none}
.nav a{display:inline-flex;align-items:center;min-height:var(--tap);padding:0 11px;
  font-size:16.2px;font-weight:600;color:var(--text);text-decoration:none;border-radius:var(--r-sm);
  white-space:nowrap;
  transition:color var(--dur) var(--ease),background var(--dur) var(--ease)}
.nav a:hover,.nav a[aria-current="page"]{color:var(--primary);background:var(--primary-subtle)}
.navcall{display:none}
.navcall span{white-space:nowrap}
/* raised from 1040: with four items plus the brand and the call button, the bar
   only genuinely fits from ~1120px. Below that the burger is the honest answer. */
@media(min-width:1120px){.nav{display:flex}.navcall{display:inline-flex}
  .brand b{white-space:nowrap}}
/* flex:none is load-bearing — as a flex item in .navrow the burger shrank to 38px
   wide at tablet widths, i.e. under the 44px floor for the ONE control that opens
   the menu. Width alone does not protect a flex item. */
.burger{display:inline-grid;place-items:center;flex:none;
  width:var(--tap);height:var(--tap);min-width:var(--tap);
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);cursor:pointer}
.burger svg{width:24px;height:24px;stroke:var(--head)}
@media(min-width:1120px){.burger{display:none}}
.mobmenu{display:none;border-top:1px solid var(--line);background:var(--surface)}
.mobmenu.open{display:block}
.mobmenu ul{list-style:none;padding:10px 0 18px}
.mobmenu a{display:flex;align-items:center;min-height:52px;font-size:17.5px;font-weight:600;
  color:var(--text);text-decoration:none;border-bottom:1px solid var(--line)}
.mobmenu a:hover{color:var(--cta-ink)}

/* --- sticky mobile call bar (C2: class matches (call|tel|phone|anruf) + position:fixed) --- */
.callbar{position:fixed;left:0;right:0;bottom:0;z-index:150;display:none;
  background:var(--surface);border-top:1px solid var(--line);
  box-shadow:0 -6px 22px rgba(16,25,35,.15);padding:9px 14px calc(9px + env(safe-area-inset-bottom))}
.callbar a{display:flex;align-items:center;justify-content:center;gap:11px;min-height:52px;
  border-radius:var(--r-sm);background:var(--cta);color:#fff;text-decoration:none;
  font-size:18.5px;font-weight:800;letter-spacing:.01em}
.callbar a svg{width:22px;height:22px;stroke:#fff;flex:none}
@media(max-width:1119px){.callbar{display:block}body{padding-bottom:84px}}

/* --- scroll to top --- */
.totop{position:fixed;right:16px;bottom:96px;z-index:140;width:48px;height:48px;
  display:grid;place-items:center;border:1px solid var(--line);border-radius:50%;
  background:var(--surface);color:var(--primary);cursor:pointer;
  opacity:0;visibility:hidden;transform:translateY(9px);
  transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease),
             visibility var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.totop.show{opacity:1;visibility:visible;transform:none}
.totop:hover{box-shadow:var(--sh);border-color:var(--line-strong)}
.totop svg{width:22px;height:22px;stroke:currentColor}
@media(min-width:1120px){.totop{bottom:24px}}

/* --- footer --- */
footer{background:var(--dark);color:var(--dark-muted);padding-top:clamp(48px,5.4vw,76px)}
footer h3{color:#fff;font-size:17px;letter-spacing:.09em;text-transform:uppercase;margin-bottom:17px}
.fgrid{display:grid;gap:34px clamp(24px,3vw,48px);grid-template-columns:1fr}
@media(min-width:700px){.fgrid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1020px){.fgrid{grid-template-columns:1.4fr 1fr 1fr 1.1fr}}
footer ul{list-style:none}
/* 44px floor on footer links too: A11 only WARNS on non-call controls, but this
   audience is older and often one-handed on a phone, so the floor is worth the height. */
footer li{margin-bottom:0}
footer a{color:var(--dark-muted);text-decoration:none;
  display:inline-flex;align-items:center;min-height:var(--tap)}
footer a:hover{color:var(--cta-light);text-decoration:underline}
footer .ftel{display:inline-flex;align-items:center;gap:11px;min-height:var(--tap);
  color:#fff;font-size:21px;font-weight:800;font-variant-numeric:tabular-nums}
footer .ftel svg{width:21px;height:21px;stroke:var(--cta-light);flex:none}
.fbase{margin-top:clamp(34px,4vw,52px);border-top:1px solid var(--dark-line);
  padding:22px 0 26px;display:flex;flex-wrap:wrap;gap:10px 22px;
  align-items:center;justify-content:space-between;font-size:15.5px}
.fbase ul{display:flex;flex-wrap:wrap;gap:2px 20px}
.fbase li{margin:0}
.fbase a{min-height:var(--tap)}

/* skip link — visible only on focus, above the sticky header */
.skip{position:absolute;left:-9999px;top:0;z-index:400;background:var(--cta);color:#fff;
  padding:13px 20px;font-weight:700;text-decoration:none;border-radius:0 0 var(--r-sm) 0}
.skip:focus{left:0;color:#fff}

/* footer lockup reuses .brand with dark-band colours */
.brand--foot b{color:#fff}
.brand--foot span span{color:var(--dark-muted)}
.brand--foot .bmark{background:var(--dark-2);border:1px solid var(--dark-line)}

/* ======================================================================== */
/*  Two late primitives, both from the hotlock/dexon reads.                 */
/* ======================================================================== */

/* THE KEYPAD GRID — the layout lines ARE the design. One `gap:1px` over a line-
   coloured ground, cells painted in the surface colour, so the rules are drawn by
   the gap rather than by per-cell borders (the source hand-tuned border-width per
   cell at every breakpoint; this needs no arithmetic and reflows for free).
   Reads unmistakably as security hardware — the closest thing this style has to a
   trade-specific structure. Also the mechanism behind the segmented assurance slab:
   three cells at gap:2px read as ONE slab, not three cards. */
.keypad{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}
.keypad>*{background:var(--surface);padding:clamp(20px,2.2vw,30px);margin:0}
.keypad--tight{gap:2px;background:var(--line-strong);border:0}
.keypad--cta{gap:2px;background:var(--cta-hover);border:0}
.keypad--cta>*{background:var(--cta);color:#fff}
.keypad--cta>* :where(h2,h3,h4,.h3,strong){color:#fff}
.section--dark .keypad{background:var(--dark-line);border-color:var(--dark-line)}
.section--dark .keypad>*{background:var(--dark-2)}
@media(min-width:640px){.keypad--2{grid-template-columns:repeat(2,1fr)}
  .keypad--3{grid-template-columns:repeat(2,1fr)}.keypad--4{grid-template-columns:repeat(2,1fr)}}
@media(min-width:980px){.keypad--3{grid-template-columns:repeat(3,1fr)}
  .keypad--4{grid-template-columns:repeat(4,1fr)}.keypad--6{grid-template-columns:repeat(6,1fr)}}

/* PHONE NUMBER AS DISPLAY TYPE — no button chrome at all. On a page where the call
   IS the only conversion channel, a huge tappable number outranks another square
   button, and it still clears the 44px target by a wide margin. */
.telbig{display:inline-flex;align-items:center;gap:14px;text-decoration:none;
  font-size:clamp(30px,4.4vw,54px);font-weight:800;line-height:1.06;
  letter-spacing:-.025em;font-variant-numeric:tabular-nums;color:var(--head);
  transition:color var(--dur) var(--ease)}
.telbig:hover{color:var(--cta-ink)}
.telbig svg{width:.72em;height:.72em;flex:none;stroke:var(--cta);stroke-width:1.8}
.section--dark .telbig{color:#fff}
.section--dark .telbig:hover{color:var(--cta-light)}
.section--dark .telbig svg{stroke:var(--cta-light)}

/* THE CALL TAB — a copper block that reads as die-cut out of the image above it:
   one rounded corner, zero top radius, and a negative pull so it bites into the
   photo's bottom edge. From the `blocks` kit, where it is a generic tab; the recon
   flagged it as "the best-shaped button in the corpus and it wants a phone number",
   so here it only ever carries the number. Unlike .btn it has NO hover wipe —
   design-tokens.md §5 forbids animating the CTA; the hover is colour only. */
.calltab{
  display:inline-flex;align-items:center;gap:13px;position:relative;z-index:2;
  margin-top:-34px;padding:20px 30px 20px 26px;
  border-radius:0 var(--r-lg) 0 0;
  background:var(--cta);color:#fff;text-decoration:none;
  font-size:clamp(19px,1.7vw,24px);font-weight:800;letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;min-height:var(--tap);
  transition:background var(--dur) var(--ease)}
.calltab:hover{background:var(--cta-hover);color:#fff}
.calltab svg{width:1.05em;height:1.05em;flex:none;stroke:currentColor;stroke-width:1.9}
.calltab .cap{display:block;font-size:12px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;line-height:1;margin-bottom:4px}

/* TAP-TARGET FLOOR for every click-to-call, whatever a page happens to call it.
   A11 measures the rendered box, so a number styled as pure display type still has
   to clear 44px. min-height only bites on a flex/block box, hence the display switch;
   verified that no tel: link on any page sits inline inside a <p>, so this is safe. */
/* :not(.navcall) is load-bearing: this rule is (0,1,1) and would otherwise beat
   `.navcall{display:none}` (0,1,0) below 1040px, resurrecting the desktop call
   button and pushing the burger off-screen on every phone <=390px. */
a[href^="tel:"]:not(.navcall){display:inline-flex;align-items:center;min-height:var(--tap)}
/* ============================================================================
   stahl-kupfer — SHARED LANGUAGE, PART 2.
   Section + layout primitives and the widget set, all drawn from the recon
   catalogue (`_recon-catalogue.md`). Neutral, composable bricks — never finished
   templates. Every value is a token; nothing here introduces a new theme value.
   ========================================================================== */

/* ------------------------------------------------------------------------- */
/*  A · THE SEAM HELPERS — the overlap grammar, made hard to get wrong.       */
/*  Rule 1 of the style is "every other band has exactly one element crossing  */
/*  its boundary". Two traps make it easy to botch by hand: the crossing       */
/*  element must be the FIRST or LAST child of its band (pulling up a mid-     */
/*  section element just collides with the heading above it), and it needs a   */
/*  z-index or the next band paints over it. These encode both.               */
/* ------------------------------------------------------------------------- */
.seam-up{position:relative;z-index:2;margin-top:calc(-1 * var(--seam,54px))}
.seam-down{position:relative;z-index:2;margin-bottom:calc(-1 * var(--seam,54px))}
.seam-sm{--seam:32px} .seam-lg{--seam:82px}
/* A .seam-down is only half the mechanism and on its own it does NOTHING: the pull is
   smaller than the band's own bottom padding, so the element never reaches the seam.
   It takes THREE classes, and all three are required:
     .section--hang   on the band the element hangs OUT of  (drops its bottom padding)
     .seam-down       on that band's LAST child
     .section--catch  on the following band                 (makes room for the overhang)
   The .seam-up counterpart needs only .seam-up on the next band's FIRST child, plus
   enough bottom padding on the band above for the element to sit in. */
.section--hang{padding-bottom:0}
.section--catch{padding-top:calc(clamp(58px,7.2vw,104px) + var(--seam,54px))}
@media(max-width:699px){
  /* below 700px the bands are too short for a crossing element to read as
     deliberate — it just looks like a mistake. Collapse them. */
  .seam-up,.seam-down{margin-top:0;margin-bottom:0}
  .section--catch{padding-top:clamp(58px,7.2vw,104px)}
}

/* ------------------------------------------------------------------------- */
/*  B · SECTION + LAYOUT PRIMITIVES                                           */
/* ------------------------------------------------------------------------- */

/* B1 · DIPTYCH — zero-gutter two-column band, each half carrying its OWN fill,
   meeting at the exact centreline with no divider. Off-centre text comes from
   asymmetric percentage padding, so it stays optically centred at every width
   with no media query for position. (hotlock: three visually different bands
   from one grid rule.) */
.diptych{display:grid;grid-template-columns:minmax(0,1fr)}
.diptych__half{padding:clamp(38px,5vw,72px) var(--gut)}
@media(min-width:900px){
  .diptych{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .diptych__half{padding:clamp(52px,6vw,92px) 8% clamp(52px,6vw,92px) 14%}
  .diptych__half+.diptych__half{padding:clamp(52px,6vw,92px) 14% clamp(52px,6vw,92px) 8%}
}
.diptych__half--dark{background:var(--dark);color:var(--dark-muted)}
.diptych__half--dark :where(h2,h3,h4){color:#fff}
.diptych__half--steel{background:var(--steel)}
.diptych__half--primary{background:var(--primary);color:var(--on-dark)}
.diptych__half--primary :where(h2,h3,h4){color:#fff}
/* steel ground so a slow or failed image reads as an empty slot, not a black void */
.diptych__half--media{padding:0;position:relative;min-height:320px;background:var(--steel)}
.diptych__half--media img,.diptych__half--media .imgph{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:0}

/* B2 · LEDGER — one hairline frame around rows whose narrow cell is a label tab
   and whose wide cell carries the content. A ruled register reads as a record;
   a card grid reads as marketing. Right for Öffnungszeiten, coverage, liability
   splits, credential lists. (dexon: 20% white label cell / 80% filled cell.) */
.ledger{border:1px solid var(--line);background:var(--surface)}
.ledger__row{display:grid;grid-template-columns:minmax(0,1fr);border-top:1px solid var(--line)}
.ledger__row:first-child{border-top:0}
/* A key is one grid cell, but a long German compound has no break opportunity in it
   ("Verbraucherstreitbeilegung" is 25 characters and no spaces): without this it
   overflows the 180px column, crosses the divider and paints on top of the value's
   second line. hyphens:auto breaks it properly for lang="de"; overflow-wrap is the
   fallback for anything that still will not fit. */
.ledger__k{padding:16px clamp(16px,2vw,24px);font-weight:700;color:var(--head);
  background:var(--surface-alt);font-size:16.5px;display:flex;align-items:center;
  min-width:0;hyphens:auto;overflow-wrap:break-word}
.ledger__v{padding:16px clamp(16px,2vw,24px);color:var(--muted);font-size:16.8px;line-height:1.55}
@media(min-width:700px){
  .ledger__row{grid-template-columns:minmax(180px,.26fr) 1fr}
  .ledger__k{border-right:1px solid var(--line)}
}
.ledger--dark{border-color:var(--dark-line);background:var(--dark-2)}
.ledger--dark .ledger__row{border-top-color:var(--dark-line)}
.ledger--dark .ledger__k{background:var(--dark-3);color:#fff;border-right-color:var(--dark-line)}
.ledger--dark .ledger__v{color:var(--dark-muted)}

/* B3 · SIDE RAIL — a sticky column against a single-file stack. The spine of a
   long service page: the number stays reachable through explanatory copy.
   Single-viewport sticky only — never a multi-viewport scroll sequence. */
.rail{display:grid;gap:clamp(28px,4vw,54px);grid-template-columns:1fr;align-items:start}
@media(min-width:1000px){
  .rail{grid-template-columns:minmax(0,.4fr) minmax(0,1fr)}
  .rail__side{position:sticky;top:108px}
  .rail--right .rail__side{order:2}
}
.rail__main{display:grid;gap:clamp(30px,3.6vw,52px)}

/* B4 · OFFSET GRID — hand-placed masonry rhythm with zero JS and no library.
   Every nth item drops, so the grid reads composed rather than tabulated.
   (ekvator built this with presentational spacer divs; nth-child is cleaner.) */
.offgrid{display:grid;gap:clamp(16px,1.8vw,26px);grid-template-columns:minmax(0,1fr);align-items:start}
@media(min-width:640px){
  .offgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .offgrid>*:nth-child(2n){margin-top:clamp(20px,3vw,44px)}
}
@media(min-width:980px){
  .offgrid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
  .offgrid--3>*:nth-child(2n){margin-top:0}
  .offgrid--3>*:nth-child(3n-1){margin-top:clamp(24px,3.4vw,52px)}
}

/* B5 · PEEK RAIL — user-driven horizontal scroll with the next card bleeding off
   the edge as the affordance. scroll-snap, NOT an auto-carousel: nothing moves
   until the reader moves it, and every card is keyboard-reachable. */
.peek{display:flex;gap:clamp(14px,1.6vw,22px);overflow-x:auto;scroll-snap-type:x mandatory;
  padding-bottom:14px;scrollbar-width:thin}
.peek>*{flex:0 0 clamp(250px,74vw,330px);scroll-snap-align:start}
@media(min-width:760px){.peek>*{flex-basis:330px}}
.peek::-webkit-scrollbar{height:8px}
.peek::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:var(--pill)}
.peek::-webkit-scrollbar-track{background:var(--surface-alt)}

/* B6 · MOSAIC — one tall image beside two stacked halves. An asymmetric 1+2
   composition out of a plain two-column grid. */
.mosaic{display:grid;gap:14px;grid-template-columns:minmax(0,1fr)}
@media(min-width:700px){.mosaic{grid-template-columns:minmax(0,1.25fr) minmax(0,1fr)}}
.mosaic__col{display:grid;gap:14px;grid-template-rows:1fr 1fr}
.mosaic .imgph{height:100%}
.mosaic--flip>*:first-child{order:2}

/* B7 · COMPARE — a static two-up with a shared centre rule. This is the LEGAL
   form of a before/after: the source templates build that with <input
   type="range">, which the validator hard-fails. Nothing here is interactive. */
.compare{display:grid;grid-template-columns:minmax(0,1fr);border:1px solid var(--line);
  background:var(--surface)}
.compare__side{padding:clamp(22px,2.6vw,34px)}
.compare__side+.compare__side{border-top:1px solid var(--line)}
@media(min-width:760px){
  .compare{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .compare__side+.compare__side{border-top:0;border-left:1px solid var(--line)}
}
.compare__tag{display:inline-flex;align-items:center;gap:9px;margin-bottom:13px;
  font-size:12.5px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}
.compare__side--a .compare__tag{color:var(--success)}
.compare__side--b .compare__tag{color:var(--danger)}

/* B8 · TIMELINE — vertical connected steps. Order matters, so a stepper, not a
   grid. The connector is a pseudo-element so no extra markup carries it. */
.timeline{list-style:none;display:grid;gap:clamp(20px,2.4vw,32px);counter-reset:tl}
.timeline>li{position:relative;padding-left:64px;counter-increment:tl}
.timeline>li::before{content:counter(tl,decimal-leading-zero);position:absolute;left:0;top:0;
  width:44px;height:44px;border-radius:50%;display:grid;place-items:center;
  background:var(--surface);color:var(--primary);box-shadow:inset 0 0 0 1.5px var(--line-strong);
  font:800 14px/1 var(--f-num);letter-spacing:.02em}
.timeline>li::after{content:"";position:absolute;left:21.5px;top:52px;bottom:calc(-1 * clamp(20px,2.4vw,32px));
  width:1px;background:var(--line)}
.timeline>li:last-child::after{display:none}
.section--dark .timeline>li::before{background:var(--dark-2);color:var(--cta-light);
  box-shadow:inset 0 0 0 1.5px var(--dark-line)}
.section--dark .timeline>li::after{background:var(--dark-line)}

/* B9 · STEPPER — the horizontal counterpart, for a sibling page that must not
   repeat the timeline's shape. */
.stepper{list-style:none;display:grid;gap:clamp(18px,2vw,26px);grid-template-columns:minmax(0,1fr);
  counter-reset:st}
@media(min-width:820px){.stepper{grid-template-columns:repeat(4,minmax(0,1fr))}}
.stepper>li{counter-increment:st;position:relative;padding-top:56px}
.stepper>li::before{content:counter(st,decimal-leading-zero);position:absolute;left:0;top:0;
  width:42px;height:42px;border-radius:var(--r-sm);display:grid;place-items:center;
  background:var(--primary);color:#fff;font:800 14px/1 var(--f-num)}
.stepper>li::after{content:"";position:absolute;left:52px;top:20px;right:0;height:1px;
  background:var(--line)}
.stepper>li:last-child::after{display:none}
@media(max-width:819px){.stepper>li::after{display:none}}

/* B10 · NOTICE — a real callout, never a paragraph. --danger is the safety
   register (gas, electrical, 110/112); --info carries the copper. */
.notice{border:1px solid var(--danger);border-left-width:5px;background:var(--danger-tint);
  padding:clamp(22px,2.6vw,34px);border-radius:var(--r-sm)}
.notice__head{display:flex;align-items:center;gap:13px;margin-bottom:12px}
.notice__ic{width:40px;height:40px;flex:none;border-radius:50%;display:grid;place-items:center;
  background:var(--danger);color:#fff}
.notice__ic svg{width:22px;height:22px;stroke:currentColor}
.notice h2,.notice h3,.notice .h3{color:var(--danger)}
.notice--info{border-color:var(--cta);background:var(--cta-tint)}
.notice--info .notice__ic{background:var(--cta)}
.notice--info :where(h2,h3,.h3){color:var(--cta-ink)}

/* B11 · STAT RAIL — static figures only. The sources animate these with
   odometers; counters are on the banlist and an invented figure breaks iron
   rule 4, so a figure appears here only if the partner can defend it. */
.stats{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);
  grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:820px){.stats{grid-template-columns:repeat(4,minmax(0,1fr))}}
.stats>div{background:var(--surface);padding:clamp(20px,2.4vw,30px)}
.stats b{display:block;font-size:clamp(28px,3.4vw,40px);font-weight:800;color:var(--primary);
  letter-spacing:-.025em;font-variant-numeric:tabular-nums;line-height:1.05}
.stats span{display:block;margin-top:7px;font-size:16px;color:var(--muted);line-height:1.4}
.section--dark .stats{background:var(--dark-line);border-color:var(--dark-line)}
.section--dark .stats>div{background:var(--dark-2)}
.section--dark .stats b{color:var(--cta-light)}
.section--dark .stats span{color:var(--dark-muted)}

/* B12 · INDEX ROWS — the hover-flood service index. Three independent sources
   converged on this shape, and it is the answer to "never a bare <ul>": a list
   that is also a gallery. Four coordinated changes, all pure CSS. */
.rows{list-style:none;border-top:1px solid var(--line)}
.rows>li{position:relative;isolation:isolate;border-bottom:1px solid var(--line);
  transition:border-color var(--dur) var(--ease)}
.rows__flood{position:absolute;inset:auto 0 0 0;height:0;z-index:-1;background:var(--primary);
  transition:height .34s var(--ease)}
.rows>li:hover .rows__flood,.rows>li:focus-within .rows__flood{height:100%}
.rows__link{display:grid;gap:6px 20px;align-items:center;padding:clamp(20px,2.4vw,30px) 0;
  text-decoration:none;color:inherit;min-height:var(--tap)}
.rows__link::after{content:"";position:absolute;inset:0;z-index:1}
.rows>li:hover :where(.rows__t,.rows__d),.rows>li:focus-within :where(.rows__t,.rows__d){color:#fff}
.rows__t{font-size:clamp(19px,1.6vw,23px);font-weight:700;color:var(--head);
  transition:color var(--dur) var(--ease)}
.rows__d{font-size:16.8px;color:var(--muted);line-height:1.55;max-width:62ch;
  transition:color var(--dur) var(--ease)}
.rows__n{font:800 13px/1 var(--f-num);color:var(--muted);letter-spacing:.08em}
.rows__thumb{position:absolute;right:clamp(16px,2.4vw,36px);top:50%;translate:0 -50%;
  width:180px;aspect-ratio:16/11;overflow:hidden;border-radius:var(--r-sm);
  opacity:0;scale:.86;transition:opacity .3s var(--ease),scale .3s var(--ease);
  pointer-events:none;display:none}
@media(min-width:1040px){
  .rows__thumb{display:block}
  .rows__link{padding-right:230px}
  .rows>li:hover .rows__thumb,.rows>li:focus-within .rows__thumb{opacity:1;scale:1}
}

/* B13 · BAND STRIP — a full-bleed accent rule between sections, carrying the
   service vocabulary. The source MARQUEES this; continuous motion is banned, so
   it is static and reads as a hard divider. */
.strip{background:var(--cta);padding:22px 0}
.strip ul{list-style:none;display:flex;flex-wrap:wrap;justify-content:center;
  align-items:center;gap:12px clamp(16px,2vw,26px)}
.strip li{color:#fff;font-size:clamp(15px,1.5vw,19px);font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;display:inline-flex;align-items:center;gap:12px clamp(16px,2vw,26px)}
.strip li:not(:last-child)::after{content:"";width:26px;height:11px;flex:none;background:#fff;
  opacity:.62;
  clip-path:polygon(0 100%,0 42%,20% 42%,20% 8%,40% 8%,40% 58%,60% 58%,60% 22%,80% 22%,80% 70%,100% 70%,100% 100%)}
.strip--dark{background:var(--dark)}
.strip--steel{background:var(--steel)}
.strip--steel li{color:var(--head)}
.strip--steel li:not(:last-child)::after{background:var(--cta);opacity:1}

/* ------------------------------------------------------------------------- */
/*  C · WIDGETS                                                              */
/* ------------------------------------------------------------------------- */

/* C1 · numeral pill — the step marker. Reads better than a bare digit or a
   filled circle, and it is the right shape for a numbered flow. */
.numpill{display:inline-flex;align-items:center;justify-content:center;min-width:44px;
  min-height:30px;padding:5px 14px;border-radius:var(--pill);border:1px solid var(--line);
  background:var(--surface-alt);color:var(--primary);font:800 13.5px/1 var(--f-num);
  letter-spacing:.04em}
.numpill--cta{background:var(--cta);border-color:var(--cta);color:#fff}
.section--dark .numpill{background:var(--dark-2);border-color:var(--dark-line);color:var(--cta-light)}

/* C2 · seal — a circular CREDENTIAL mark, sized to straddle a band seam.
   Deliberately NOT a rating badge: the sources put "4.5 (1,200 reviews)" in this
   slot, and a rating may only appear if it is real, attributable and evidenced. */
.seal{width:clamp(120px,12vw,148px);height:clamp(120px,12vw,148px);border-radius:50%;
  background:var(--cta);color:#fff;display:grid;place-content:center;justify-items:center;
  gap:3px;text-align:center;padding:12px;border:6px solid var(--surface)}
.seal svg{width:25px;height:25px;stroke:currentColor}
.seal b{font-size:12.5px;font-weight:800;line-height:1.15}
/* opacity:.92 white over copper blends to #f9f1ed = 4.35:1 — under AA for 11px text, and
   axe flags every seal on the page. Same trap the .callbtn .cap comment names: never dim
   text on the accent fill to "soften" it. Full white is 4.86:1. */
.seal i{font-style:normal;font-size:11px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase}
.seal--on-dark{border-color:var(--dark)}
.seal--primary{background:var(--primary)}

/* C3 · caption chip welded to an image corner — one widget, two content types
   (a Stadtteil on a job photo, a date on an article), which is where a gallery
   and a blog grid start reading as one family. */
.capchip{position:absolute;left:0;bottom:0;z-index:2;background:var(--surface);color:var(--head);
  padding:9px 15px;font-size:14px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  border-radius:0 var(--r-sm) 0 0;max-width:calc(100% - 22px)}
.capchip--cta{background:var(--cta);color:#fff}
.capchip--tr{left:auto;right:0;bottom:auto;top:0;border-radius:0 0 0 var(--r-sm)}

/* C4 · avatar cluster ending in a quote mark — the only social-proof widget in
   the whole corpus that carries zero numbers and zero stars, which is why it is
   the one that ports into a German trade site untouched. */
.avatars{display:flex;align-items:center;flex-wrap:wrap}
.avatars img,.avatars .avatars__q{width:56px;height:56px;border-radius:50%;object-fit:cover;
  border:3px solid var(--surface);flex:none}
.avatars>*+*{margin-left:-15px}
.avatars__q{background:var(--cta);color:#fff;display:grid;place-items:center}
.avatars__q svg{width:22px;height:22px}
.section--dark .avatars img,.section--dark .avatars .avatars__q{border-color:var(--dark)}

/* C5 · quote card — a speech card with a tail. No stars, no counts, no rating. */
.quote{position:relative;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);padding:clamp(22px,2.4vw,30px)}
.quote p{font-size:17.2px;line-height:1.62;color:var(--text)}
.quote__by{margin-top:16px;padding-top:14px;border-top:1px solid var(--line);
  font-size:14px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--cta-ink)}
.section--dark .quote{background:var(--dark-2);border-color:var(--dark-line)}
.section--dark .quote p{color:var(--on-dark)}
.section--dark .quote__by{color:var(--cta-light);border-top-color:var(--dark-line)}

/* C6 · flanked hairline — centred text with two rules growing to fill the row.
   Three declarations, and nothing like the usual border-top. */
.hrule{display:flex;align-items:center;gap:18px;font-size:14px;font-weight:800;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.hrule::before,.hrule::after{content:"";flex:1;height:1px;background:var(--line)}
/* the LABEL needs the dark-band colour too, not only the two rules — --muted on --dark
   is 2.49:1. Every widget below that paints text in --muted needs the same partner rule. */
.section--dark .hrule{color:var(--dark-muted)}
.section--dark .hrule::before,.section--dark .hrule::after{background:var(--dark-line)}

/* C7 · colophon chip — a bordered circle holding one letter, beside a name.
   Pure editorial garnish; the right home for a Meister/Innung mark in a hero. */
.colophon{display:inline-flex;align-items:center;gap:10px;font-size:12.5px;font-weight:700;
  letter-spacing:.05em;color:var(--muted)}
.colophon i{font-style:normal;width:22px;height:22px;flex:none;border-radius:50%;
  border:1.6px solid var(--line-strong);display:grid;place-items:center;
  font:800 11px/1 var(--f-num);color:var(--primary)}
.section--dark .colophon{color:var(--dark-muted)}
.section--dark .colophon i{border-color:var(--dark-line);color:var(--cta-light)}

/* C8 · double-framed card — an outer tinted frame at 10px padding with an inner
   hairline. Two rules, and every card stops looking flat. */
.framed{background:var(--surface-alt);border-radius:var(--r-lg);padding:10px}
.framed__in{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:clamp(20px,2.2vw,28px);height:100%}
.section--dark .framed{background:var(--dark-3)}
.section--dark .framed__in{background:var(--dark-2);border-color:var(--dark-line)}

/* C9 · expanding hover pill — an arrow square at rest that grows into a labelled
   button. max-width (not width) so there is no layout shift. */
.hpill{display:inline-flex;align-items:center;gap:0;min-height:var(--tap);
  border-radius:var(--pill);background:var(--primary);color:#fff;text-decoration:none;
  padding:6px;overflow:hidden}
.hpill__ic{width:32px;height:32px;flex:none;display:grid;place-items:center}
.hpill__ic svg{width:18px;height:18px;stroke:currentColor}
.hpill__t{max-width:0;opacity:0;white-space:nowrap;overflow:hidden;font-weight:700;font-size:16px;
  transition:max-width .34s var(--ease),opacity .28s var(--ease),padding .34s var(--ease);padding:0}
.hpill:hover,.hpill:focus-visible{color:#fff;background:var(--cta)}
.hpill:hover .hpill__t,.hpill:focus-visible .hpill__t{max-width:16rem;opacity:1;padding:0 12px 0 4px}

/* C10 · eyebrow variant D — a bordered chip with a solid accent disc flush in
   its left cap. Gives a section a coloured anchor without a coloured band. */
.eyebrow--cap{border:1.5px solid var(--line);border-radius:var(--pill);
  padding:4px 16px 4px 4px;color:var(--primary);letter-spacing:.11em;gap:11px}
.eyebrow--cap::before{width:28px;height:28px;border-radius:50%;background:var(--cta);
  clip-path:none}
.section--dark .eyebrow--cap{border-color:var(--dark-line);color:#fff}

/* C11 · crushed numeral — one deliberate break in the type system. A token-driven
   design with no exceptions reads machine-made; exactly one exception reads
   art-directed. Use it ONCE per page, never twice.
   THE NUMERAL IS DECORATION, AND THE MARKUP HAS TO SAY SO. At --line it carries
   1.41:1 (--dark-4 on --dark: 1.33:1) and could never clear AA as text — 3:1 is the
   floor even for display sizes. So the digits are passed in `data-n` and painted by the
   pseudo-element, the same move .timeline already makes for its step counters: a graphic
   in numeral form, not text a reader must decipher. Meaning belongs in the heading
   beside it. Written as <span class="bignum" data-n="01" aria-hidden="true"></span>. */
.bignum{font-size:clamp(56px,8vw,104px);font-weight:800;line-height:.72;letter-spacing:-.04em;
  color:var(--line);font-variant-numeric:tabular-nums;display:block}
.bignum::before{content:attr(data-n)}
.bignum--cta{color:var(--cta)}
.section--dark .bignum{color:var(--dark-4)}

/* C12 · hours rows — opening times as a real record, not a paragraph. */
.hours{list-style:none;display:grid;gap:0}
.hours li{display:flex;justify-content:space-between;gap:18px;padding:12px 0;
  border-bottom:1px solid var(--line);font-size:16.8px}
.hours li:last-child{border-bottom:0}
.hours b{color:var(--head);font-weight:700}
.hours span{color:var(--muted);text-align:right}
.section--dark .hours li{border-bottom-color:var(--dark-line)}
.section--dark .hours b{color:#fff}
.section--dark .hours span{color:var(--dark-muted)}

/* C13 · breadcrumbs */
.crumbs{list-style:none;display:flex;flex-wrap:wrap;align-items:center;gap:2px 4px;font-size:15.5px}
.crumbs li{display:inline-flex;align-items:center;gap:4px;color:var(--muted)}
.crumbs li+li::before{content:"";width:6px;height:6px;flex:none;border-right:1.5px solid var(--line-strong);
  border-top:1.5px solid var(--line-strong);rotate:45deg;margin-right:4px}
.crumbs a{display:inline-flex;align-items:center;min-height:var(--tap);min-width:var(--tap);
  color:var(--muted);text-decoration:none}
.crumbs a:hover{color:var(--cta-ink);text-decoration:underline}
.crumbs [aria-current="page"]{color:var(--head);font-weight:700}

/* C14 · in-page index — the sticky rail's table of contents. */
.toc{list-style:none;display:grid;gap:2px;border-left:2px solid var(--line)}
.toc a{display:flex;align-items:center;min-height:var(--tap);padding:6px 0 6px 16px;
  margin-left:-2px;border-left:2px solid transparent;color:var(--muted);text-decoration:none;
  font-size:16.5px;transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.toc a:hover,.toc a[aria-current="true"]{color:var(--primary);border-left-color:var(--cta);
  font-weight:700}

/* C15 · tab switcher — buttons with the real roles. The sources build this from
   radio inputs; any <input> is a hard validator fail, and site.js already wires
   [data-switcher] with roving focus. */
.tabs{display:grid;gap:clamp(24px,3vw,44px);grid-template-columns:minmax(0,1fr);align-items:start}
@media(min-width:900px){.tabs{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}}
.tabs__list{display:grid;gap:0;list-style:none}
/* The source dims the inactive tab with opacity:.45. That cannot be rescued: --muted at
   .45 over white is 2.07:1, and even at .9 it is still 4.63:1 on --steel — an opacity
   veil scales the CONTRAST down along with the colour, so no value both dims visibly and
   clears AA on every band this style ships. The inactive state is therefore carried by
   colour and border, the way the rest of the style carries state. */
.tabs__btn{display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
  padding:18px 0;border-bottom:1px solid var(--line);color:inherit;font:inherit;
  min-height:var(--tap);transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.tabs__btn:hover{border-bottom-color:var(--line-strong)}
.tabs__btn[aria-selected="true"]{border-bottom-color:var(--cta);border-bottom-width:2px}
.tabs__btn b{display:block;font-size:clamp(18px,1.5vw,21px);font-weight:700;color:var(--muted);
  margin-bottom:5px;transition:color var(--dur) var(--ease)}
.tabs__btn:hover b,.tabs__btn[aria-selected="true"] b{color:var(--head)}
.tabs__btn span{display:block;font-size:16.4px;color:var(--muted);line-height:1.55}
.section--dark .tabs__btn b,.section--dark .tabs__btn span{color:var(--dark-muted)}
.section--dark .tabs__btn:hover b,.section--dark .tabs__btn[aria-selected="true"] b{color:#fff}
.tabs__panel[hidden]{display:none}

/* C16 · FAQ accordion — native <details>, so it works with JS off; site.js only
   adds the one-open-at-a-time behaviour. */
.faq{display:grid;gap:12px}
.faq details{border:1px solid var(--line-strong);border-radius:var(--r-sm);
  background:var(--surface);overflow:hidden}
.faq summary{display:flex;align-items:center;gap:14px;justify-content:space-between;
  padding:18px clamp(16px,2vw,24px);min-height:var(--tap);cursor:pointer;list-style:none;
  font-size:clamp(17px,1.4vw,19.5px);font-weight:700;color:var(--head)}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"";width:12px;height:12px;flex:none;
  border-right:2px solid var(--cta);border-bottom:2px solid var(--cta);rotate:45deg;
  translate:0 -3px;transition:rotate var(--dur) var(--ease)}
.faq details[open] summary::after{rotate:-135deg;translate:0 3px}
.faq details[open]{border-color:var(--cta)}
.faq__a{padding:0 clamp(16px,2vw,24px) 20px;color:var(--muted);font-size:17px;line-height:1.62}
.section--dark .faq details{background:var(--dark-2);border-color:var(--dark-line)}
.section--dark .faq summary{color:#fff}
.section--dark .faq summary::after{border-color:var(--cta-light)}

/* C17 · check / icon list — the default treatment for factors, symptoms and
   benefits. Never a <ul> with the bullets deleted. */
.iclist{list-style:none;display:grid;gap:clamp(14px,1.7vw,22px)}
.iclist--2{grid-template-columns:minmax(0,1fr)}
@media(min-width:700px){.iclist--2{grid-template-columns:repeat(2,minmax(0,1fr))}}
.iclist li{display:grid;grid-template-columns:auto 1fr;gap:14px;align-items:start}
.iclist__ic{width:34px;height:34px;flex:none;border-radius:50%;display:grid;place-items:center;
  background:var(--primary-subtle);color:var(--primary)}
.iclist__ic svg{width:18px;height:18px;stroke:currentColor;stroke-width:2.2}
.iclist--cta .iclist__ic{background:var(--cta);color:#fff}
.iclist b{display:block;color:var(--head);font-weight:700;font-size:17.5px;margin-bottom:3px}
.iclist p{font-size:16.6px;color:var(--muted);line-height:1.55}
.section--dark .iclist__ic{background:var(--dark-3);color:var(--cta-light)}
.section--dark .iclist b{color:#fff}
.section--dark .iclist p{color:var(--dark-muted)}

/* C18 · media frame — an image with the notch, a seam badge slot and a caption
   chip, so a photo is a composed object rather than a rectangle. */
.frame{position:relative}
.frame__seal{position:absolute;left:50%;bottom:0;translate:-50% 50%;z-index:3}
.frame__seal--r{left:auto;right:clamp(16px,4vw,54px);translate:0 50%}

/* ===== _site.css ===== */
/* ============================================================================
   PART 3 — what THIS build adds to the shared language.

   Three kinds of rule live here and nothing else:
   (a) WordPress integration, because the markup is rendered by WP, not by a
       static builder;
   (b) the TEAL band — the style ships only a near-black `--dark` band, but this
       site's signature hero is a solid brand-colour panel, and a `--primary`
       fill is lighter than `--dark`, so it needs its OWN ink containment set;
   (c) hardening for the failure modes that are invisible in source and only
       show up when the rendered boxes are measured.
   ========================================================================== */

/* --------------------------------------------------------- (a) WordPress --- */

/* WP wraps every rendered template part in its own element. The chrome is
   styled on our own classes, so these wrappers must be inert. */
.wp-block-template-part{display:contents}
/* A pattern body is emitted as raw HTML inside the template; core's group
   wrapper must not add a second container. */
.wp-block-group.is-layout-flow > *{margin-block-start:0}
/* The admin bar is 32px/46px of fixed chrome; the sticky call bar and the
   header both position against the viewport top/bottom. */
body.admin-bar .site-header{top:32px}
@media(max-width:782px){body.admin-bar .site-header{top:46px}}

/* Long German compounds set a block's min-content width; without this a single
   word (Barrierefreiheitsstärkungsgesetz, Zentralschließanlage) makes the whole
   document scroll horizontally at 320px. Headings already carry it; body text,
   list items and table cells did not. */
body{overflow-wrap:break-word}
li,td,th,figcaption,summary,dt,dd{hyphens:auto;overflow-wrap:break-word}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ------------------------------------------------- (b) THE TEAL BAND ------- */
/* `--primary` (#165C50) is a fill, not a dark band: white on it is 7.83:1 but
   the style's `--cta-light` copper is only 3.61:1 there and would ship an
   under-AA eyebrow on every hero. Every descendant that the style re-inks on
   `.section--dark` is re-inked here too, against the measured teal numbers.
   Adding a new primitive means adding it to this list. */
.section--primary,.panel{background:var(--primary);color:var(--on-dark)}
.section--primary h1,.section--primary h2,.section--primary h3,.section--primary h4,
.panel h1,.panel h2,.panel h3,.panel h4{color:#fff}                       /* 7.83:1 */
.section--primary .lead,.panel .lead{color:var(--on-dark)}                /* 6.29:1 */
.section--primary .eyebrow,.panel .eyebrow{color:var(--cta-on-primary)}   /* 4.92:1 */
.section--primary .display,.panel .display{color:#fff}
.section--primary .display .l2,.panel .display .l2{color:var(--cta-on-primary)}
.section--primary .pinrule,.panel .pinrule{border-top-color:var(--cta-on-primary)}
.section--primary .pinrule i,.panel .pinrule i{background:var(--cta-on-primary)}
.section--primary .hrule,.panel .hrule{color:var(--cta-on-primary)}
.section--primary .hrule::before,.section--primary .hrule::after,
.panel .hrule::before,.panel .hrule::after{background:rgba(255,255,255,.28)}
/* Same (0,1,1)-beats-(0,1,0) trap the style documents for `.section--dark a`:
   without the :not() chain this rule repaints every button label copper on a
   copper fill. The exclusion list is load-bearing, not decoration. */
.section--primary a:not(.btn):not(.callbtn):not(.calltab):not(.chip):not(.brand):not(.ftel):not(.telbig),
.panel a:not(.btn):not(.callbtn):not(.calltab):not(.chip):not(.brand):not(.ftel):not(.telbig){
  color:var(--cta-on-primary);text-decoration:underline;text-underline-offset:.18em}
.section--primary .telbig,.panel .telbig{color:#fff}
.section--primary .telbig:hover,.panel .telbig:hover{color:var(--cta-on-primary)}
.section--primary .telbig svg,.panel .telbig svg{stroke:var(--cta-on-primary)}
.section--primary .ibadge,.panel .ibadge{background:rgba(255,255,255,.12);color:#fff}
.section--primary .chip,.panel .chip{background:rgba(255,255,255,.1);
  border-color:rgba(255,255,255,.34);color:#fff}
.section--primary .chip:hover,.panel .chip:hover{background:rgba(255,255,255,.2);
  border-color:#fff;color:#fff}
.section--primary .numpill,.panel .numpill{border-color:rgba(255,255,255,.42);color:#fff}
.section--primary .btn--ghost,.panel .btn--ghost{
  --btn-fg:#fff;--btn-bg-h:#fff;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.44)}
.section--primary .btn--ghost:hover,.panel .btn--ghost:hover{color:var(--primary)}
/* A LIGHT-surfaced primitive nested in the teal band paints its own fill and
   must therefore keep its own dark ink — the band rules above must not leak
   into it. This is the "band ink on a light card" defect, pre-empted. */
.section--primary :where(.card,.notch__in,.keypad__cell,.quote,.notice,.framed,.hours),
.panel :where(.card,.notch__in,.keypad__cell,.quote,.notice,.framed,.hours){color:var(--text)}
.section--primary :where(.card,.notch__in,.keypad__cell,.quote,.notice) :where(h2,h3,h4),
.panel :where(.card,.notch__in,.keypad__cell,.quote,.notice) :where(h2,h3,h4){color:var(--head)}
.section--primary :where(.card,.notch__in,.keypad__cell,.quote,.notice) p,
.panel :where(.card,.notch__in,.keypad__cell,.quote,.notice) p{color:var(--muted)}
.section--primary :where(.card,.notch__in,.keypad__cell) a:not(.btn):not(.chip),
.panel :where(.card,.notch__in,.keypad__cell) a:not(.btn):not(.chip){color:var(--primary)}
/* A single dark focus ring is 2.17:1 on this fill — invisible. The dual ring
   works because its inner layer is white (7.83:1 here) whatever it floats over. */
.section--primary :where(a,button,summary):focus-visible,
.panel :where(a,button,summary):focus-visible{
  outline:3px solid #fff;outline-offset:2px;box-shadow:0 0 0 6px var(--focus)}

/* `.panel` is the teal card dropped into a LIGHT band — the counterpart of the
   style's "dark phone card in a light band". It is teal by its OWN fill, so it
   receives none of the `.section--*` rules; every one it needs is above. */
.panel{border-radius:var(--r);padding:clamp(26px,3.4vw,46px);position:relative;
  isolation:isolate}
.panel--notch{clip-path:polygon(0 0,calc(100% - 26px) 0,100% 26px,100% 100%,0 100%);
  border-radius:0}
.panel--dark{background:var(--dark)}
.panel--dark .eyebrow{color:var(--cta-light)}          /* 8.22:1 on --dark */
.panel--dark a:not(.btn):not(.callbtn):not(.calltab):not(.chip):not(.telbig){color:var(--cta-light)}
.panel--dark .display .l2{color:var(--cta-light)}
.panel--dark .telbig:hover{color:var(--cta-light)}
.panel--dark .telbig svg{stroke:var(--cta-light)}

/* The brand hue itself, used as a fill. It is lighter again (5.18:1 for white),
   so it carries WHITE text only — never --on-dark (4.16) and never copper. */
.fill-brand{background:var(--primary-2);color:#fff}
.fill-brand :where(h2,h3,h4,p,li,strong,b){color:#fff}

/* ------------------------------------------------------- (c) hardening ----- */

/* A `1fr` or `auto` grid track takes a min-content minimum, so one long German
   compound sets the page's minimum width and the document scrolls sideways at
   320px. The style already uses minmax(0,1fr) in most of its DESKTOP rules;
   what needed hardening is the single-column base declarations.
 *
 * THE TRAP, and it cost a rebuild: those base declarations live inside a media
 * query in the shared sheet, and this file is concatenated AFTER it. An
 * unconditional `.split{grid-template-columns:minmax(0,1fr)}` here therefore
 * beats `@media(min-width:900px){.split{…1fr 1fr}}` at EVERY width — media
 * queries add no specificity, so source order decides — and every two-column
 * split and every sticky rail on the site silently collapsed to one column.
 * The validator passed, axe passed, the responsive sweep passed: a one-column
 * page is not a defect by any measurement, it is just the wrong design.
 * Each override below is therefore scoped to the width band it belongs to. */
.split,.fgrid,.rail,.keypad,.iclist li,.compare,.mosaic{min-width:0}
.split > *,.rail > *,.grid > *,.fgrid > *,.keypad > *,.compare > *{min-width:0}
.iclist li{grid-template-columns:auto minmax(0,1fr)}
@media(max-width:899px){ .split{grid-template-columns:minmax(0,1fr)} }
@media(max-width:999px){ .rail{grid-template-columns:minmax(0,1fr)} }
@media(max-width:699px){ .fgrid{grid-template-columns:minmax(0,1fr)} }
@media(min-width:700px) and (max-width:1019px){
  .fgrid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:1020px){
  .fgrid{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1.1fr)}}
@media(min-width:640px) and (max-width:979px){
  .keypad--2,.keypad--3,.keypad--4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:980px){
  .keypad--2{grid-template-columns:repeat(2,minmax(0,1fr))}
  .keypad--3{grid-template-columns:repeat(3,minmax(0,1fr))}
  .keypad--4{grid-template-columns:repeat(4,minmax(0,1fr))}
  .keypad--6{grid-template-columns:repeat(6,minmax(0,1fr))}}

/* A flex item's default min-width:auto stops the brand shrinking and pushes the
   burger off-screen at 360px, with body{overflow-x:hidden} hiding the evidence. */
.brand{min-width:0}
/* superseded below: this pinned the FOOTER lockup too. */
.navrow > .callbtn,.navrow > .burger{flex:0 0 auto}

/* Two independent ways a seam overlap silently renders as ZERO crossing:
   the receiving band's own top padding swallows the pull, and — once that
   padding is gone — the child's negative margin collapses THROUGH the parent
   so the whole band (and its colour boundary) travels up with it. `:has()`
   makes the first fix automatic, `flow-root` kills the collapse. */
.section:has(> .wrap > .seam-up:first-child),
.section:has(> .seam-up:first-child){padding-top:0;display:flow-root}
.section--hang,.section--catch{display:flow-root}

/* A scrollable region must be reachable and named, or axe fails it (serious). */
.scrollx{overflow-x:auto;-webkit-overflow-scrolling:touch}
.scrollx:focus-visible{outline:3px solid var(--focus);outline-offset:2px}

/* Photographs. Self-hosted WebP at three widths with width/height on the tag,
   so there is no layout shift and no request to a third-party host. */
.pic{position:relative;display:block;margin:0}
.pic img{display:block;width:100%;height:auto;border-radius:var(--r);background:var(--steel)}
.pic--notch img{clip-path:polygon(0 0,calc(100% - 26px) 0,100% 26px,100% 100%,0 100%);
  border-radius:0}
.pic--tall img{aspect-ratio:3/4;object-fit:cover}
.pic--wide img{aspect-ratio:16/9;object-fit:cover}
.pic--square img{aspect-ratio:1/1;object-fit:cover}
.pic figcaption{margin-top:10px;font-size:14.5px;line-height:1.5;color:var(--muted)}
.section--dark .pic figcaption{color:var(--dark-muted)}
.section--primary .pic figcaption,.panel .pic figcaption{color:var(--on-dark)}

/* The sticky call bar and the scroll-to-top live INSIDE <footer> so the clone
   gate strips them with the rest of the chrome. The cost of that move is a
   specificity collision nobody sees in source: `footer a` is (0,1,1), exactly
   the same weight as `.callbar a`, and it is declared LATER in the shared
   sheet — so it wins, and the copper button ships --dark-muted text at 3.41:1
   with display:inline-flex instead of a full-width flex row. Both have to be
   re-declared here, after it. */
footer .callbar a{display:flex;align-items:center;justify-content:center;
  min-height:52px;color:#fff;text-decoration:none}
footer .callbar a:hover{color:#fff;text-decoration:none;background:var(--cta-hover)}
footer .callbar a svg{stroke:#fff}
footer .totop{color:var(--primary)}
/* A fixed scroll-to-top floats over every band, so no single ring colour works
   everywhere — an all-teal ring over the teal band is 1:1. Two rings, ~17:1
   against each other, means one of them always contrasts with what is behind. */
.totop:focus-visible{outline:3px solid #fff;outline-offset:0;
  box-shadow:0 0 0 6px var(--focus)}

/* Print: the number is the whole point of the page. */
@media print{
  .site-header,.callbar,.totop,.drawer,.skip{display:none!important}
  .section{padding:18px 0}
  a[href^="tel:"]::after{content:" (" attr(href) ")";font-size:.85em}
}

/* The breadcrumb bar, emitted at the top of <main>. A <nav>, so the clone gate
   strips it with the rest of the chrome and it costs nothing against the 65 %
   ceiling — which is why the trail is here rather than inside a section. */
.crumbbar{background:var(--surface);border-bottom:1px solid var(--line)}
.crumbbar .crumbs{padding:2px 0}
.crumbbar + .section--dark,.crumbbar + .section--primary{border-top:0}

/* A lead photograph inside a section that has no slot of its own — composed
   outside the treatment so it cannot be swallowed by a grid or a list. */
.media-lead{margin:0 0 clamp(26px,3vw,42px)}
.media-lead .pic img{max-height:min(58vh,520px);object-fit:cover}
.section--dark .media-lead .pic img,.section--primary .media-lead .pic img{opacity:.96}

/* A COLOURED HALF IS NOT A COLOURED BAND. `.diptych__half--primary` and
   `--dark` paint their own fill inside a `.section--plain` row, so not one of
   the `.section--primary`/`.section--dark` ink rules above reaches them — and
   the default `.eyebrow` colour is the copper meant for LIGHT grounds, which
   axe measured at serious contrast failure on the teal half. Any element that
   paints its own dark fill has to re-declare the whole descendant set; adding a
   new one means adding it here. */
.diptych__half--primary{color:var(--on-dark)}
.diptych__half--primary .eyebrow{color:var(--cta-on-primary)}     /* 4.92:1 */
.diptych__half--primary .display,
.diptych__half--primary :where(h1,h2,h3,h4){color:#fff}           /* 7.83:1 */
.diptych__half--primary .display .l2{color:var(--cta-on-primary)}
.diptych__half--primary .lead,.diptych__half--primary p{color:var(--on-dark)}
.diptych__half--primary .small{color:var(--on-dark)}
.diptych__half--primary .pinrule i{background:var(--cta-on-primary)}
.diptych__half--primary a:not(.btn):not(.callbtn):not(.calltab):not(.chip):not(.telbig){
  color:var(--cta-on-primary)}
.diptych__half--primary :where(a,button,summary):focus-visible{
  outline:3px solid #fff;outline-offset:2px;box-shadow:0 0 0 6px var(--focus)}

.diptych__half--dark .eyebrow{color:var(--cta-light)}             /* 8.22:1 on --dark */
.diptych__half--dark .display{color:#fff}
.diptych__half--dark .display .l2{color:var(--cta-light)}
.diptych__half--dark .lead,.diptych__half--dark p,.diptych__half--dark .small{color:var(--on-dark)}
.diptych__half--dark .pinrule i{background:var(--cta-light)}
.diptych__half--dark a:not(.btn):not(.callbtn):not(.calltab):not(.chip):not(.telbig){
  color:var(--cta-light)}
.diptych__half--dark :where(a,button,summary):focus-visible{
  outline:3px solid #fff;outline-offset:2px;box-shadow:0 0 0 6px var(--focus)}

/* The steel half is a LIGHT ground: it keeps the site's normal ink, which is
   only worth stating because it sits next to a half that does not. */
.diptych__half--steel .eyebrow{color:var(--cta-ink)}              /* 4.98:1 on --steel */

/* REVEAL, MADE SAFE WITHOUT LOSING THE FADE.
   `.reveal{opacity:0}` plus a JS-only `.in` means that if scripting is off — or
   simply fails — most of the page is present in the DOM and invisible on screen.
   Scoping the starting state to `html.js` inverts the default: the page is
   complete and opaque unless a script has arrived to animate it. That covers a
   failed script as well as a disabled one, which a <noscript> block does not. */
html:not(.js) .reveal{opacity:1;transform:none}
html:not(.js) .wipe{clip-path:none}

/* ===========================================================================
   INK CONTAINMENT — the ONE list of every dark-ish surface, and the ONE list of
   every light-surfaced primitive that may be dropped onto one.

   Three separate defects on this build had a single cause: a primitive whose
   ink is re-declared only for `.section--dark` landing on a surface that is
   dark for a DIFFERENT reason. Measured before this block existed:
     · `.iclist b` at 1.02:1 — --head ink on a `.panel--dark`, invisible
     · `.notice p` at 1.77:1 — --dark-muted inherited onto the notice's own
       light --cta-tint fill inside a dark band
   Both passed the Site Standard validator and axe-core.

   Rule: adding a surface means adding it to DARKISH; adding a primitive that
   paints its own light fill means adding it to LIGHTBOX. Nothing else.

   Specificity note: these selectors tie with the `.section--dark <thing>` rules
   in the shared sheet (:is() takes its most specific argument). The tie is
   broken by source order, and this file is concatenated AFTER the shared sheet
   by tools/build-theme.js — that ordering is load-bearing, not incidental.
   =========================================================================== */

/* --- (1) a LIGHT primitive on a dark-ish surface keeps its own dark ink ---
 *
 * TWO SURFACE FAMILIES, and conflating them cost a round of measured defects.
 *
 *   (1a) A surface this build introduced — the teal band, the panels, the
 *        diptych halves, the chrome. The shared sheet knows nothing about them,
 *        so EVERY light-surfaced primitive dropped on one needs its ink back.
 *
 *   (1b) `.section--dark`, which the shared sheet already handles thoroughly:
 *        it repaints `.card`, `.notch__in`, `.faq details`, `.chip`, `.keypad`
 *        and `.ibadge` as DARK surfaces in that band. Applying the light-ink
 *        containment there does not rescue anything — it forces --head onto a
 *        --dark-2 card and produces exactly the invisible text it was written
 *        to prevent (measured: 1.10:1 on a FAQ summary, 2.04:1 on a card body).
 *        Only the primitives the shared sheet leaves LIGHT belong here.
 *
 * Within each family the heading rule is declared LAST on purpose: the renderer
 * emits `<p class="h3">` for card and cell titles, which matches both the
 * paragraph list and the heading list, and the later rule wins the tie.
 */
:is(.section--primary, .panel, .panel--dark, .fill-brand,
    .diptych__half--dark, .diptych__half--primary, footer, .topbar)
:is(.card, .notch__in, .notice, .quote, .framed__in, .compare, .hours, .stats,
    .ledger:not(.ledger--dark), .faq details){color:var(--text)}
:is(.section--primary, .panel, .panel--dark, .fill-brand,
    .diptych__half--dark, .diptych__half--primary, footer, .topbar)
:is(.card, .notch__in, .notice, .quote, .framed__in, .compare, .hours,
    .ledger:not(.ledger--dark), .faq details)
:is(p, li, .small, .lead, .ledger__v, .faq__a){color:var(--muted)}
:is(.section--primary, .panel, .panel--dark, .fill-brand,
    .diptych__half--dark, .diptych__half--primary, footer, .topbar)
:is(.card, .notch__in, .notice, .quote, .framed__in, .compare, .hours,
    .ledger:not(.ledger--dark), .faq details)
:is(h2, h3, h4, .h3, .stitle, b, strong, summary, .ledger__k, .compare__tag){
  color:var(--head)}

/* (1b) — only what the shared sheet leaves light inside its OWN dark band.
 *   Verified against _core-body.css rather than assumed: it repaints .card,
 *   .notch__in, .faq details, .chip, .keypad, .ibadge, .quote, .hours, .stats
 *   AND .framed__in as dark surfaces in that band, so every one of those is
 *   excluded here. Only .notice, .compare and the light .ledger keep a light
 *   fill on a dark band and therefore need their ink back. */
.section--dark
:is(.notice, .compare, .ledger:not(.ledger--dark)){
  color:var(--text)}
.section--dark
:is(.notice, .compare, .ledger:not(.ledger--dark))
:is(p, li, .small, .lead, .ledger__v){color:var(--muted)}
.section--dark
:is(.notice, .compare, .ledger:not(.ledger--dark))
:is(h2, h3, h4, .h3, .stitle, b, strong, .ledger__k, .compare__tag){color:var(--head)}
/* The safety notice keeps its own register whatever it sits on. */
:is(.section--dark, .section--primary, .panel, .panel--dark, .fill-brand,
    .diptych__half--dark, .diptych__half--primary) .notice :is(h2, h3, .h3){color:var(--danger)}
:is(.section--dark, .section--primary, .panel, .panel--dark, .fill-brand,
    .diptych__half--dark, .diptych__half--primary) .notice--info :is(h2, h3, .h3){color:var(--cta-ink)}
:is(.section--dark, .section--primary, .panel, .panel--dark, .fill-brand,
    .diptych__half--dark, .diptych__half--primary) .notice .iclist__ic{
  background:var(--primary-subtle);color:var(--primary)}

/* --- (2) a dark-ish surface that is NOT a `.section--dark` still needs the
       dark treatment of every ink-carrying primitive it can contain --------- */
:is(.panel, .panel--dark, .fill-brand, .diptych__half--dark, .diptych__half--primary)
:is(.iclist b, .stats b, .rows__t){color:#fff}
:is(.panel, .panel--dark, .fill-brand, .diptych__half--dark, .diptych__half--primary)
:is(.iclist p, .rows__d, .stats span, .hrule){color:var(--on-dark)}
:is(.panel, .panel--dark, .fill-brand, .diptych__half--dark, .diptych__half--primary)
.iclist__ic{background:rgba(255,255,255,.14);color:#fff}
:is(.panel, .panel--dark, .fill-brand, .diptych__half--dark, .diptych__half--primary)
.numpill{border-color:rgba(255,255,255,.42);color:#fff}
/* .btn--ghost is the worst of them: its label colour IS its only colour, so on
   a dark panel it renders text the exact colour of its own background. */
:is(.panel, .panel--dark, .fill-brand, .diptych__half--dark, .diptych__half--primary)
.btn--ghost{--btn-fg:#fff;--btn-bg-h:#fff;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.44)}
:is(.panel, .panel--dark, .fill-brand, .diptych__half--dark, .diptych__half--primary)
.btn--ghost:hover{color:var(--dark)}

/* --- (3) the sticky call bar at 320px ------------------------------------
   "Jetzt anrufen: 0176 4287 6015" at 18.5px/800 plus the icon and the gap has a
   min-content of ~303px, which inside 2×14px padding pushed the document to
   322px in a 320px viewport. body{overflow-x:hidden} hides the symptom; the
   tell is innerWidth !== clientWidth, which no validator looks at. */
@media(max-width:369px){
  .callbar{padding-inline:8px}
  .callbar a{font-size:16.5px;gap:9px}
  .callbar a svg{width:19px;height:19px}
}

/* The nowrap+ellipsis lockup guard belongs to the HEADER brand only. Applied to
   `.brand` globally it also pinned the FOOTER lockup, whose 25-character name at
   800 weight measures 253px — 32px more than the 221px a 320px viewport leaves
   between the gutters, the mark and the gap. That was the entire 322px document
   overflow, and it is invisible in a screenshot because body{overflow-x:hidden}
   swallows the scrollbar: the tell is innerWidth 322 against clientWidth 320.
   The footer has vertical room, so there the name simply wraps. */
.navrow .brand b,.navrow .brand span{overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;display:block}
.brand--foot b,.brand--foot span{white-space:normal;overflow:visible;
  text-overflow:clip;display:block}
.brand--foot{max-width:100%}

/* The brand mark. `fs_icon_mark()` paints the rosette with fill:currentColor and
   the lever with stroke:currentColor, so the glyph follows the lockup's inherited
   ink — which is --head, i.e. near-black on the teal tile: a mark that is
   technically present and effectively invisible. Setting the COLOUR (not just a
   stroke) drives both halves of the glyph at once. White on --primary is 7.83:1. */
.brand .bmark{color:#fff}
.brand .bmark svg{stroke:currentColor;fill:currentColor;width:24px;height:24px}
.brand--foot .bmark{color:var(--cta-light)}          /* 8.22:1 on --dark-2 */

/* ===========================================================================
   FLOW — vertical rhythm for GENERATED markup.

   The shared sheet opens with `*{margin:0}` and the reference pages then set
   every gap by hand in their page-local CSS. That works for four hand-built
   pages and not at all for 41 generated ones: without a default, two paragraphs
   in a hero sit line-on-line and a heading touches the text under it.

   Written as an owl (`A + B`) at ZERO specificity via :where(), so it is the
   floor and never the ceiling — every explicit margin in the shared sheet
   (`.card .h3{margin-bottom:9px}`, `.shead .stitle+.lead{margin-top:15px}`)
   still wins. It beats `*{margin:0}` only because it is declared later, which
   the build's concatenation order guarantees.
   =========================================================================== */
main :where(p, .lead, .small, h2, h3, h4, .h3, .stitle, ul, ol, figure, .grid, .offgrid,
  .keypad, .ledger, .compare, .diptych, .timeline, .stepper, .faq, .rows, .stats,
  .chips, .media-lead, .notice, .framed, .split, .rail, .telbig, .callbtn, .toc)
+ :where(p, .lead, .small, h2, h3, h4, .h3, .stitle, ul, ol, figure, .grid, .offgrid,
  .keypad, .ledger, .compare, .diptych, .timeline, .stepper, .faq, .rows, .stats,
  .chips, .media-lead, .notice, .framed, .split, .rail, .telbig, .callbtn, .toc){
  margin-top:clamp(14px,1.5vw,20px)}

/* A heading needs more air above it than a paragraph does, and less below. */
main :where(p, ul, ol, .grid, .keypad, .ledger, .compare, .timeline, .stepper, .faq, .rows)
+ :where(h2, .stitle){margin-top:clamp(28px,3vw,44px)}
main :where(h2, h3, h4, .h3, .stitle) + :where(p, .lead, ul, ol){margin-top:12px}
main :where(.display) + :where(p, .lead){margin-top:clamp(16px,1.8vw,24px)}
main :where(.eyebrow) + :where(h1, h2, .display, .stitle){margin-top:14px}
main :where(p, .lead) + :where(.grid, .keypad, .ledger, .compare, .diptych, .timeline,
  .stepper, .faq, .rows, .stats, .chips, .offgrid, .rail){margin-top:clamp(22px,2.4vw,34px)}
main :where(.telbig, .callbtn) + :where(p, .small){margin-top:12px}
/* The card and cell primitives carry their own head spacing; keep the paragraph
   rhythm inside them tighter than the section rhythm so a three-line card does
   not read as three separate blocks. */
main :where(.card, .notch__in, .keypad > *, .compare__side, .diptych__half,
  .timeline > li, .stepper > li, .iclist li > div) :where(p) + :where(p){margin-top:9px}

/* A full-bleed hero owns the top of the page: the band's own top padding would
   put an empty strip of the previous ground above it, which reads as a mistake
   rather than as breathing room. */
.hero:has(> .diptych){padding-top:0}
.hero:has(> .diptych) > .wrap{margin-top:0}
.section--plain{background:var(--surface)}

/* ===========================================================================
   BUTTON GEOMETRY — squarer, wider, shorter, with a real edge.

   The style's own rule is "square is structural, round is interactive", and the
   call button had drifted to the far end of it: a full 999px pill, 68px tall,
   with no edge at all. Height came from an unset line-height — the number
   inherited body 1.7, so a 21px figure occupied 36px and the 46px icon disc was
   not even the tallest thing in the box.

   What changes: radius to the structural 4px, a 2px border in a colour that
   clears the 3:1 UI gate against what it sits on, horizontal padding up,
   vertical padding and the leading down. The 44px tap floor is the hard limit
   and the new height (54px) clears it with room.
   =========================================================================== */

/* --- the call button, the most prominent control on every page ------------ */
.callbtn{
  border-radius:var(--r-sm);
  border:2px solid var(--cta-edge);            /* 2.29:1 against the copper fill — a rim you see */
  padding:7px 46px 7px 7px;                   /* wider, and a third less tall */
  gap:16px;
}
.callbtn .disc{width:34px;height:34px;border-radius:var(--r-sm)}
.callbtn .disc svg{width:18px;height:18px}
/* The two lines had no leading of their own and inherited body 1.7 — that, not
   the padding, was where the height went. */
.callbtn .cap{font-size:11px;line-height:1;letter-spacing:.16em}
.callbtn .num{font-size:20px;line-height:1.05;margin-top:4px}
.callbtn:hover{transform:translateY(-1px)}
/* On a white ground the fill is gone, so the edge is the only boundary: copper
   at 4.86:1 well clears the 3:1 a border needs. */
.callbtn.callbtn--dark{border-color:var(--cta-edge)}   /* 11.13:1 on white */
/* On the teal and dark bands a copper-on-copper edge would disappear. */
.section--dark .callbtn,.section--primary .callbtn,.panel .callbtn{
  border-color:rgba(255,255,255,.55)}

/* --- the general button --------------------------------------------------- */
.btn{
  --btn-bd:var(--primary-edge);
  padding:10px 34px;
  border:2px solid var(--btn-bd);
  border-radius:var(--r-sm);
  min-height:var(--tap);
  letter-spacing:.012em;
}
.btn--cta{--btn-bd:var(--cta-edge)}
.btn--lg{padding:13px 44px;font-size:18.5px}
/* The ghost button drew its edge with an inset shadow; now that every button
   has a real border, one mechanism is enough. */
.btn--ghost{box-shadow:none;border-color:var(--line-strong)}
.section--dark .btn--ghost{box-shadow:none;border-color:var(--dark-line-2)}
:is(.panel,.panel--dark,.fill-brand,.diptych__half--dark,.diptych__half--primary,
    .section--primary) .btn--ghost{box-shadow:none;border-color:rgba(255,255,255,.5)}
/* The header call button is the one place the label is a single line, so it can
   afford to be shorter still without going under the tap floor. */
.navcall{padding-block:8px}

/* --- the sticky call bar -------------------------------------------------- */
footer .callbar a{
  border:2px solid var(--cta-edge);
  border-radius:var(--r-sm);
  min-height:54px;
  letter-spacing:.01em;
}

/* --- chips ---------------------------------------------------------------- */
/* Squared to match, and the border moved off the decorative --line (1.15:1 on
   the brushed band — invisible) onto --line-strong. A chip carries no underline,
   so its boundary IS its affordance and has to clear 3:1 on its own. */
.chip{
  border-radius:var(--r-sm);
  border:2px solid var(--line-strong);
  padding:8px 16px;
  min-height:40px;
  font-weight:650;
}
.chip:hover{border-color:var(--cta);background:var(--cta-tint)}
.section--dark .chip{border-color:var(--dark-line-2)}
.section--primary .chip,.panel .chip{border-color:rgba(255,255,255,.5)}
.chips{gap:11px}

/* The copper assurance slab must stay copper on a DARK hero.
   `.section--dark .keypad{background:var(--dark-line)}` is (0,2,0) and silently
   beat `.keypad--cta` (0,1,0), so on the notdienst, problem and kontakt pages —
   every page whose hero is the dark band — the slab that crosses the seam
   rendered dark-on-dark and lost the one thing it is there to do. Legible
   throughout, and invisible as a design element, which is why no contrast or
   layout check ever mentioned it. Doubling the class to (0,3,0) settles it. */
.section--dark .keypad.keypad--cta{background:var(--cta-hover);border:0}
.section--dark .keypad.keypad--cta > *{background:var(--cta);color:#fff}
.section--dark .keypad.keypad--cta > * :where(h2,h3,h4,.h3,p,strong){color:#fff}

/* --- chips, finished ------------------------------------------------------
   Three things the first pass left: the inline pin SVG had no size at all, so
   it fell back to its intrinsic box and rendered five times too large; the chip
   is an <a> and therefore inherited the default underline, which turned a
   compact tag into two underlined lines; and on the dark band both the chip's
   own --dark-3 fill (1.5:1) and its --dark-line border (1.9:1) are effectively
   invisible against --dark, so a chip without an underline had no boundary at
   all. Dropping the underline is only legitimate if the container is
   identifiable without it — hence a light border there, computed, not assumed. */
.chip svg{width:17px;height:17px;flex:none;stroke-width:1.9}
.chip{text-decoration:none;line-height:1.25}
.chip:hover{text-decoration:none}
.section--dark .chip{border-color:rgba(255,255,255,.46);background:var(--dark-3)}
.section--dark .chip:hover{border-color:var(--cta-light);background:var(--dark-4)}

/* ===========================================================================
   .prose — the ONE place on this site where the markup is not generated.

   A post is written by a human in the block editor, so it arrives as core block
   output: `.wp-block-list`, `.wp-block-quote`, `.wp-block-image`,
   `.wp-block-table`, `.wp-block-separator`. The theme dequeues the core block
   stylesheet — 22 kB of rules for blocks this site does not use — so without
   this scope a post would render as unstyled browser defaults: 16px Times, blue
   underlined links, bullet discs.

   Everything below is the design language applied to that markup, from the same
   tokens as the rest of the site. Adding a block type to a post means adding it
   here; a block with no rule falls back to the base type, which is legible but
   generic.
   =========================================================================== */
.prose{font-size:18.5px;line-height:1.75;color:var(--text)}
.prose > * + *{margin-top:clamp(18px,1.9vw,26px)}

.prose :where(h2,h3,h4){color:var(--head);hyphens:manual;overflow-wrap:break-word}
.prose h2{font-size:clamp(25px,2.9vw,34px);font-weight:800;letter-spacing:-.02em;line-height:1.16}
.prose h3{font-size:clamp(20px,1.9vw,24px);font-weight:800;letter-spacing:-.012em;line-height:1.28}
.prose h4{font-size:19px;font-weight:700}
.prose :where(h2,h3,h4) + *{margin-top:12px}
.prose > :where(h2){margin-top:clamp(34px,3.6vw,52px)}
.prose > :where(h3){margin-top:clamp(26px,2.8vw,38px)}

.prose p{max-width:none}
.prose strong,.prose b{color:var(--head);font-weight:700}
.prose a{color:var(--primary);text-decoration:underline;text-decoration-thickness:1px;
  text-underline-offset:.18em}
.prose a:hover{color:var(--cta-ink)}

/* Core emits <ul class="wp-block-list">; the shared sheet strips list markers
   sitewide because every other list on this site is a designed treatment. Inside
   a post a real bullet is correct, so it is put back — as a copper square, which
   is the one place the accent is allowed outside the phone action. */
.prose :where(ul,ol){padding-left:0;list-style:none}
.prose :where(ul,ol) > li{position:relative;padding-left:30px;margin-top:10px}
.prose ul > li::before{content:"";position:absolute;left:2px;top:.62em;width:8px;height:8px;
  background:var(--cta);border-radius:var(--r-xs)}
.prose ol{counter-reset:pr}
.prose ol > li{counter-increment:pr}
.prose ol > li::before{content:counter(pr) ".";position:absolute;left:0;top:0;
  font:800 16px/1.75 var(--f-num);color:var(--cta-ink)}
.prose li > :where(ul,ol){margin-top:8px}

.prose blockquote{margin:0;padding:clamp(18px,2vw,26px) clamp(20px,2.4vw,32px);
  background:var(--surface-alt);border-left:4px solid var(--cta);border-radius:0 var(--r) var(--r) 0}
.prose blockquote p{font-size:19.5px;line-height:1.6}
.prose blockquote cite{display:block;margin-top:10px;font-style:normal;font-size:15.5px;
  font-weight:700;color:var(--muted)}

.prose :where(figure,.wp-block-image){margin:0}
.prose :where(figure,.wp-block-image) img{width:100%;height:auto;display:block;
  border-radius:var(--r);background:var(--steel)}
.prose figcaption{margin-top:10px;font-size:15px;line-height:1.5;color:var(--muted)}

/* A table needs its own scroller on a phone, and a scroller has to be focusable
   and named or it is a serious axe failure. `.wp-block-table` is already a
   <figure>, so the role and the label go on it. */
.prose .wp-block-table{overflow-x:auto}
.prose table{width:100%;border-collapse:collapse;font-size:16.8px;background:var(--surface)}
.prose :where(th,td){padding:12px 16px;border:1px solid var(--line);text-align:left;vertical-align:top}
.prose th{background:var(--surface-alt);color:var(--head);font-weight:700}
.prose td{color:var(--muted)}

.prose hr,.prose .wp-block-separator{border:0;border-top:1px solid var(--line);margin-block:clamp(30px,3.4vw,46px)}
.prose :where(code,kbd,samp){font-family:var(--f-num);font-size:.92em;background:var(--surface-alt);
  padding:2px 6px;border-radius:var(--r-xs)}
.prose pre{overflow-x:auto;padding:18px 20px;background:var(--dark);color:var(--on-dark);
  border-radius:var(--r)}
.prose pre code{background:none;padding:0;color:inherit}
.prose :where(.wp-block-embed,iframe,video){max-width:100%}

/* --- the post list -------------------------------------------------------- */
.blogcard{display:flex;flex-direction:column;padding:0;overflow:hidden}
.blogcard__media{display:block;aspect-ratio:16/9;background:var(--steel);overflow:hidden}
.blogcard__media img{width:100%;height:100%;object-fit:cover;transition:scale var(--dur-slow) var(--ease)}
.blogcard:hover .blogcard__media img{scale:1.04}
.blogcard > :not(.blogcard__media){padding-inline:clamp(18px,2vw,26px)}
.blogcard > :first-child:not(.blogcard__media){padding-top:clamp(18px,2vw,26px)}
.blogcard > :last-child{padding-bottom:clamp(18px,2vw,26px)}
.blogcard__date{font:700 13px/1 var(--f-num);letter-spacing:.09em;text-transform:uppercase;
  color:var(--cta-ink);margin-top:clamp(18px,2vw,26px)}
.blogcard .h3{margin-top:10px}
.blogcard .h3 a{color:var(--head);text-decoration:none}
.blogcard:hover .h3 a{color:var(--primary)}
.blogcard p:not(.blogcard__date){color:var(--muted);font-size:17px;margin-top:9px}

.pager{margin-top:clamp(32px,3.6vw,50px)}
.pager ul{display:flex;flex-wrap:wrap;gap:9px;list-style:none;padding:0}
.pager :where(a,span){display:inline-flex;align-items:center;justify-content:center;
  min-width:var(--tap);min-height:var(--tap);padding:0 16px;border:2px solid var(--line-strong);
  border-radius:var(--r-sm);color:var(--text);text-decoration:none;font-weight:700}
.pager a:hover{border-color:var(--cta);background:var(--cta-tint);color:var(--cta-ink)}
.pager .current{background:var(--primary);border-color:var(--primary);color:#fff}
