@import url("./fonts.css");

/* Single authoritative layer for layout and components. Every colour, spacing, radius,
   shadow and motion value resolves to a semantic token from light.css / dark.css.
   Raw values that remain are geometry or illustration, each documented at its rule. */

:root{
  /* Content measure for the marketing shell. Not a manifest token: the manifest offers
     size.content.default (1280) and layout.marketing.maxWidth (1440); this site's column
     is narrower by composition decision. */
  --wrap:1200px;
  /* One page-padding step per layout size; media queries cannot read token breakpoints. */
  --page-padding:var(--layout-compact-page-padding);
  --section-padding:var(--space-12);
  --section-gutter:var(--layout-compact-gutter);
  /* Glow art direction: two coloured light sources stay anchored in place and breathe
     independently, replacing the single masked brand-gradient band whose screen blend at ~20%
     read as a grey smudge. Strength is per-theme; light keeps a softer wash. */
  --glow-violet:color-mix(in srgb,var(--palette-violet-500) 55%,transparent);
  --glow-rose:color-mix(in srgb,var(--palette-frozen-pink) 42%,transparent);
  --glow-cyan:color-mix(in srgb,var(--palette-frozen-blue) 22%,transparent);
  /* Ambient pulse: deliberately far longer than any UI motion token — this is background
     atmosphere, not feedback, so it must read as slow breathing rather than movement. */
  --glow-drift-duration:10s;
  /* Navigation metrics. JS republishes --nav-height from the rendered bar; these are the
     no-JS fallbacks and the source for every scroll offset. */
  --nav-offset:14px;
  --nav-height:52px;
}
html[data-theme="light"]{--glow-violet:color-mix(in srgb,var(--palette-violet-500) 26%,transparent);--glow-rose:color-mix(in srgb,var(--palette-frozen-pink) 45%,transparent);--glow-cyan:color-mix(in srgb,var(--palette-frozen-blue) 30%,transparent)}
@supports selector(:has(*)){
  html:not([data-theme]):has(#themeLight:checked){--glow-violet:color-mix(in srgb,var(--palette-violet-500) 26%,transparent);--glow-rose:color-mix(in srgb,var(--palette-frozen-pink) 45%,transparent);--glow-cyan:color-mix(in srgb,var(--palette-frozen-blue) 30%,transparent)}
  @media (prefers-color-scheme:light){
    html:not([data-theme]):has(#themeSystem:checked){--glow-violet:color-mix(in srgb,var(--palette-violet-500) 26%,transparent);--glow-rose:color-mix(in srgb,var(--palette-frozen-pink) 45%,transparent);--glow-cyan:color-mix(in srgb,var(--palette-frozen-blue) 30%,transparent)}
  }
}
@media (min-width:768px){:root{--page-padding:var(--layout-medium-page-padding);--section-padding:var(--space-16);--section-gutter:var(--layout-medium-gutter)}}
@media (min-width:1024px){:root{--page-padding:var(--layout-expanded-page-padding);--section-padding:var(--space-24);--section-gutter:var(--layout-expanded-gutter)}}
@media (min-width:1280px){:root{--page-padding:var(--layout-marketing-page-padding);--section-gutter:var(--layout-marketing-gutter)}}

*{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--color-background);color:var(--color-foreground);
  font-family:var(--font-family-website);font-size:17px;line-height:1.6;
  -webkit-font-smoothing:antialiased;overflow-x:hidden;isolation:isolate;
  /* Reflow (WCAG 1.4.10): at 200% text a long German compound must break as a last resort
     rather than widen the page. Only applies when a word cannot fit its line box. */
  overflow-wrap:break-word;
}
h1,h2,h3{font-family:var(--font-family-display);font-weight:var(--font-weight-bold);line-height:1.02;margin:0}
p{margin:0}
a{color:inherit;text-decoration:none}
a:not(.pill):not(.logo):not(.nav-menu-toggle){text-decoration-thickness:var(--stroke-default);text-underline-offset:3px}
.skip-link{
  position:fixed;left:16px;top:12px;z-index:var(--layer-tooltip);
  display:inline-flex;align-items:center;min-height:var(--size-target-web-touch);padding:10px 16px;
  border-radius:var(--radius-md);
  background:var(--gradient-primary-action);color:var(--color-primary-foreground);font:var(--type-label);
  transform:translateY(-160%);transition:transform var(--motion-duration-fast) var(--motion-easing-standard);
}
.skip-link:focus{transform:none}
.wrap{width:min(100% - 2 * var(--page-padding),var(--wrap));margin-inline:auto}
.section{padding-block:var(--section-padding)}
section[id]{scroll-margin-top:calc(var(--nav-offset) + var(--nav-height) + var(--space-4))}
:focus-visible{outline:var(--focus-width) solid var(--color-focus);outline-offset:var(--focus-offset);border-radius:var(--radius-xs)}
.label{color:var(--color-link)}

/* Fortschrittsbalken */
.progress{position:fixed;top:0;left:0;height:var(--size-progress-line);width:100%;transform-origin:0 50%;transform:scaleX(0);background:var(--gradient-live);z-index:var(--layer-sticky);pointer-events:none}

/* Navigation — Frozen marketing glass bar */
.nav{position:fixed;top:var(--nav-offset);left:0;right:0;z-index:var(--layer-sticky);display:flex;justify-content:center;padding-inline:76px;pointer-events:none}
.nav-in{
  pointer-events:auto;position:relative;display:flex;align-items:center;flex-wrap:wrap;
  width:auto;max-width:min(100%,var(--wrap));min-height:52px;gap:var(--space-2);padding:4px 8px 4px 16px;
  border:var(--stroke-default) solid var(--material-frozen-border);border-radius:var(--radius-lg);
  background:linear-gradient(135deg,var(--material-frozen-background-start),var(--material-frozen-background-end));
  box-shadow:inset 0 1px 0 var(--material-frozen-inner-highlight),var(--elevation-sm);
  backdrop-filter:saturate(var(--material-frozen-saturate)) blur(var(--material-frozen-blur));
  -webkit-backdrop-filter:saturate(var(--material-frozen-saturate)) blur(var(--material-frozen-blur));
  transform:translateY(-120%);opacity:0;animation:navIn .9s var(--motion-easing-enter) .3s forwards;
}
/* Frozen Edge: a lighter block-start/inline-start refraction, not a full outline. */
.nav-in::before{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  border-block-start:var(--stroke-default) solid var(--material-frozen-edge);
  border-inline-start:var(--stroke-default) solid var(--material-frozen-edge);
}
@keyframes navIn{to{transform:none;opacity:1}}
.logo{
  display:flex;align-items:center;gap:6px;min-height:var(--size-target-web-touch);
  margin-right:4px;padding-inline:4px;color:var(--color-foreground);
}
/* The delivered artwork keeps its own margin (30 and 77 of 707 units beside the ink); the negative
   inline margins take it out of the layout, so only the visible letters claim space in the bar. */
.logo-mark{--logo-height:44px;display:block;width:auto;height:var(--logo-height);margin-inline:calc(var(--logo-height) * -.14) calc(var(--logo-height) * -.36)}
.logo-on-light{display:none}
html[data-theme="light"] .logo-on-dark{display:none}
html[data-theme="light"] .logo-on-light{display:block}
@supports selector(:has(*)){
  html:not([data-theme]):has(#themeLight:checked) .logo-on-dark{display:none}
  html:not([data-theme]):has(#themeLight:checked) .logo-on-light{display:block}
  @media (prefers-color-scheme:light){
    html:not([data-theme]):has(#themeSystem:checked) .logo-on-dark{display:none}
    html:not([data-theme]):has(#themeSystem:checked) .logo-on-light{display:block}
  }
}
.nav-links{display:flex;gap:var(--space-1)}
.nav-links a{
  display:inline-flex;align-items:center;min-height:var(--size-target-web-touch);
  padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
  color:var(--color-muted-foreground);text-decoration:none;
  transition:color var(--motion-duration-fast) var(--motion-easing-standard),background-color var(--motion-duration-fast) var(--motion-easing-standard);
}
.nav-links a:hover{background:var(--color-secondary-hover);color:var(--color-foreground)}
.nav-links a:active{background:var(--color-secondary-pressed);color:var(--color-foreground)}
.nav-links a[aria-current="true"]{color:var(--color-foreground)}
.nav-menu{display:none}
.nav-menu-toggle{
  display:flex;align-items:center;justify-content:center;gap:var(--space-2);
  min-width:var(--size-target-web-touch);min-height:var(--size-target-web-touch);padding:0 var(--space-3);
  border-radius:var(--radius-md);color:var(--color-foreground);cursor:pointer;list-style:none;
  font:var(--font-weight-label) var(--type-label-size)/var(--type-label-line-height) var(--font-family-website);
}
.nav-menu-toggle::-webkit-details-marker{display:none}
.nav-menu-toggle:hover,.nav-menu[open] .nav-menu-toggle{background:var(--color-secondary-hover)}
/* Burger/close glyph: 18x2 bars at a 6px offset are icon geometry, not spacing. */
.nav-menu-icon,.nav-menu-icon::before,.nav-menu-icon::after{display:block;width:18px;height:2px;border-radius:1px;background:currentColor;transition:transform var(--motion-duration-fast) var(--motion-easing-standard)}
.nav-menu-icon{position:relative}
.nav-menu-icon::before,.nav-menu-icon::after{content:"";position:absolute;left:0}
.nav-menu-icon::before{top:-6px}
.nav-menu-icon::after{top:6px}
.nav-menu[open] .nav-menu-icon{background:transparent}
.nav-menu[open] .nav-menu-icon::before{transform:translateY(6px) rotate(45deg)}
.nav-menu[open] .nav-menu-icon::after{transform:translateY(-6px) rotate(-45deg)}
.nav-menu-panel{
  position:absolute;inset-inline:0;top:calc(100% + var(--space-2));z-index:var(--layer-popover);
  display:flex;flex-direction:column;gap:var(--space-1);
  max-height:calc(100svh - var(--nav-height) - var(--space-12));overflow:auto;padding:var(--space-2);
  border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-lg);
  background:var(--color-surface-elevated);box-shadow:var(--elevation-md);
}
.nav-menu-panel a{
  display:flex;align-items:center;min-height:var(--size-row-comfortable);padding-inline:var(--space-4);
  border-radius:var(--radius-md);color:var(--color-muted-foreground);text-decoration:none;
  font:var(--font-weight-medium) var(--type-body-large-size)/var(--type-body-large-line-height) var(--font-family-website);
}
.nav-menu-panel a:hover{background:var(--color-secondary);color:var(--color-foreground)}
.nav-menu-panel a:active{background:var(--color-secondary-pressed)}
.nav-menu-panel a:focus-visible{outline:var(--focus-width) solid var(--color-focus);outline-offset:calc(-1 * var(--focus-width))}
.nav-menu-panel a[aria-current="true"]{color:var(--color-foreground)}
.nav-links a>span,.nav-menu-panel a>span,.lab-shapes button>span{
  position:relative;display:inline-block
}
.nav-menu-panel a>span,.lab-shapes button>span{min-width:0;max-width:100%;overflow-wrap:anywhere}
.nav-links a>span::after,.nav-menu-panel a>span::after,.lab-shapes button>span::after{
  content:"";position:absolute;inset-inline:0;bottom:calc(0px - var(--space-1) - var(--size-progress-line));height:var(--size-progress-line);
  border-radius:var(--radius-round);background:var(--color-link);opacity:0;
  transition:opacity var(--motion-duration-fast) var(--motion-easing-standard)
}
.nav-links a[aria-current="true"]>span::after,.nav-menu-panel a[aria-current="true"]>span::after,.lab-shapes button[aria-pressed="true"]>span::after{opacity:1}

/* Einstellungen (Darstellung + Sprache) */
.theme-picker{position:fixed;top:var(--nav-offset);right:20px;z-index:var(--layer-popover);display:block}
.theme-toggle{
  position:relative;width:var(--size-target-web-touch);height:var(--size-target-web-touch);
  display:grid;place-items:center;flex:none;padding:0;border-radius:var(--radius-md);
  border:var(--stroke-default) solid var(--material-frozen-border);color:var(--color-foreground);
  background:linear-gradient(135deg,var(--material-frozen-background-start),var(--material-frozen-background-end));
  box-shadow:inset 0 1px 0 var(--material-frozen-inner-highlight),var(--elevation-sm);
  backdrop-filter:saturate(var(--material-frozen-saturate)) blur(var(--material-frozen-blur));
  -webkit-backdrop-filter:saturate(var(--material-frozen-saturate)) blur(var(--material-frozen-blur));
  cursor:pointer;list-style:none;transition:background-color var(--motion-duration-fast) var(--motion-easing-standard);
}
.theme-toggle::-webkit-details-marker{display:none}
.theme-toggle::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  border-block-start:var(--stroke-default) solid var(--material-frozen-edge);
  border-inline-start:var(--stroke-default) solid var(--material-frozen-edge);
}
.theme-toggle:hover,.theme-picker[open] .theme-toggle{background:var(--color-secondary-hover)}
.theme-toggle svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.theme-menu{
  position:absolute;top:calc(100% + var(--space-2));right:0;z-index:var(--layer-popover);
  width:260px;max-width:calc(100vw - var(--space-10));padding:var(--space-3);
  background:var(--color-surface-elevated);border:var(--stroke-default) solid var(--color-border);
  border-radius:var(--radius-lg);box-shadow:var(--elevation-md);
}
.theme-menu[hidden]{display:none}
/* Settings rows: 10px gap/padding is control-internal geometry inside a 44px row. */
.theme-option{
  width:100%;display:flex;align-items:center;gap:10px;min-height:var(--size-target-web-touch);padding:10px;
  border:0;border-radius:var(--radius-md);background:transparent;color:var(--color-foreground);
  font-size:var(--type-body-small-size);line-height:normal;text-align:left;cursor:pointer;
}
.theme-option:hover,.theme-option:focus-visible{background:var(--color-secondary-hover)}
.theme-option input{width:18px;height:18px;accent-color:var(--color-primary)}
.settings-label{display:block;margin:0 0 var(--space-2);color:var(--color-muted-foreground)}
.settings-language{margin-top:var(--space-4);padding-top:14px;border-top:var(--stroke-default) solid var(--color-border)}
/* Segmented language control: 1px gap over a 3px inset frame is control geometry. */
.language-switch{
  display:flex;align-items:center;justify-content:stretch;gap:1px;padding:3px;
  border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-secondary);
}
.language-switch a{
  flex:1;display:flex;align-items:center;justify-content:center;min-height:var(--size-target-web-touch);
  padding:7px 9px;border-radius:var(--radius-sm);text-align:center;font-size:13px;
  color:var(--color-muted-foreground);text-decoration:none;
}
.language-switch a[aria-current="page"]{background:var(--color-surface);color:var(--color-foreground)}

/* Buttons */
.pill{
  display:inline-flex;align-items:center;justify-content:center;min-height:var(--size-target-web-touch);
  font-family:var(--font-family-website);font-size:14.5px;font-weight:var(--font-weight-semibold);
  color:var(--color-primary-foreground);background:var(--gradient-primary-action);
  padding:10px 20px;border-radius:var(--radius-md);border:0;cursor:pointer;text-decoration:none;
  /* Labels wrap inside the control rather than pushing the page wide at 200% text.
     The navigation CTA keeps nowrap: its bar geometry is measured and asserted. */
  max-width:100%;white-space:normal;text-align:center;
  transition:transform var(--motion-duration-standard) var(--motion-easing-standard),box-shadow var(--motion-duration-standard) var(--motion-easing-standard),background-color var(--motion-duration-fast) var(--motion-easing-standard);
}
.nav .pill{white-space:nowrap}
.pill:hover{box-shadow:var(--elevation-sm);transform:translateY(-1px)}
.pill:active{transform:scale(var(--motion-pressed-scale))}
.pill-grad{background:var(--gradient-primary-action);color:var(--color-primary-foreground);box-shadow:var(--elevation-glow-primary)}
.pill-grad:hover{box-shadow:var(--elevation-glow-primary),var(--elevation-md)}
.pill-ghost{background:transparent;color:var(--color-foreground);border:var(--stroke-default) solid var(--color-border-strong)}
.pill-ghost:hover{background:var(--color-secondary-hover);box-shadow:none}
.pill-ghost:active{background:var(--color-secondary-pressed)}
.pill-lg{min-height:var(--size-control-lg);font-size:16px;padding:0 var(--space-8)}
.pill:focus-visible,.nav-links a:focus-visible,.foot-links a:focus-visible,.mailto:focus-visible{outline:var(--focus-width) solid var(--color-focus);outline-offset:3px}

/* HERO.
   overflow-x:clip on the section: the glow field below is laid out edge-to-edge with the
   viewport, so the >1 scale of glowDriftHero pushes its box past both viewport edges for part
   of its cycle and widens documentElement.scrollWidth. Clip only the inline axis — the glow's
   edges are fully transparent under its mask, so nothing visible is lost — and leave the
   block axis visible, since the glow legitimately extends below the section. clip creates no
   scroll container, so position:sticky elsewhere is unaffected (a root-level clip would break it). */
/* Full-height hero: the statement and the particle brand fill a screen, with the copy in
   the lower third above the trailing space. */
.hero{
  position:relative;isolation:isolate;display:flex;align-items:flex-end;justify-content:center;text-align:center;
  /* Reserve the glow's complete footprint so it never paints into the following process section. */
  min-height:calc(100svh + var(--hero-glow-overhang));
  padding-block:calc(var(--nav-offset) + var(--nav-height) + var(--space-10)) calc(clamp(var(--space-12),14svh,150px) + var(--hero-glow-overhang));
  overflow-x:clip;
}
/* Partikel-Moralo im Hero: nur die Wortmarke, kein Labor-Ensemble.
   Nimmt als absolut positionierte Ebene den ganzen Hero ein; ohne WebGL 2 und ohne
   JavaScript verborgen, damit der Hero nie ein leeres Feld oder eine sichtbare Box zeigt. */
/* The overhang matches the glow below the CTA and pushes the process section down by exactly that amount. */
:root{--hero-glow-overhang:calc(min(22svh,220px) + var(--space-1) - clamp(var(--space-12),14svh,150px))}
.hero-brand{position:absolute;inset:0;z-index:-1;opacity:0;animation:fadeUp var(--motion-duration-page) var(--motion-easing-enter) .18s forwards;pointer-events:none}
.hero-brand[hidden]{display:none}
.hero-particles{position:absolute;inset:0;display:block;width:100%;height:100%;touch-action:pan-y}
@media (prefers-reduced-motion:reduce){.hero-brand{animation:none;opacity:1}}

/* Hero glow: starts just below the CTA row while still appearing in the initial viewport.
   Violet and rose sources share one layer and drift slowly (transform only). */
.hero .wrap{position:relative}
.hero .wrap::after{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  top:calc(100% + var(--space-1));inset-inline:calc(-1 * max(var(--page-padding),calc((100vw - var(--wrap)) / 2)));
  height:min(22svh,220px);
  background:
    radial-gradient(closest-side at 34% 55%,var(--glow-violet),color-mix(in srgb,var(--glow-violet) 50%,transparent) 38%,color-mix(in srgb,var(--glow-violet) 15%,transparent) 70%,transparent),
    radial-gradient(closest-side at 68% 45%,var(--glow-rose),color-mix(in srgb,var(--glow-rose) 50%,transparent) 38%,color-mix(in srgb,var(--glow-rose) 15%,transparent) 70%,transparent),
    radial-gradient(closest-side at 52% 70%,var(--glow-cyan),color-mix(in srgb,var(--glow-cyan) 50%,transparent) 38%,color-mix(in srgb,var(--glow-cyan) 15%,transparent) 70%,transparent);
  -webkit-mask:radial-gradient(ellipse 58% 42% at 50% 50%,#000 0%,rgba(0,0,0,.45) 45%,transparent 100%);
  mask:radial-gradient(ellipse 58% 42% at 50% 50%,#000 0%,rgba(0,0,0,.45) 45%,transparent 100%);
}
/* Partikel-Moralo im Hero: nur die Wortmarke, kein Labor-Ensemble.
   Nimmt als absolut positionierte Ebene den ganzen Hero ein; ohne WebGL 2 und ohne
   JavaScript verborgen, damit der Hero nie ein leeres Feld oder eine sichtbare Box zeigt. */
.hero-brand{position:absolute;inset:0;z-index:-1;opacity:0;animation:fadeUp var(--motion-duration-page) var(--motion-easing-enter) .18s forwards;pointer-events:none}
.hero-brand[hidden]{display:none}
.hero-particles{position:absolute;inset:0;display:block;width:100%;height:100%;touch-action:pan-y}
@media (prefers-reduced-motion:reduce){.hero-brand{animation:none;opacity:1}}

/* A single long compound word must break rather than push the page wide at 200% text. */
h1.hero-h{max-width:16ch;margin-inline:auto;color:var(--color-foreground);text-wrap:balance;hyphens:manual}
.word{display:inline-block;overflow:hidden;padding-block:.14em;margin-block:-.14em;vertical-align:bottom;max-width:100%}
.word span{display:inline-block;transform:translateY(105%);animation:wordUp var(--motion-duration-page) var(--motion-easing-enter) forwards}
@keyframes wordUp{to{transform:none}}
.hero-sub{color:var(--color-muted-foreground);max-width:56ch;margin:var(--space-5) auto 0;opacity:0;animation:fadeUp var(--motion-duration-page) var(--motion-easing-enter) .32s forwards}
.hero-cta{display:flex;gap:var(--space-3);justify-content:center;flex-wrap:wrap;margin-top:var(--space-8);opacity:0;animation:fadeUp var(--motion-duration-page) var(--motion-easing-enter) .38s forwards}
@keyframes fadeUp{from{opacity:0;transform:translateY(var(--motion-distance-medium))}to{opacity:1;transform:none}}

/* Abschnittskopf */
.head{max-width:820px}
.head :is(h1,h2){max-width:14ch;margin:0 auto;text-wrap:balance}
.head p{color:var(--color-muted-foreground);margin-top:var(--space-5);max-width:58ch}
.center{margin-inline:auto;text-align:center}
.center p{margin-inline:auto}

/* Leistungen */
/* min() on the track floor: at 200% text a 270px floor exceeds the 320px page column.
   overflow-x:clip (not hidden — it creates no scroll container and keeps position:sticky
   working) stops WebKit adding a child's min-content width to the document scroll width
   while the rendered boxes already fit; the clip margin preserves the card hover shadow. */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(270px,100%),1fr));gap:var(--section-gutter);margin-top:var(--space-10);overflow-x:clip;overflow-clip-margin:var(--space-3)}
.card{
  background:var(--material-standard-background);border:var(--stroke-default) solid var(--material-standard-border);
  border-radius:var(--radius-lg);padding:var(--space-6);position:relative;display:flex;flex-direction:column;min-width:0;
  transition:transform var(--motion-duration-standard) var(--motion-easing-standard),box-shadow var(--motion-duration-standard) var(--motion-easing-standard);
}
.card svg{position:relative;width:var(--icon-lg);height:var(--icon-lg);stroke:var(--color-primary);stroke-width:var(--icon-stroke);fill:none;stroke-linecap:round;stroke-linejoin:round}
.card :is(h2,h3){position:relative;margin-top:var(--space-4)}
.card p{position:relative;color:var(--color-muted-foreground);margin-top:var(--space-2);padding-bottom:var(--space-5)}
/* The tag is a card footer: aligned across the row regardless of copy length. */
/* overflow-wrap:anywhere (not break-word, which never shrinks the intrinsic
   min-content width): at 200% text a one-word tag like "Automatisierung" keeps a
   min-content wider than the 320px card column, so its text overruns the pill.
   'anywhere' lets the intrinsic size drop and the tag wrap inside its card
   without clipping, ellipsis or horizontal scrolling. Normal-size tags never
   reach the break, so their rendering is unchanged. */
.card .tag{position:relative;align-self:flex-start;max-width:100%;margin-top:auto;display:inline-flex;align-items:center;min-height:var(--size-badge-md);padding-inline:var(--space-3);border-radius:var(--radius-round);background:var(--color-muted);color:var(--color-foreground);overflow-wrap:anywhere}
:is(#leistungen,#showcases) .cards{grid-template-columns:minmax(0,1fr)}
/* Leistungen illustrations: every colour resolves from the active theme (light / dark / system). */
.card>.service-illustration{display:block;width:100%;max-width:none;height:auto;aspect-ratio:2/1;align-self:stretch;flex:none;background:var(--color-surface-inset);border:var(--stroke-default) solid var(--material-standard-border);border-radius:var(--radius-md);overflow:hidden;stroke:none;fill:none}
.service-illustration .si-st-violet{stop-color:var(--palette-violet-500)}
.service-illustration .si-st-violet-soft{stop-color:var(--palette-violet-400)}
.service-illustration .si-st-blue{stop-color:var(--palette-frozen-blue)}
.service-illustration .si-st-cyan{stop-color:var(--palette-frozen-cyan)}
.service-illustration .si-st-fade{stop-opacity:0}
.service-illustration .si-glow{opacity:.34}
.service-illustration .si-fx-shadow{flood-color:var(--color-shadow);flood-opacity:.18}
.service-illustration .si-frame{fill:var(--color-surface-elevated);stroke:var(--material-standard-border);stroke-width:1}
.service-illustration .si-screen{fill:var(--color-surface)}
.service-illustration .si-panel{fill:var(--color-surface);stroke:var(--color-border);stroke-width:1}
.service-illustration .si-glass{fill:var(--color-surface-elevated);stroke:var(--color-border);stroke-width:1}
.service-illustration .si-inset{fill:var(--color-surface-inset)}
.service-illustration .si-line{fill:var(--color-border)}
.service-illustration .si-text{fill:var(--color-foreground);opacity:.82}
.service-illustration .si-accent{fill:var(--color-accent)}
.service-illustration .si-primary{fill:var(--color-primary)}
.service-illustration .si-on-primary{fill:var(--color-primary-foreground)}
.service-illustration .si-on-primary-soft{fill:var(--color-primary-foreground);opacity:.6}
.service-illustration .si-dot{fill:var(--color-border-strong);opacity:.55}
.service-illustration .si-glyph{fill:none;stroke:var(--color-brand-signal);stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
.service-illustration .si-glyph-on{fill:none;stroke:var(--color-primary-foreground);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.service-illustration .si-grid{fill:none;stroke:var(--color-border);stroke-width:1;stroke-dasharray:3 4}
.service-illustration .si-wire{fill:none;stroke-width:2;stroke-linecap:round}
.service-illustration .si-ring{fill:none;stroke-width:1.25}
@media (min-width:768px){:is(#leistungen,#showcases) .cards{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* The service cards are editorial articles, not destinations. */
#leistungen .card:hover,#leistungen .card.rv.on:hover{transform:none;box-shadow:none}

/* Shared showcase stage chrome: the former homepage Labor lives on as Showcase 01.
   The stats chip, control bar and segmented picker below are shared by every showcase body. */
/* Live readout: an opaque chip so the numbers stay legible over any particle density. */
.lab-stats{
  position:absolute;left:var(--space-4);bottom:var(--space-4);display:flex;flex-wrap:wrap;gap:var(--space-1) var(--space-4);margin:0;
  padding:var(--space-2) var(--space-3);max-width:calc(100% - 2 * var(--space-4));pointer-events:none;
  border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface-elevated);
}
/* Wrap value under label when a long label (e.g. "Druckiterationen") plus a live number
   no longer fits one line at 320px/200% text (WCAG 1.4.10). */
.lab-stats div{display:flex;flex-wrap:wrap;gap:0 var(--space-2);align-items:baseline;min-width:0}
.lab-stats dt{color:var(--color-muted-foreground);overflow-wrap:anywhere}
.lab-stats dd{margin:0;min-width:0;max-width:100%;overflow-wrap:anywhere;color:var(--color-foreground);font-variant-numeric:tabular-nums}
.lab-engine-value{display:flex;flex-wrap:wrap;gap:0 var(--space-2)}
.lab-controls{display:flex;flex-wrap:wrap;gap:var(--space-3);justify-content:center;align-items:center;margin-top:var(--space-6)}
/* Segmented shape picker: same frame recipe as the language switch, sized for touch. */
.lab-shapes{
  display:flex;flex-wrap:wrap;justify-content:center;gap:var(--space-1);padding:3px;max-width:100%;
  border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-secondary);
}
.lab-shapes button,.lab-pause,.lab-action,.stoff-action{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);min-height:var(--size-target-web-touch);
  padding:0 var(--space-4);border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-muted-foreground);
  font:var(--type-label);cursor:pointer;
  transition:background-color var(--motion-duration-fast) var(--motion-easing-standard),color var(--motion-duration-fast) var(--motion-easing-standard);
}
.lab-shapes button:hover{background:var(--color-secondary-hover);color:var(--color-foreground)}
/* Selected state: foreground text and the same link-colour marker as active navigation. */
.lab-shapes button[aria-pressed="true"]{background:transparent;color:var(--color-foreground);box-shadow:none}
.lab-shapes button[aria-pressed="true"]:hover{background:var(--color-secondary-hover)}
.lab-pause,.lab-action,.stoff-action{border:var(--stroke-default) solid var(--color-border-strong);border-radius:var(--radius-md);color:var(--color-foreground)}
.lab-pause:hover,.lab-action:hover,.stoff-action:hover{background:var(--color-secondary-hover)}
/* .lab-action: a secondary command beside the pause button that stays under reduced motion. */
/* Pause/play glyph: two 3px bars or a CSS triangle — icon geometry, not spacing. */
.lab-pause i{width:10px;height:12px;flex:none;border-inline:3px solid currentColor}
.lab-pause[data-state="paused"] i{width:0;height:0;border-inline:0;border-block:6px solid transparent;border-inline-start:10px solid currentColor}
@media (max-width:767.98px){
  .lab-stats{left:var(--space-3);bottom:var(--space-3);font:var(--type-caption)}
  .lab-stats .lab-engine{display:none}
}
@media (prefers-reduced-motion:reduce){.lab-pause{display:none}}
@media (forced-colors:active){.lab-shapes button[aria-pressed="true"]{outline:2px solid CanvasText}}
.lab-stats[hidden],.lab-controls[hidden]{display:none}

/* Showcase: overview and detail pages share the home chrome. Without the home
   hero the first section has to clear the fixed navigation itself. */
.section.page-intro{padding-block-start:calc(var(--nav-offset) + var(--nav-height) + var(--space-12))}
.showcase-page{min-height:100svh;display:flex;flex-direction:column}
.showcase-page main{flex:1}
/* Showcase cards extend the Leistungen card: same surface, illustration and tag. The whole
   card is the hit target (stretched title link), so unlike the service cards it lifts on hover. */
.showcase-cards{list-style:none;padding:0}
:is(.card-tags,.showcase-tags){display:flex;flex-wrap:wrap;gap:var(--space-2);margin-top:auto;padding:0;list-style:none}
:is(.card-tags,.showcase-tags) .tag{margin-top:0}
.showcase-card h2{position:static}
.showcase-card h2 a{color:var(--color-foreground);text-decoration:none}
.showcase-card h2 a::after{content:"";position:absolute;inset:0;z-index:1;border-radius:inherit}
.showcase-card h2 a:focus-visible{outline:none}
.showcase-card:has(h2 a:focus-visible){outline:var(--focus-width) solid var(--color-focus);outline-offset:var(--focus-offset)}
.showcase-card h2 a:hover{color:var(--color-link)}
@media (hover:hover){.showcase-card:hover{box-shadow:var(--elevation-md);transform:translateY(calc(-1 * var(--motion-hover-lift)))}}
/* Detail page */
.showcase-back{margin-bottom:var(--space-6)}
.showcase-back a{display:inline-flex;align-items:center;gap:var(--space-2);min-height:var(--size-target-web-touch);color:var(--color-link);text-decoration:underline}
.showcase-back a::before{content:"←";display:inline-block;text-decoration:none}
.showcase-back a:hover{color:var(--color-link-hover)}
.showcase-detail .head h1{max-width:20ch;margin-inline:0}
/* Same stage recipe as the Labor; the note is the no-JS / unsupported fallback. */
.showcase-stage{
  position:relative;margin-top:var(--space-10);aspect-ratio:16/9;min-height:260px;max-height:min(72svh,680px);width:100%;
  border:var(--stroke-default) solid var(--material-standard-border);border-radius:var(--radius-2xl);overflow:hidden;
  background:radial-gradient(ellipse 70% 60% at 50% 45%,var(--color-surface),var(--color-surface-inset));
  box-shadow:inset 0 1px 0 var(--material-frozen-inner-highlight),var(--elevation-md);
}
.showcase-canvas{position:absolute;inset:0;display:block;width:100%;height:100%;touch-action:pan-y}
/* Fallback note: flows inside the stage (not absolutely placed) so it can never be clipped at 200% text. */
.showcase-stage:not(.is-live){display:flex;align-items:flex-end;justify-content:center;aspect-ratio:auto;min-height:260px;max-height:none;padding:var(--space-4)}
.showcase-stage-note{position:relative;max-width:52ch;padding:var(--space-3) var(--space-4);border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface-elevated);color:var(--color-muted-foreground);text-align:center;font:var(--type-body-small)}
.showcase-stage.is-live .showcase-stage-note{display:none}
/* Optional still image of the demo for no-JS / unsupported browsers (transparent, token background shows through). */
.showcase-still{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;object-position:50% 30%;padding:var(--space-4)}
.showcase-stage.is-live .showcase-still{display:none}
/* With a still the fallback stage keeps the live proportions; the note still grows it when needed. */
.showcase-stage:has(.showcase-still):not(.is-live){aspect-ratio:16/9}
.showcase-stats[hidden],.showcase-controls[hidden]{display:none}
.showcase-explain{max-width:var(--size-content-reading);margin-top:var(--space-12)}
.showcase-explain h2{font:var(--type-heading-2)}
.showcase-explain p{margin-top:var(--space-4);color:var(--color-muted-foreground)}
/* Showcase 01 · Wachstum: the stage is a drawing surface, so touch strokes paint instead of
   scrolling (the stage is capped at 70svh on small screens, the page stays scrollable around it). */
.growth-canvas{touch-action:none;cursor:crosshair;user-select:none;-webkit-user-select:none}
/* Showcase 03 · Tinte: a mouse stirs on hover; touch stirs while pressed. Vertical swipes still
   scroll the page (pan-y), horizontal strokes stir. */
.ink-canvas{touch-action:pan-y;cursor:crosshair;user-select:none;-webkit-user-select:none}
/* Showcase 06 · Frozen Glass: dragging turns the scene (touch: horizontal only, so vertical
   swipes keep scrolling the page); a click forms the next object. */
.glass-canvas{touch-action:pan-y;cursor:grab;user-select:none;-webkit-user-select:none}
.glass-canvas[data-dragging]{cursor:grabbing}
/* Its readout has a long label and a two-number value: let an item wrap at 200 % text. */
.glass-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
.glass-stats dd{overflow-wrap:anywhere}
/* Showcase 08 · Welt: the mouse steers by its offset from the centre; touch steers with a
   horizontal drag, so vertical swipes keep scrolling the page. */
.world-canvas{touch-action:pan-y;cursor:crosshair;user-select:none;-webkit-user-select:none}
/* The still is a full-bleed landscape, not an object on the stage background. */
.showcase-still.world-still{object-fit:cover;object-position:50% 50%;padding:0}
.world-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Time-of-day slider: a native range input (keyboard, screen readers) on a 44px target. */
.world-time{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.world-time input{width:min(16rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
/* Showcase 10 · Eine Million: a drag throws a mass into the galaxies; vertical swipes keep
   scrolling the page. The still is a full-bleed night sky. */
.million-canvas{touch-action:pan-y;cursor:crosshair;user-select:none;-webkit-user-select:none}
.showcase-still.million-still{object-fit:cover;object-position:50% 50%;padding:0}
.million-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
.million-stats dd{overflow-wrap:anywhere}
/* Showcase 12 · Das erwachende Königreich: grabbing the gear turns it, dragging beside it turns
   the view (touch: vertical swipes keep scrolling the page). */
.kingdom-canvas{touch-action:pan-y;cursor:grab;user-select:none;-webkit-user-select:none}
.kingdom-canvas[data-hover="gear"]{cursor:pointer}
.kingdom-canvas[data-grip]{cursor:grabbing}
.kingdom-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Gear slider: the keyboard and screen-reader twin of the gear on the stage, on a 44px target. */
.kingdom-gear{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.kingdom-gear input{width:min(14rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.lab-action[aria-pressed="true"]{background:var(--color-surface);box-shadow:inset 0 -2px 0 var(--color-primary)}
/* Showcase 09 · Licht: dragging turns the camera (touch: horizontal only, so vertical swipes
   keep scrolling the page). The still is a full-bleed render. */
.light-canvas{touch-action:pan-y;cursor:grab;user-select:none;-webkit-user-select:none}
.light-canvas[data-dragging]{cursor:grabbing}
.showcase-still.light-still{object-fit:cover;object-position:50% 50%;padding:0}
.light-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Lens sliders: native range inputs (keyboard, screen readers) on 44px targets with a live value. */
.light-range{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.light-range input{width:min(10rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.light-range output{min-width:5ch;color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
/* Showcase 13 · Drei Sonnen: a drag that starts on a sun moves it (that touch belongs to the
   canvas); anywhere else vertical swipes keep scrolling the page. The still is full-bleed. */
.suns-canvas{touch-action:pan-y;cursor:default;user-select:none;-webkit-user-select:none}
.suns-canvas[data-hover="sun"]{cursor:grab}
.suns-canvas[data-dragging]{cursor:grabbing}
.showcase-still.suns-still{object-fit:cover;object-position:50% 50%;padding:0}
.suns-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Showcase 14 · Der leere Thron: the mouse moves the candle anywhere on the floor; touch only
   takes a drag that starts on the candle, so vertical swipes keep scrolling the page. */
.throne-canvas{touch-action:pan-y;cursor:crosshair;user-select:none;-webkit-user-select:none}
.throne-canvas[data-hover="candle"]{cursor:grab}
.throne-canvas[data-dragging]{cursor:grabbing}
.throne-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Candle sliders: the keyboard and screen-reader twin of dragging the candle, on 44px targets. */
.throne-range{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.throne-range input{width:min(10rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.throne-range output{min-width:6ch;color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
/* Showcase 15 · Raben statt Regler: a drag that starts on a keep carries a raven (that touch
   belongs to the canvas); anywhere else vertical swipes keep scrolling the page. The readout
   sits below the stage, as in Datenverkehr: the whole stage belongs to the map. The two keep
   pickers and the send button are the keyboard and screen-reader twin of the drag. */
.ravens-canvas{touch-action:pan-y;cursor:default;user-select:none;-webkit-user-select:none}
.ravens-canvas[data-hover="keep"]{cursor:grab}
.ravens-canvas[data-dragging]{cursor:grabbing}
.showcase-still.ravens-still{object-fit:cover;object-position:50% 50%;padding:0}
.lab-stats.ravens-stats{position:static;justify-content:center;width:fit-content;max-width:100%;margin:var(--space-4) auto 0}
.ravens-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
.ravens-seats{display:flex;flex-wrap:wrap;gap:0 var(--space-2)}
.ravens-send{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);max-width:100%}
.ravens-pick{display:inline-flex;flex-wrap:wrap;min-width:0;align-items:center;gap:var(--space-2);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
/* contain:paint: WebKit lets the menulist's inner label (the longest option) spill out as layout
   overflow even after the select shrinks, widening the page at 320px/200% text. */
.ravens-pick select{min-width:0;max-width:100%;contain:paint;min-height:var(--size-target-web-touch);padding:0 var(--space-3);border:var(--stroke-default) solid var(--color-border-strong);border-radius:var(--radius-md);background:var(--color-surface-inset);color:var(--color-foreground);font:var(--type-label);cursor:pointer}
/* Showcase 16 · Anziehung: the mouse grabs the nearest attractor; touch only takes a drag that
   starts on an attractor, so vertical swipes keep scrolling the page. */
.attract-canvas{touch-action:pan-y;cursor:crosshair;user-select:none;-webkit-user-select:none}
.attract-canvas[data-hover="attractor"]{cursor:grab}
.attract-canvas[data-dragging]{cursor:grabbing}
.attract-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Showcase 17 · Noch einmal: a press on a sun aims a push, a press on the timeline strip at the
   bottom scrubs; touch elsewhere keeps scrolling the page. The readout sits below the stage, the
   floor of the stage belongs to the timeline. */
.again-canvas{touch-action:pan-y;cursor:crosshair;user-select:none;-webkit-user-select:none}
.again-canvas[data-hover="sun"]{cursor:grab}
.again-canvas[data-hover="timeline"]{cursor:ew-resize}
.again-canvas[data-dragging]{cursor:grabbing}
.lab-stats.again-stats{position:static;justify-content:center;width:fit-content;max-width:100%;margin:var(--space-4) auto 0}
.again-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Moment slider: the keyboard and screen-reader twin of the timeline strip, on a 44px target. */
.again-range{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.again-range input{width:min(14rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.again-range output{min-width:6ch;color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
/* Showcase 18 · Abweichung: two skies side by side (stacked on narrow stages). Only a touch
   that starts on a body is taken; vertical swipes keep scrolling the page. */
.drift-canvas{touch-action:pan-y;cursor:default;user-select:none;-webkit-user-select:none}
.drift-canvas[data-hover="body"]{cursor:grab}
.drift-canvas[data-dragging]{cursor:grabbing}
.drift-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Nudge size slider: powers of ten, on a 44px target. */
.drift-range{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.drift-range input{width:min(10rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.drift-range output{min-width:8ch;color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
/* Showcase 19 · Nichts ist vorbei: a horizontal drag moves the cutting plane through the life;
   vertical swipes keep scrolling the page. */
.score-canvas{touch-action:pan-y;cursor:ew-resize;user-select:none;-webkit-user-select:none}
.score-canvas[data-dragging]{cursor:grabbing}
.score-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* The scene under the plane, in words: the text twin of the drawing, read out politely. */
.score-caption{max-width:var(--size-content-reading);min-height:calc(3 * 1.5em);margin:var(--space-4) auto 0;color:var(--color-muted-foreground);text-align:center;font:var(--type-body-small)}
.score-caption[hidden]{display:none}
/* Plane and distance sliders: the keyboard and screen-reader twin of dragging, on 44px targets. */
.score-range{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.score-range input{width:min(12rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.score-range output{min-width:8ch;color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
.score-scenes{display:grid;grid-template-columns:minmax(0,1fr);overflow-wrap:anywhere;gap:var(--space-4);margin:var(--space-4) 0 0;padding-inline-start:var(--space-6)}
/* Grid items default to min-width:auto, so a long compound word ("Klingelschild") at 200% text
   pushed each scene past the column. Let the item shrink and break words like the rest of the copy. */
.score-scenes>li{min-width:0;overflow-wrap:break-word;hyphens:auto}
.score-scenes h3{font:var(--type-label);color:var(--color-foreground)}
.score-scenes p{margin-top:var(--space-1)}
/* Showcase 20 · Wie lang war gerade?: the canvas only shows; the experiment is driven by the
   panel below it (start, estimate form, resolution), so it works by keyboard and screen reader.
   The readout sits below the stage so it never covers the grid while a sequence runs. */
.duration-canvas{pointer-events:none}
.lab-stats.duration-stats{position:static;justify-content:center;width:fit-content;max-width:100%;margin:var(--space-4) auto 0}
.duration-status{max-width:var(--size-content-reading);margin:var(--space-4) auto 0;color:var(--color-muted-foreground);text-align:center}
.duration-status:empty{display:none}
.duration-panel{max-width:var(--size-content-reading);margin:var(--space-6) auto 0}
.duration-panel[hidden],.duration-step[hidden],.duration-form[hidden]{display:none}
.duration-step{display:flex;flex-direction:column;align-items:center;gap:var(--space-4);text-align:center}
.duration-step p{color:var(--color-muted-foreground)}
/* The estimate form reuses the contact form recipe (form, .zeile, .feld, .field-error). */
.duration-choice{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-3);min-width:0;margin:0;padding:0;border:0}
.duration-choice legend{width:100%;margin-bottom:var(--space-2);padding:0;color:var(--color-foreground);font:var(--type-heading-3)}
.duration-option{display:inline-flex;align-items:center;gap:var(--space-2);min-height:var(--size-target-web-touch);padding:0 var(--space-4);border:var(--stroke-default) solid var(--color-border-strong);border-radius:var(--radius-md);color:var(--color-foreground);font:var(--type-label);cursor:pointer}
.duration-option:hover{background:var(--color-secondary-hover)}
.duration-option:has(input:checked){background:var(--color-surface-elevated);box-shadow:inset 0 -2px 0 var(--color-primary)}
.duration-option:has(input:focus-visible){outline:var(--focus-width) solid var(--color-focus);outline-offset:var(--focus-offset)}
.duration-option input{width:18px;height:18px;margin:0;accent-color:var(--color-primary)}
.duration-choice .field-error{width:100%}
.duration-form .field-error:not([hidden]){display:block}
.duration-form .feld input[aria-invalid="true"]{border-color:var(--color-danger-foreground);box-shadow:0 0 0 1px var(--color-danger-foreground)}
.duration-form>.pill{align-self:flex-start}
.duration-result{align-items:flex-start;text-align:start}
.duration-result-title{color:var(--color-foreground);font:var(--type-heading-3)}
.duration-result-title:focus{outline:none}
.duration-result-title:focus-visible{outline:var(--focus-width) solid var(--color-focus);outline-offset:var(--focus-offset)}
.duration-result .duration-result-fact{color:var(--color-foreground)}
.duration-estimates{display:flex;flex-direction:column;gap:var(--space-2)}
.duration-estimates[hidden]{display:none}
.duration-result .duration-estimates-title{color:var(--color-foreground);font:var(--type-label)}
.duration-estimates-list{display:flex;flex-wrap:wrap;gap:var(--space-3) var(--space-6);margin:0}
.duration-estimates-list div{display:flex;flex-direction:column;gap:var(--space-1)}
.duration-estimates-list dt{color:var(--color-muted-foreground);font-size:var(--type-body-small-size)}
.duration-estimates-list dd{margin:0;color:var(--color-foreground);font-weight:var(--font-weight-label);font-variant-numeric:tabular-nums}
.duration-result-note{padding-left:var(--space-4);border-left:var(--stroke-default) solid var(--color-border-strong)}
@media (forced-colors:active){.duration-option:has(input:checked){outline:2px solid CanvasText}}
/* Showcase 22 · Kopfüber: a ramp game. The canvas takes the keys while focused (inset ring,
   the stage clips outside outlines); the hold buttons take one pointer each, so thrust and turn
   work with two fingers, and never start a scroll, selection or callout. Readout, status and
   key list sit below the stage so nothing covers the scene. Under reduced motion a round only
   runs after an explicit start, so its pause button stays available. */
.upside-canvas:focus{outline:none}
.upside-canvas:focus-visible{outline:var(--focus-width) solid var(--color-focus);outline-offset:calc(-1 * var(--focus-width) - 2px);border-radius:inherit}
.showcase-still.upside-still{object-fit:cover;object-position:50% 50%;padding:0}
.lab-stats.upside-stats{position:static;justify-content:center;width:fit-content;max-width:100%;margin:var(--space-4) auto 0}
.upside-status{display:flex;flex-direction:column;align-items:center;gap:var(--space-1);max-width:var(--size-content-reading);margin:var(--space-4) auto 0;color:var(--color-muted-foreground);text-align:center}
.upside-status-title{color:var(--color-foreground);font:var(--type-label)}
.upside-status-score{color:var(--color-foreground);font-variant-numeric:tabular-nums}
.upside-status[hidden],.upside-status-score[hidden],.upside-keys[hidden]{display:none}
.upside-holds{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--space-3)}
.upside-hold{min-width:var(--size-target-web-touch);touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.upside-throttle{min-width:calc(2 * var(--size-target-web-touch))}
.upside-hold [aria-hidden="true"]{font-size:1.4em;line-height:1}
.upside-hold[data-held]{background:var(--color-surface-elevated);box-shadow:inset 0 -2px 0 var(--color-primary)}
@media (prefers-reduced-motion:reduce){.upside-controls .lab-pause{display:inline-flex}}
@media (forced-colors:active){.upside-hold[data-held]{outline:2px solid CanvasText}}
.upside-keys{max-width:var(--size-content-reading);margin:var(--space-6) auto 0;color:var(--color-muted-foreground);font:var(--type-body-small)}
.upside-keys-title{color:var(--color-foreground);font:var(--type-label)}
.upside-keys dl{display:grid;gap:var(--space-1);margin:var(--space-2) 0 0}
.upside-keys dl div{display:flex;flex-wrap:wrap;gap:0 var(--space-3)}
.upside-keys dt{flex:0 1 12rem;min-width:0;color:var(--color-foreground)}
.upside-keys dd{margin:0}
@media (max-width:767.98px){.upside-keys dl div{flex-direction:column}.upside-keys dt{flex-basis:auto}}
.upside-disclaimer{padding-left:var(--space-4);border-left:var(--stroke-default) solid var(--color-border-strong)}
.showcase-explain .upside-disclaimer,.showcase-explain .upside-sources-title{color:var(--color-foreground)}
.upside-sources{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-4);margin-top:var(--space-2);padding:0;list-style:none}
.upside-sources a{display:inline-flex;align-items:center;min-height:var(--size-target-web-touch);color:var(--color-link);text-decoration:underline}
.upside-sources a:hover{color:var(--color-link-hover)}
@media (max-width:767.98px){
  .showcase-stage.is-live{aspect-ratio:4/5;max-height:70svh}
  .showcase-stage:not(.is-live){min-height:200px}
}
/* Showcase 22 · Foldable: a book-style foldable built from CSS 3D planes
   (src/showcases/foldable). It opens like a book: the flap (.fold-left) swings around the hinge
   on its right edge and carries the outer display on its back; the base (.fold-right) carries the
   camera. Position rules use .fold-left/.fold-right, motion rules .fold-flap/.fold-base. The
   renderer writes the transforms of #foldBody and #foldFlap; the app on the screens is real DOM.
   Base size of one half: --fold-w × --fold-h × --fold-depth = 250 × 350 × 15 px (FOLD_W, FOLD_H,
   FOLD_D in the renderer): a book-style foldable of 84 × 118 × 5.2 mm per half, so the outer
   display and the unfolded inner display share one aspect ratio of about 1.41. The renderer scales the
   whole device to the stage (--fold-scale) and writes --shade per face and --crease on the base;
   the defaults below are the unlit, unfolded state. Dragging on the device folds it, beside it
   turns the view; vertical swipes keep scrolling the page. The readout sits below the stage. */
.fold-scene{--fold-scale:1;position:absolute;inset:0;perspective:calc(var(--fold-scale) * 1500px);touch-action:pan-y;cursor:grab;user-select:none;-webkit-user-select:none}
.fold-scene[hidden]{display:none}
.fold-scene[data-dragging]{cursor:grabbing}
/* Centred on the hinge by position, not by layout: the device is wider than a phone's stage. */
.fold-body{--fold-w:250px;--fold-h:350px;--fold-depth:15px;position:absolute;left:50%;top:50%;width:calc(2 * var(--fold-w));height:var(--fold-h);margin:calc(var(--fold-h) / -2) 0 0 calc(-1 * var(--fold-w));transform-style:preserve-3d;will-change:transform}
.fold-half{position:absolute;top:0;width:var(--fold-w);height:var(--fold-h);transform-style:preserve-3d}
.fold-left{left:0}
.fold-right{left:var(--fold-w)}
.fold-flap{transform-origin:100% 50%;will-change:transform}
.fold-face{position:absolute;inset:0;border-radius:28px;background:linear-gradient(160deg,var(--palette-neutral-600),var(--palette-neutral-850) 70%);box-shadow:inset 0 0 0 1px var(--material-standard-border);backface-visibility:hidden;-webkit-backface-visibility:hidden}
/* Thickness (FOLD_D in the renderer): the screen side lies in the hinge plane, the back
   --fold-depth behind it, so the closed flap rests on the base instead of cutting into it. */
.fold-back{transform:translateZ(calc(-1 * var(--fold-depth))) rotateY(180deg)}
/* Side walls: outlines of the half stacked between front and back read as one solid frame,
   rounded corners included; front and back hide their inside, so a ring is enough. Strips along
   the straight edges close the frame seen edge-on. All come first in the markup, so a browser
   that paints the planes flat still shows the screen on top. */
.fold-wall{position:absolute;inset:0;border:8px solid var(--palette-neutral-600);border-radius:28px 4px 4px 28px;pointer-events:none;transform:translateZ(calc(var(--fold-depth) * var(--wall) / -7))}
.fold-right .fold-wall{border-radius:4px 28px 28px 4px}
.fold-wall:nth-child(1){--wall:1}
.fold-wall:nth-child(2){--wall:2}
.fold-wall:nth-child(3){--wall:3}
.fold-wall:nth-child(4){--wall:4}
.fold-wall:nth-child(5){--wall:5}
.fold-wall:nth-child(6){--wall:6}
.fold-side{position:absolute;background:var(--palette-neutral-600);pointer-events:none}
/* A pixel inside the outlines, so the layers stay in front and the strips only fill their gaps. */
.fold-side-outer{top:28px;bottom:28px;left:1px;width:var(--fold-depth);transform-origin:0 50%;transform:rotateY(90deg)}
.fold-right .fold-side-outer{left:calc(var(--fold-w) - 1px)}
.fold-side-top,.fold-side-bottom{left:28px;width:calc(var(--fold-w) - 32px);height:var(--fold-depth);transform-origin:50% 0;transform:rotateX(-90deg)}
.fold-side-top{top:1px}
.fold-side-bottom{top:calc(var(--fold-h) - 1px)}
.fold-right :is(.fold-side-top,.fold-side-bottom){left:4px}
/* Spine: three strips on an arc around the hinge join the backs of both halves; the renderer turns
   and stretches them with the fold and writes their --shade. */
.fold-spine{position:absolute;top:3px;bottom:3px;left:var(--fold-w);width:0;transform-style:preserve-3d;pointer-events:none}
.fold-spine i{--shade:0;position:absolute;top:0;bottom:0;left:calc(-1 * var(--fold-depth));width:calc(2 * var(--fold-depth));background:color-mix(in srgb,var(--palette-neutral-600),var(--palette-neutral-950) calc(var(--shade) * 100%));transform:scaleX(0)}
.fold-left .fold-front{border-radius:28px 4px 4px 28px}
.fold-right .fold-front{--crease:0;border-radius:4px 28px 28px 4px}
/* Seen from behind, each half's hinge side swaps sides on its back face. */
.fold-left .fold-back{border-radius:4px 28px 28px 4px}
.fold-right .fold-back{border-radius:28px 4px 4px 28px}
.fold-shade{--shade:0;position:absolute;inset:0;border-radius:inherit;background:var(--palette-neutral-950);opacity:var(--shade);pointer-events:none}
.fold-crease{position:absolute;top:7px;bottom:7px;left:0;width:10px;background:linear-gradient(90deg,var(--palette-neutral-950),transparent);opacity:var(--crease);pointer-events:none}
.fold-camera{position:absolute;top:18px;left:18px;display:grid;gap:6px;padding:7px;border-radius:16px;background:var(--palette-neutral-900);box-shadow:inset 0 0 0 1px var(--material-standard-border)}
.fold-camera i{width:20px;height:20px;border:4px solid var(--palette-neutral-650);border-radius:50%;background:var(--palette-neutral-950)}
/* Screens: the inner display runs across both halves without a bezel at the hinge. Seen from
   behind, the camera sits in the top corner away from the hinge. */
/* Isolate the screen so Chromium hit-tests rotated controls above their background. */
.fold-screen{isolation:isolate;position:absolute;inset:7px;overflow:hidden;border-radius:21px;background:var(--color-background);color:var(--color-foreground);font-family:var(--font-family-product);font-size:13px;line-height:1.3;text-align:left}
.fold-left .fold-front .fold-screen{right:0;border-radius:21px 0 0 21px}
.fold-right .fold-front .fold-screen{left:0;border-radius:0 21px 21px 0}
.fold-view{position:absolute;inset:0;display:flex;flex-direction:column;padding:14px 12px;transition:opacity var(--motion-duration-fast) var(--motion-easing-standard)}
.fold-bar{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:24px;font-size:14px}
.fold-bar b{color:var(--color-primary);font-weight:700}
.fold-bar-title{color:var(--color-muted-foreground);font-size:12px}
.fold-avatar{display:grid;place-items:center;width:24px;height:24px;border-radius:50%;background:var(--color-secondary);font-size:10px;font-weight:600}
.fold-heading{margin:6px 0;font-size:18px;font-weight:650;line-height:1.2}
.fold-segments{display:flex;padding:3px;border-radius:10px;background:var(--color-secondary);font-size:12px}
.fold-segments span{flex:1;padding:4px;border-radius:8px;color:var(--color-muted-foreground);text-align:center}
.fold-segments .is-on{background:var(--color-surface);color:var(--color-foreground)}
.fold-list{display:flex;flex-direction:column;gap:5px;margin:8px 0 0;padding:0;list-style:none}
.fold-job{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:"time title" "time customer";align-items:center;gap:2px 10px;width:100%;min-height:50px;padding:7px 10px;border:1px solid var(--color-border);border-radius:14px;background:var(--color-surface);color:inherit;font:inherit;text-align:left;cursor:pointer}
.fold-job[aria-pressed="true"]{border-color:var(--color-primary);background:var(--color-surface-elevated);box-shadow:inset 3px 0 0 var(--color-primary)}
.fold-job:focus-visible{outline:var(--focus-width) solid var(--color-focus);outline-offset:-3px}
.fold-job-time{grid-area:time;color:var(--color-muted-foreground);font-size:12px;font-variant-numeric:tabular-nums}
.fold-job-title{grid-area:title;overflow:hidden;font-weight:600;white-space:nowrap;text-overflow:ellipsis}
.fold-job-customer{grid-area:customer;overflow:hidden;color:var(--color-muted-foreground);font-size:12px;white-space:nowrap;text-overflow:ellipsis}
.fold-detail[hidden]{display:none}
.fold-badge{display:inline-block;margin-top:12px;padding:3px 8px;border-radius:999px;background:var(--color-primary);color:var(--color-primary-foreground);font-size:11px;font-weight:600}
.fold-detail-title{margin:10px 0 0;font-size:19px;font-weight:650;line-height:1.2}
.fold-detail-customer{margin:2px 0 0;color:var(--color-muted-foreground)}
.fold-facts{display:flex;flex-direction:column;gap:6px;margin:14px 0 0}
.fold-facts div{display:flex;align-items:baseline;justify-content:space-between;gap:8px;padding:8px 10px;border-radius:10px;background:var(--color-surface)}
.fold-facts dt{color:var(--color-muted-foreground);font-size:12px}
.fold-facts dd{margin:0;font-weight:600;font-variant-numeric:tabular-nums}
.fold-actions-view{display:none;justify-content:center;padding:16px}
.fold-steps{min-width:0;margin:0;padding:0;border:0}
.fold-steps legend{margin-bottom:var(--space-3);padding:0;font-weight:600}
.fold-step-list{display:flex;gap:var(--space-2)}
.fold-step{display:flex;align-items:center;justify-content:center;gap:var(--space-2);min-width:0;padding:var(--space-3);border:var(--stroke-default) solid var(--color-border-strong);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-foreground);cursor:pointer}
.fold-step input{flex:none;width:20px;height:20px;margin:0;accent-color:var(--color-primary);cursor:pointer}
.fold-step:has(input:checked){border-color:var(--color-primary);background:var(--color-surface-elevated)}
.fold-step:has(input:focus-visible){outline:var(--focus-width) solid var(--color-focus);outline-offset:2px}
.fold-actions-view .fold-step{flex:1;flex-direction:column;min-height:110px;padding:8px;font-weight:600}
.fold-actions-view .fold-step input{width:28px;height:28px}
.fold-progress{display:flex;flex-direction:column;gap:6px;margin-top:14px;font-size:12px;font-weight:600}
.fold-progress progress{display:block;width:100%;height:6px;overflow:hidden;border:0;border-radius:var(--radius-round);background:var(--color-secondary);color:var(--color-primary);accent-color:var(--color-primary);appearance:none}
.fold-progress progress::-webkit-progress-bar{background:var(--color-secondary)}
.fold-progress progress::-webkit-progress-value{background:var(--color-primary)}
.fold-progress progress::-moz-progress-bar{background:var(--color-primary)}
/* The unscaled equivalents remain reachable on a phone, where the device targets shrink. */
.fold-step-controls{flex-basis:100%;font:var(--type-label);text-align:center}
.fold-step-controls[hidden]{display:none}
.fold-step-controls .fold-step-list{flex-wrap:wrap;justify-content:center}
.fold-step-controls .fold-step{min-height:var(--size-target-web-touch);min-width:var(--size-target-web-touch);padding:var(--space-2) var(--space-3)}
/* Half open (laptop posture): the hinge lies across, so both inner halves turn their content a
   quarter: the standing base shows the detail upright, the lying flap the actions, each on one
   inner screen half turned on its side (--fold-h less 2 × 7 px bezel by --fold-w less 7 px). */
.fold-stage[data-layout="half"] .fold-front .fold-list-view{display:none}
.fold-stage[data-layout="half"] .fold-actions-view{display:flex}
.fold-stage[data-layout="half"] :is(.fold-actions-view,.fold-detail-view){inset:auto;top:0;left:0;width:calc(var(--fold-h) - 14px);height:calc(var(--fold-w) - 7px);transform-origin:0 0;transform:translateX(calc(var(--fold-w) - 7px)) rotate(90deg)}
.fold-stage[data-layout="half"] .fold-detail-view{padding:12px 16px}
.fold-stage[data-layout="half"] .fold-badge{display:none}
.fold-stage[data-layout="half"] .fold-detail-title{margin-top:6px}
.fold-stage[data-layout="half"] .fold-facts{flex-direction:row;margin-top:10px}
.fold-stage[data-layout="half"] .fold-facts div{flex:1;flex-direction:column;gap:2px;padding:6px 10px}
.fold-stage[data-layout="half"] .fold-progress{flex-direction:row;align-items:center;gap:12px;margin-top:8px}
.fold-stage[data-layout="half"] .fold-progress span{white-space:nowrap}
.fold-stage[data-layout="half"] .fold-progress progress{flex:1;min-width:0}
.lab-stats.fold-stats{position:static;justify-content:center;width:fit-content;max-width:100%;margin:var(--space-4) auto 0}
.fold-stats div{flex-wrap:wrap;min-width:0;column-gap:var(--space-1)}
/* Opening angle: the keyboard and screen-reader twin of dragging the device, on a 44px target. */
/* Job picker: the full-size twin of the job rows on the device, which shrink with it on phones.
   contain:paint as in the ravens picker, so WebKit's menulist label never widens the page. */
.fold-pick{display:inline-flex;flex-wrap:wrap;min-width:0;align-items:center;justify-content:center;gap:var(--space-2);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.fold-pick select{min-width:var(--size-target-web-touch);max-width:100%;contain:paint;min-height:var(--size-target-web-touch);padding:0 var(--space-3);border:var(--stroke-default) solid var(--color-border-strong);border-radius:var(--radius-md);background:var(--color-surface-inset);color:var(--color-foreground);font:var(--type-label);cursor:pointer}
.fold-range{display:inline-flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--space-2) var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;color:var(--color-foreground);font:var(--type-label)}
.fold-range input{width:min(12rem,100%);min-width:0;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.fold-range output{min-width:5ch;color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
.fold-context{max-width:var(--size-content-reading);margin-top:var(--space-8);color:var(--color-muted-foreground);font:var(--type-body-small)}
.fold-context p+p{margin-top:var(--space-2)}
/* Showcase 02 · Stoff: the cloth is grabbed and pulled, so touch drags belong to the canvas.
   The stage stays below 70svh on phones, leaving room around it to scroll the page. */
.stoff-canvas{touch-action:none;cursor:grab}
.stoff-canvas[data-grab="active"]{cursor:grabbing}
/* Its fallback stacks the still above the note, so the banner with its wordmark and
   bottom edge is never covered, down to 320px. */
.stoff-stage:has(.showcase-still):not(.is-live){flex-direction:column;align-items:center;justify-content:flex-start;gap:var(--space-3);aspect-ratio:auto}
.stoff-stage:not(.is-live) .showcase-still{position:static;flex:none;width:100%;height:auto;aspect-ratio:16/9;padding:0}
/* Showcase 04 · Zerfall: the stage holds a real mini page in normal flow (it is also the
   no-JS still), with room below for the heap and the readout chip. The canvas lies on top and
   only takes the pointer while the page is sand. The page is faded, never hidden, while it is
   sand, so it stays in the accessibility tree. Its own surface is transparent on purpose:
   only content (text, button, card) turns into grains. */
.showcase-stage.decay-stage{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:var(--space-4);aspect-ratio:auto;min-height:0;max-height:none;padding:var(--space-8) var(--space-8) var(--space-16)}
.showcase-stage.decay-stage:not(.is-live){padding-bottom:var(--space-6)}
.decay-canvas{pointer-events:none;cursor:grab;user-select:none;-webkit-user-select:none}
.decay-canvas[data-digging]{cursor:grabbing}
.decay-stage.is-live:is([data-state="sand"],[data-state="restoring"]) .decay-canvas{pointer-events:auto;touch-action:pan-y}
.decay-stage:is([data-state="sand"],[data-state="restoring"]) .decay-page{opacity:0}
/* The readout sits below the stage here: the floor of the stage belongs to the heap. */
.lab-stats.decay-stats{position:static;justify-content:center;width:fit-content;max-width:100%;margin:var(--space-4) auto 0}
.decay-page{position:relative;display:flex;flex-direction:column;gap:var(--space-8);min-width:0;color:var(--color-foreground)}
.decay-bar{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:var(--space-2) var(--space-6);padding-bottom:var(--space-3);border-bottom:var(--stroke-default) solid var(--color-border-strong)}
.decay-logo{display:flex;align-self:center}
.decay-logo .logo-mark{--logo-height:32px}
.decay-nav{display:flex;flex-wrap:wrap;gap:var(--space-1) var(--space-4);font:var(--type-label);color:var(--color-muted-foreground)}
.decay-body{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,15rem),1fr));gap:var(--space-8);align-items:start}
.decay-copy{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-3);min-width:0}
.decay-title{font:var(--type-heading-1);color:var(--color-foreground);overflow-wrap:anywhere}
.decay-text,.decay-card-text{max-width:42ch;font:var(--type-body-small);color:var(--color-muted-foreground);overflow-wrap:anywhere}
.decay-like{display:inline-flex;align-items:center;gap:var(--space-2);min-height:var(--size-target-web-touch);margin-top:var(--space-2);padding:0 var(--space-5);border:0;border-radius:var(--radius-round);background:var(--color-primary);color:var(--color-primary-foreground);font:var(--type-label);cursor:pointer}
.decay-like:hover{background:var(--color-primary-hover)}
.decay-like[aria-pressed="true"]{background:var(--color-accent);color:var(--color-accent-foreground)}
.decay-count{font-variant-numeric:tabular-nums}
.decay-card{display:flex;flex-direction:column;gap:var(--space-2);min-width:0;padding:var(--space-5);border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-surface-elevated)}
.decay-swatch{display:flex;gap:var(--space-2);margin-bottom:var(--space-2)}
.decay-swatch i{width:var(--space-8);height:var(--space-8);border-radius:var(--radius-round);background:var(--palette-violet-500)}
.decay-swatch i:nth-child(2){background:var(--palette-frozen-pink)}
.decay-swatch i:nth-child(3){background:var(--palette-frozen-blue)}
.decay-card-title{font:var(--type-heading-3);color:var(--color-foreground);overflow-wrap:anywhere}
@media (max-width:767.98px){
  .showcase-stage.decay-stage{padding:var(--space-5) var(--space-4) var(--space-20)}
  .decay-page{gap:var(--space-5)}
}
/* Showcase 05 · Lernt live: the stage is a drawing surface for the target image (touch paints
   instead of scrolling, the stage is capped at 70svh on small screens). Below the stage a panel
   shows the target the network is learning and the loss curve; both are 2D canvases drawn in
   token colours and sit on the inset surface like the stage. */
.learn-canvas{touch-action:none;cursor:crosshair;user-select:none;-webkit-user-select:none}
.learn-panel{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-4);margin-top:var(--space-4)}
.learn-panel[hidden]{display:none}
.learn-figure{display:flex;flex-direction:column;gap:var(--space-2);min-width:0;margin:0;padding:var(--space-3);border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-surface-inset)}
.learn-figure figcaption{display:flex;flex-wrap:wrap;gap:0 var(--space-4);font:var(--type-caption);color:var(--color-muted-foreground)}
.learn-figure output{color:var(--color-foreground);font-variant-numeric:tabular-nums}
.learn-target{display:block;width:100%;height:auto;aspect-ratio:16/9;object-fit:contain}
.learn-loss{display:block;width:100%;height:clamp(96px,14vw,150px)}
@media (min-width:768px){.learn-panel{grid-template-columns:minmax(0,1fr) minmax(0,2fr)}}
/* Showcase 07 · Datenverkehr: a click on a router fails or repairs it, so the pointer shows
   that only over a router. The router toggles are numbered like the map; the word "Knoten" is
   kept for screen readers only. The load slider reuses the segmented-control frame. */
.traffic-canvas{touch-action:pan-y;user-select:none;-webkit-user-select:none}
.traffic-canvas[data-hover="router"]{cursor:pointer}
/* The readout sits below the stage, as in Zerfall: the whole stage belongs to the map. */
.lab-stats.traffic-stats{position:static;justify-content:center;width:fit-content;max-width:100%;margin:var(--space-4) auto 0}
.traffic-nodes button{min-width:var(--size-target-web-touch);padding:0 var(--space-3)}
.traffic-nodes button[aria-pressed="true"]{background:var(--color-danger);color:var(--color-danger-foreground);box-shadow:inset 0 -2px 0 var(--color-destructive)}
.traffic-node-word{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.traffic-load{display:inline-flex;align-items:center;gap:var(--space-3);min-height:var(--size-target-web-touch);max-width:100%;padding:0 var(--space-4);border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-secondary);color:var(--color-foreground);font:var(--type-label)}
.traffic-load input{width:9rem;min-width:0;flex:1 1 auto;height:var(--size-target-web-touch);margin:0;accent-color:var(--color-primary);cursor:pointer}
.traffic-load output{min-width:4.5ch;text-align:end;font-variant-numeric:tabular-nums}
.lab-action:disabled{opacity:.55;cursor:default}
.lab-action:disabled:hover{background:transparent}

/* Showroom: with JavaScript the overview opens a showcase below the cards
   (src/components/showcase/showroom.ts). Before a selection the grid wraps as usual and a
   prompt fills the stage area; once one is open the cards become a single horizontally
   scrollable picker row. */
.showroom[hidden],.showroom-empty[hidden],.showroom-bar[hidden],.showroom-picker-bar[hidden],.showroom-message[hidden]{display:none}
.showroom{margin-top:var(--space-10)}
.showroom-empty{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2);min-height:220px;
  padding:var(--space-8) var(--space-6);border:var(--stroke-default) dashed var(--color-border-strong);border-radius:var(--radius-2xl);
  background:radial-gradient(ellipse 70% 60% at 50% 45%,var(--color-surface),var(--color-surface-inset));
  color:var(--color-muted-foreground);text-align:center
}
.showroom-empty strong{font:var(--type-heading-3);color:var(--color-foreground)}
/* Only the close button is visible; the selected title is the showcase heading right below.
   The status line announces the selection to screen readers. */
.showroom.is-open{margin-top:var(--space-3)}
.showroom-message{padding:var(--space-8);border:var(--stroke-default) solid var(--color-border);border-radius:var(--radius-lg);color:var(--color-muted-foreground)}
.showroom-message button{margin-top:var(--space-4)}
.showroom-picker-bar{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:var(--space-3);margin-top:var(--space-6)}
.showroom-picker-actions{display:flex;flex-wrap:wrap;gap:var(--space-2)}
.showroom-scroll{min-width:var(--size-target-web-touch);padding-inline:var(--space-3)}
.showroom-scroll:disabled{opacity:.45;cursor:default}
/* Keep the page title, but do not place another hero between selection and rendering. */
#showcases.has-selection>.wrap>.head h1{font:var(--type-heading-2);max-width:none}
#showcases.has-selection>.wrap>.head p{display:none}
.showroom-status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* The mounted showcase is a component of this page: the detail article without its page frame
   (no page-intro offset for the fixed navigation, no back link). Its h1/h2 were demoted to
   h2/h3 by the showroom, so they take over the detail page's heading styles here. */
.showroom-view[hidden]{display:none}
.showroom-view{margin-top:0}
.showroom-showcase>.wrap{width:100%}
.showroom-view[aria-busy="true"]{min-height:320px}
.showroom-showcase .showcase-stage{margin-top:0}
.showroom-showcase .head{margin-top:var(--space-8)}
.showroom-showcase .head h2{max-width:20ch;margin-inline:0}
.showroom-showcase .showcase-explain h3{font:var(--type-heading-2)}
/* Picker row: one line, scrolls sideways, snaps to cards. The block padding leaves room for
   the hover lift, the shadow and the focus ring that a scroll container would otherwise clip. */
.showcase-cards.is-showroom{
  display:flex;gap:var(--space-4);position:relative;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;scroll-padding-inline:var(--space-3);padding:var(--space-3);margin-inline:calc(-1 * var(--space-3));
  flex-wrap:nowrap;scrollbar-width:auto;margin-top:var(--space-2);
  scroll-margin-top:calc(var(--nav-offset) + var(--nav-height) + var(--space-4))
}
.showcase-cards.is-showroom .showcase-card{flex:0 0 min(12rem,72%);scroll-snap-align:start;padding:var(--space-3)}
.showcase-cards.is-showroom .showcase-card>p,.showcase-cards.is-showroom .showcase-card .tag{display:none}
.showcase-cards.is-showroom .showcase-card h2{font:var(--type-title);margin-top:var(--space-2)}
.showcase-card.is-selected{border-color:var(--color-primary);box-shadow:inset 0 0 0 1px var(--color-primary)}
.showcase-card.is-selected h2 a{color:var(--color-link)}
@media (hover:hover){.showcase-cards.is-showroom .showcase-card:hover{transform:none}}
@media (forced-colors:active){.showcase-card.is-selected{outline:3px solid Highlight}}

/* Eigene Produkte. Opaque
   Standard cards on the page background: the live product leads with its product shot, the
   products in development follow as a group. Compact (< 768px) values first. */
#produkte,#demos{padding-block:var(--section-padding)}
.produkte-wrap{width:min(100% - 2 * var(--page-padding),var(--size-content-default));margin-inline:auto}
.products-head{max-width:760px}
.products-head h2{text-wrap:balance}
.products-head p{color:var(--color-muted-foreground);margin-top:var(--space-5)}
/* Zero specificity: the list resets must not outrank the spacing rules below. */
:where(#produkte,#demos) :is(ul,h4){list-style:none;margin:0;padding:0}
.products-live{margin-top:var(--space-8)}
.products-progress{margin-top:var(--space-8)}
.products-progress>ul{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-4);margin-top:var(--space-4)}
/* clip, not hidden: a clipping box must not become a scroll container that scrollIntoView can
   shift. hidden stays as the fallback for browsers without clip (Safari before 16). */
.product{height:100%;overflow:hidden;overflow:clip;background:var(--material-standard-background);border:var(--stroke-default) solid var(--material-standard-border);border-radius:var(--radius-xl)}
.product p{color:var(--color-muted-foreground)}
/* Names and the address have no break opportunity at 200% text in a 320px column. */
.product :is(h3,h4),.product-address{overflow-wrap:anywhere}
/* The product shot sits in a fixed accent stage and runs out of its bottom edge. */
.product-live-media{height:280px;overflow:hidden;overflow:clip;display:flex;align-items:flex-start;justify-content:center;padding-top:var(--space-8);background:var(--color-accent)}
.product-live-media picture{display:contents}
.product-live-media img{display:block;flex:none;width:300px;height:auto;aspect-ratio:1/2;border-radius:44px;box-shadow:var(--elevation-lg)}
.product-live-body{display:flex;flex-direction:column;gap:var(--space-3);padding:var(--space-6)}
.product-live-copy{display:flex;flex-direction:column;gap:var(--space-2);min-width:0}
/* Name and marker share a line and wrap below each other only when the line is too short. */
.product-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3);padding-block:var(--space-1)}
.product-live-action{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-3);padding-top:var(--space-2)}
.product-live-action .pill{gap:var(--space-2)}
/* Pressed as specified: scaled by .pill:active, without the glow and elevation of .pill-grad:hover. */
.product-live-action .pill:active{box-shadow:none}
.product-live-action .pill svg{width:var(--icon-sm);height:var(--icon-sm);flex:none}
/* Scrum Poker's BetaFlag (.topbar__beta): a tilted sticker, no states. */
.beta-flag{display:inline-flex;flex:none;align-items:center;padding:2px 6px;border-radius:var(--radius-xs);background:var(--color-primary);color:var(--color-primary-foreground);white-space:nowrap;transform:rotate(-4deg)}
.product-dev-media{height:200px;background:var(--color-surface-inset)}
.product-dev-media img{display:block;width:100%;height:100%;object-fit:cover}
.product-dev-body{display:flex;flex-direction:column;gap:var(--space-3);padding:var(--space-6)}
/* Mystery card: an unannounced app. The same night stage in both themes: brand light
   and a swarm of question marks drift behind reeded glass, the nearest marks pass in front of it,
   the copy sits on the calm lower half. `.is-dated` puts the reveal date in front of it all. */
.product-mystery{--mystery-night:color-mix(in srgb,var(--palette-violet-850) 30%,var(--palette-space-950));position:relative;isolation:isolate;display:flex;flex-direction:column;background:linear-gradient(170deg,var(--palette-violet-850) 0%,var(--mystery-night) 62%);border-color:var(--palette-violet-850);color:var(--palette-frozen-white)}
.mystery-backdrop{position:absolute;inset:0;z-index:-1;overflow:hidden;overflow:clip;pointer-events:none}
.mystery-light{position:absolute;aspect-ratio:1;border-radius:50%;opacity:.7;will-change:transform}
/* Night settles over the lower half: drifting light never passes behind the copy at full strength. */
.mystery-backdrop::after{content:"";position:absolute;inset:0;background:linear-gradient(transparent 110px,color-mix(in srgb,var(--mystery-night) 88%,transparent) 270px)}
.mystery-light-a{width:min(340px,100%);left:-60px;top:-150px;background:radial-gradient(closest-side,color-mix(in srgb,var(--palette-violet-500) 85%,transparent),transparent)}
/* At rest every light stays inside the card's right edge, so reflow checks see no overhang. */
.mystery-light-b{width:min(280px,70%);right:8%;top:-90px;background:radial-gradient(closest-side,color-mix(in srgb,var(--palette-frozen-blue) 65%,transparent),transparent)}
.mystery-light-c{width:min(240px,60%);right:0;top:30px;background:radial-gradient(closest-side,color-mix(in srgb,var(--palette-frozen-pink) 55%,transparent),transparent)}
/* Reeds: one lit edge and one shaded edge per 14px flute, faded out before the copy begins. */
.mystery-glass{position:absolute;inset:0;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--palette-white) 12%,transparent) 0 1px,color-mix(in srgb,var(--palette-white) 3%,transparent) 2px,transparent 7px,color-mix(in srgb,var(--palette-space-950) 22%,transparent) 13px,color-mix(in srgb,var(--palette-white) 12%,transparent) 14px);-webkit-mask-image:linear-gradient(#000 0 150px,#0000 230px);mask-image:linear-gradient(#000 0 150px,#0000 230px)}
/* The swarm (board "Mystery-Karte Variante Fragezeichen-Schwarm"): far, mid and near question
   marks with their own size, blur, opacity, path and tempo; positions come from the markup. */
.mystery-swarm{position:absolute;inset:0}
.mystery-q{position:absolute;left:var(--q-x);top:var(--q-y);font:600 var(--q-size)/1 var(--font-family-display);transform:rotate(var(--q-turn));will-change:transform}
.mystery-q-far{opacity:.28;filter:blur(1.5px)}
.mystery-q-mid{opacity:.42;filter:blur(.5px)}
.mystery-q-near{opacity:.5}
.is-dated .mystery-q-near{opacity:.3;filter:blur(2px)}
.mystery-q-white{color:var(--palette-frozen-white)}
.mystery-q-lilac{color:var(--palette-frozen-lilac)}
.mystery-q-blue{color:var(--palette-frozen-blue)}
.mystery-q-pink{color:var(--palette-frozen-pink)}
/* The date scales with the card (cqi), so it stays whole in a 240px card at 200% text. */
.mystery-stage{container-type:inline-size;height:200px;flex:none;display:flex;align-items:center;justify-content:center}
.mystery-date{font:600 min(96px,25cqi)/1 var(--font-family-display);letter-spacing:-.02em;color:var(--palette-frozen-white);font-variant-numeric:tabular-nums;text-shadow:0 10px 30px color-mix(in srgb,var(--palette-space-950) 50%,transparent);white-space:nowrap}
.product-mystery .product-dev-body{flex:1}
.product-mystery h4{color:var(--palette-frozen-white)}
.product-mystery .product-platform{color:var(--palette-frozen-lilac)}
.product-mystery p{color:var(--palette-space-400)}
/* Two sentences, two lines: the line break lives in the text. */
.mystery-text{white-space:pre-line}
@media (prefers-reduced-motion:no-preference){
  .mystery-light-a{animation:mystery-drift-a 19s ease-in-out infinite alternate}
  .mystery-light-b{animation:mystery-drift-b 23s ease-in-out infinite alternate}
  .mystery-light-c{animation:mystery-drift-c 17s ease-in-out infinite alternate}
  .mystery-q{animation:mystery-wander var(--q-time) ease-in-out var(--q-delay) infinite alternate}
}
@keyframes mystery-drift-a{to{transform:translate(260px,70px) scale(1.15)}}
@keyframes mystery-drift-b{to{transform:translate(-220px,90px) scale(.85)}}
@keyframes mystery-drift-c{to{transform:translate(-180px,-60px) scale(1.2)}}
@keyframes mystery-wander{
  0%{transform:translate(0,0) rotate(var(--q-turn))}
  33%{transform:translate(var(--q-ax),var(--q-ay)) rotate(calc(var(--q-turn) + var(--q-ar)))}
  66%{transform:translate(var(--q-bx),var(--q-by)) rotate(calc(var(--q-turn) + var(--q-br)))}
  100%{transform:translate(var(--q-cx),var(--q-cy)) rotate(calc(var(--q-turn) + var(--q-cr)))}
}
@media (forced-colors:active){.mystery-backdrop{display:none}}
.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}
@media (max-width:359.98px){
  .product-live-media img{width:240px;border-radius:36px}
  /* Keeps "Open Scrum Poker" and its icon on one line in the 232px card column. */
  .product-live-action .pill{padding-inline:var(--space-6)}
}
@media (min-width:768px){
  .products-live{margin-top:var(--space-12)}
  .products-progress{margin-top:var(--space-12)}
  .products-progress>ul{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-6);margin-top:var(--space-6)}
  .product-live-media{height:400px;padding-top:56px}
  .product-live-media img{width:min(960px,100% - 2 * var(--space-10));aspect-ratio:960/600;border-radius:14px}
  .product-live-body{padding:var(--space-10)}
  .product-dev-body{padding:var(--space-6) var(--space-8) var(--space-8)}
  /* Side by side the mystery card is taller; names share one line and the free space above
     a card's name is the image slot. */
  .product-dev{display:flex;flex-direction:column}
  .product-dev-body{margin-top:auto}
}
@media (min-width:1024px){
  .product-live-body{flex-direction:row;align-items:center;gap:var(--space-12)}
  .product-live-copy{flex:1}
  .product-live-action{flex:none;align-items:flex-end;padding-top:0}
}
@media (prefers-reduced-transparency:reduce),(prefers-contrast:more){.hero .wrap::after{display:none}}
@media (forced-colors:active){.hero .wrap::after{display:none}}

/* Person */
.person{display:grid;grid-template-columns:.9fr 1.1fr;gap:var(--space-12);align-items:center}
.person>*{min-width:0}
.portrait{position:relative;width:min(100%,460px);aspect-ratio:1;margin:0;border-radius:var(--radius-xl);overflow:hidden;background:var(--color-surface-inset);box-shadow:var(--elevation-md)}
.portrait img{display:block;width:100%;height:100%;object-fit:cover}
/* Opaque caption backing: the portrait behind it is photographic, never a contrast guarantee. */
.portrait figcaption{position:absolute;left:var(--space-4);right:var(--space-4);bottom:var(--space-4);display:flex;flex-direction:column;gap:var(--space-0-5);padding:var(--space-3) var(--space-4);border:var(--stroke-default) solid var(--material-elevated-border);border-radius:var(--radius-lg);background:var(--color-surface-elevated);box-shadow:var(--elevation-sm)}
.portrait strong{line-height:1.2}
.portrait span{color:var(--color-muted-foreground)}
.person h2{text-wrap:balance}
.person .p-lead{color:var(--color-muted-foreground);margin-top:var(--space-5);max-width:52ch}
.person ul{list-style:none;padding:0;margin:var(--space-6) 0 0;display:flex;flex-direction:column;gap:var(--space-3)}
.person li{display:flex;gap:var(--space-3);color:var(--color-muted-foreground)}
/* The bullet is a fixed-size flex item; the copy beside it must be allowed to shrink. */
.person li>span{min-width:0}
.person li b{color:var(--color-foreground);font-weight:var(--font-weight-semibold)}
.person li::before{content:"";width:var(--size-indicator-md);height:var(--size-indicator-md);flex:none;margin-top:2px;border-radius:var(--radius-round);background:var(--color-primary);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' d='M5 13l4 4L19 7'/%3E%3C/svg%3E") center/16px no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' d='M5 13l4 4L19 7'/%3E%3C/svg%3E") center/16px no-repeat}

/* Werdegang: one time axis in true scale (Person.astro). Every entry starts where its first month
   sits on the axis; its bar runs to the last month. Positions are months, the span is the whole
   axis, so each length is (end - start) / span of the chart width. */
.career{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--space-6) var(--space-12);align-items:start;margin-top:var(--space-24)}
.career-intro{display:contents}
.career-intro h3{text-wrap:balance}
.career-intro p{max-width:56ch;color:var(--color-muted-foreground)}
.career-chart{grid-column:1/-1;margin-top:var(--space-6)}
.career-line{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-8)}
.career-line li{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-1);min-width:0;
  padding-inline-start:calc(var(--career-start) / var(--career-span) * 100%);overflow-wrap:break-word}
/* Labels shrink to the room right of their start, so long role names wrap instead of overflowing. */
.career-line li>:not(.career-bar){max-width:100%}
.career-bar{display:block;align-self:stretch;max-width:calc((var(--career-end) - var(--career-start)) / (var(--career-span) - var(--career-start)) * 100%);
  height:var(--space-2);margin-top:var(--space-3);border-radius:var(--radius-round);background:var(--color-border-strong)}
.career-line .is-current .career-bar{background:var(--color-primary)}
/* The certificate is a moment, not a stage: a mark on the axis instead of a bar. */
.career-line .is-cert{position:relative}
.career-line .is-cert::before{content:"";position:absolute;inset-block:0;inset-inline-start:calc(var(--career-start) / var(--career-span) * 100%);
  width:var(--stroke-strong);margin-inline-start:-1px;background:var(--color-brand-signal)}
.career-line .is-cert>*{margin-inline-start:var(--space-4)}
.career-time{color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
.career-line strong{color:var(--color-foreground);font-weight:var(--font-weight-semibold)}
.career-employer{color:var(--color-muted-foreground)}
.cert-link{display:inline-flex;align-items:center;gap:var(--space-2);min-height:var(--size-target-web-touch);color:var(--color-link);text-underline-offset:3px}
.cert-link svg{width:var(--icon-sm);height:var(--icon-sm);flex:none}
.cert-link:hover{color:var(--color-link-hover);text-decoration:underline}
.career-axis{position:relative;container:career-axis/inline-size;height:var(--space-10);margin-top:var(--space-8);border-top:var(--stroke-default) solid var(--color-border)}
.career-axis span{position:absolute;top:0;left:calc(var(--career-at) / var(--career-span) * 100%);translate:-50% 0;padding-top:var(--space-3);
  color:var(--color-muted-foreground);font-variant-numeric:tabular-nums}
.career-axis span::before{content:"";position:absolute;top:0;left:50%;width:var(--stroke-default);height:calc(1.5 * var(--space-1));background:var(--color-border-strong)}
/* The year labels need room in their own font size, so the thresholds are in em: with 200 % text
   the axis thins out at twice the width. A label is about 2.3em wide; every second year needs the
   axis to be about 16em, every year about 32em. */
@container career-axis (max-width:36em){.career-axis span.is-odd{display:none}}
@container career-axis (max-width:18em){.career-axis span.is-minor{display:none}}

/* 404: one centred column on the hero's light, reachable from any depth (src/pages/404.astro). */
.missing-page{min-height:100svh;display:grid;grid-template-rows:1fr auto;place-items:center;background:var(--color-background)}
.missing{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-6);max-width:40rem;padding-block:var(--space-16)}
.missing h1{text-wrap:balance}
.missing p{color:var(--color-muted-foreground);max-width:52ch}
.missing-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-3) var(--space-6)}
.missing-link,.missing-en a{color:var(--color-link);text-underline-offset:3px}
.missing-link{display:inline-flex;align-items:center;min-height:var(--size-target-web-touch)}
.missing-link:hover,.missing-en a:hover{color:var(--color-link-hover);text-decoration:underline}
.missing-foot{display:flex;gap:var(--space-6);padding-block:var(--space-6)}
.missing-foot a{display:inline-flex;align-items:center;min-height:var(--size-target-web-touch);color:var(--color-muted-foreground);text-underline-offset:3px}
.missing-foot a:hover{color:var(--color-foreground);text-decoration:underline}
.missing-en{margin-top:var(--space-4);padding-top:var(--space-6);border-top:var(--stroke-default) solid var(--color-border)}

/* Kontakt */
.kontakt{margin-top:var(--space-8)}
.k-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-12);align-items:start}
.k-grid>*{min-width:0}
.k-grid h2{text-wrap:balance}
.k-lead{color:var(--color-muted-foreground);margin-top:var(--space-5);max-width:42ch}
.honeypot{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* Block-level flex that hugs its content: an inline-flex box cannot claim its own block
   margin, which is what let the recovery links collide on the server's result pages. */
.mailto{display:flex;width:fit-content;max-width:100%;align-items:center;gap:var(--space-2);min-height:var(--size-target-web-touch);margin-top:var(--space-6);font:var(--type-heading-3);color:var(--color-link);overflow-wrap:anywhere}
.mailto i{width:var(--space-2);height:var(--space-2);flex:none;border-radius:var(--radius-round);background:var(--color-brand-signal)}
.mailto:hover span{color:var(--color-link-hover);text-decoration:underline;text-underline-offset:3px}
form{
  display:flex;flex-direction:column;gap:var(--space-4);padding:var(--space-6);
  background:var(--color-surface);border:var(--stroke-default) solid var(--color-border);
  border-radius:var(--radius-lg);box-shadow:var(--elevation-sm);
  /* Same WebKit min-content leak as .cards: the select is sized by its widest option. The
     clip margin keeps the field focus ring, which spreads 3px outside the border box. */
  overflow-x:clip;overflow-clip-margin:var(--space-3);
}
.zeile{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4)}
.feld{display:flex;flex-direction:column;gap:var(--space-2);min-width:0}
.feld label{color:var(--color-muted-foreground)}
/* 16px keeps iOS Safari from zooming the viewport when a field takes focus. */
.feld input,.feld textarea,.feld select{
  font:inherit;font-size:16px;color:var(--color-foreground);background:var(--color-surface-inset);
  border:var(--stroke-default) solid var(--color-border-strong);border-radius:var(--radius-md);
  padding:var(--space-3) var(--space-4);width:100%;min-height:var(--size-target-web-touch);
  transition:border-color var(--motion-duration-fast) var(--motion-easing-standard),background-color var(--motion-duration-fast) var(--motion-easing-standard);
}
.feld textarea{resize:vertical;min-height:var(--size-textarea-md)}
/* The native arrow hugs the border in Chromium and ignores padding. Our own caret sits one
   field inset from the edge, like the text on the left; background-color on focus keeps it. */
.feld select{
  appearance:none;padding-right:var(--space-10);text-overflow:ellipsis;
  --caret:var(--color-foreground);
  background-image:linear-gradient(45deg,transparent 50%,var(--caret) 50%),linear-gradient(135deg,var(--caret) 50%,transparent 50%);
  background-position:calc(100% - var(--space-4) - 5px) 50%,calc(100% - var(--space-4)) 50%;
  background-size:5px 5px;background-repeat:no-repeat;cursor:pointer;
}
/* Forced colors drop background images, so the native arrow takes over there. */
@media (forced-colors:active){.feld select{appearance:auto;padding-right:var(--space-4)}}
.feld input:focus,.feld textarea:focus,.feld select:focus{outline:none;background-color:var(--color-surface);border-color:var(--color-focus);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-focus) 24%,transparent)}
/* Let the browser track interaction; never show errors while editing. */
.field-hint,.field-error{font:var(--type-caption);margin:0}
.field-hint{color:var(--color-muted-foreground)}
#privacy-hinweis a{overflow-wrap:anywhere}
.field-error{display:none;color:var(--color-danger-foreground)}
.feld :is(input,textarea,select):user-invalid:not(:focus){border-color:var(--color-danger-foreground);box-shadow:0 0 0 1px var(--color-danger-foreground)}
.feld :is(input,textarea):user-invalid:not(:focus)~.field-error{display:block}
.hinweis{font:var(--type-caption);color:var(--color-muted-foreground)}
#anfrage[aria-busy="true"] .pill{cursor:progress;opacity:.72}
#form-hinweis[data-state="error"]{color:var(--color-danger-foreground)}
#form-hinweis[data-state="success"]{color:var(--color-success-foreground)}

/* Fuß */
.foot{padding:0 0 var(--space-12)}
.foot-in{border-top:var(--stroke-default) solid var(--color-border);padding-top:var(--space-8);display:flex;flex-wrap:wrap;gap:var(--space-4);justify-content:space-between;align-items:center;font:var(--type-body);color:var(--color-muted-foreground)}
.foot-links{display:flex;flex-wrap:wrap;gap:var(--space-4) var(--space-6);margin-left:auto;min-width:0}
.foot-links a{display:inline-flex;align-items:center;min-height:var(--size-target-web-touch);color:var(--color-muted-foreground);text-decoration:underline}
.foot-links a:hover{color:var(--color-link)}
.foot-links [aria-current="page"]{color:var(--color-foreground);text-decoration:underline;text-underline-offset:4px}

/* Reveal */
.rv{opacity:1;transform:none}
.reveal-ready .rv{opacity:0;transform:translateY(var(--motion-distance-medium));transition:opacity var(--motion-duration-page) var(--motion-easing-enter),transform var(--motion-duration-page) var(--motion-easing-enter)}
.reveal-ready .rv.on{opacity:1;transform:none}
/* Higher specificity than .reveal-ready .rv.on so the hover lift still applies once a card has revealed. */
@media (hover:hover){.reveal-ready .card.rv.on:hover{transform:translateY(calc(-1 * var(--motion-hover-lift)))}}

/* Rechtliche Seiten und native Kontakt-Antworten */
.legal-page{min-height:100svh;display:flex;flex-direction:column}
/* Legal navigation: only the full-label back action is persistent. The logo and native
   settings disclosure remain in document flow so the reader retains the viewport budget
   at enlarged text without hiding, clipping, or scrolling controls inside the chrome. */
.legal-header{display:flex;align-items:center;padding-block:var(--space-2)}
/* A minimum 32px Firefox font preference makes the full-label action wrap at 320px.
   Remove only the decorative block inset while the viewport is too short for the normal
   16px total inset; the 44px target and authored label typography remain unchanged. */
@media (max-height:521px){.legal-header{padding-block:0}}
.legal-support{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:var(--space-4);padding-block:var(--space-4)}
/* The appearance control on legal pages behaves exactly as on the marketing
   pages — the toggle is the fixed viewport control and the open menu is the shared 260px
   popover anchored under it. Keeping picker and menu in document flow would stretch the open
   panel across the full content width and push the toggle below the logo. */
.legal-page .legal-support details:not([open]) .theme-menu{display:none}
.legal-page .legal-support .language-switch{flex-wrap:wrap}
.legal-page .legal-support .language-switch a{min-width:0;flex-basis:auto}
/* Keep fragment and focused targets clear of a wrapped native-sized back label without a
   scroll listener. The em fallback grows with real browser text resizing, not only root scale. */
html:has(body.legal-page){scroll-padding-block-start:max(calc(var(--size-target-web-touch) + 3 * var(--space-2)),6em)}
.legal-page :is(a,input,summary,h1,h2){scroll-margin-block-start:max(calc(var(--size-target-web-touch) + 3 * var(--space-2)),6em)}
.legal-content{max-width:var(--size-content-reading);padding-block:clamp(2.5rem,7vw,5.5rem);flex:1;overflow-wrap:anywhere}
.legal-content h1{margin-top:var(--space-4)}
.legal-content h1+p{margin-top:var(--space-4)}
.legal-content section{margin-top:var(--space-10)}
.legal-content h2{margin-bottom:var(--space-4)}
.legal-content p+p{margin-top:var(--space-4)}
/* Scoped away from .pill: the reading-link recipe would otherwise repaint the primary
   recovery button's label in --color-link over --gradient-primary-action (1.09:1 in Light). */
.legal-content a:not(.pill){text-decoration:underline;text-underline-offset:3px;color:var(--color-link)}
.legal-content a:not(.pill):hover{color:var(--color-link-hover)}
/* Pre-publication notices use the warning status pair: they flag content to verify, not an error. */
.legal-pending{background:var(--color-warning);border-left:3px solid var(--color-warning-foreground);color:var(--color-warning-foreground);padding:var(--space-4) var(--space-5);border-radius:0 var(--radius-md) var(--radius-md) 0}
.legal-pending,.legal-source{font:var(--type-body-small)}
.legal-source{margin-top:var(--space-10);color:var(--color-muted-foreground)}
/* Sticky "Zur Startseite" bar. An opaque elevated surface, not a translucent
   Frozen material: the header sits over scrolling body copy the whole time, so it needs
   guaranteed contrast rather than the marketing bar's glass treatment. */
.legal-header-bar{position:sticky;top:0;z-index:var(--layer-sticky);background:var(--color-surface-elevated);border-bottom:var(--stroke-default) solid var(--color-border);box-shadow:var(--elevation-sm)}

/* Cross-document view transitions between Home, Impressum and Datenschutz.
   Pure progressive enhancement: browsers without support simply navigate as before, with
   no flash, no blocked paint and no JS requirement. Gating the whole at-rule behind
   no-preference means a reduced-motion user gets the plain, un-transitioned navigation the
   browser already provides — nothing left to separately disable. */
@media (prefers-reduced-motion:no-preference){
  @view-transition{navigation:auto}
  /* Restrained: a brief cross-fade, plus the logo carried over as one shared element
     rather than a dramatic slide. Uses the same duration/easing as the rest of the UI. */
  ::view-transition-group(root){animation-duration:var(--motion-duration-standard);animation-timing-function:var(--motion-easing-standard)}
  .logo{view-transition-name:site-logo}
}

/* Contact result status (server formResponse): semantic success/error pair, icon only (title already states the outcome). */
.result-status{width:var(--size-avatar-md);height:var(--size-avatar-md);display:flex;align-items:center;justify-content:center;border-radius:var(--radius-round);margin-bottom:var(--space-4)}
.result-status svg{width:var(--icon-md);height:var(--icon-md);stroke:currentColor;stroke-width:var(--icon-stroke);fill:none;stroke-linecap:round;stroke-linejoin:round}
.result-status[data-state="success"]{background:var(--color-success);color:var(--color-success-foreground)}
.result-status[data-state="error"]{background:var(--color-danger);color:var(--color-danger-foreground)}
/* Recovery block: the email link and the back-to-contact button are two separate
   destinations and must not touch. */
.result-recovery{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-6);margin-top:var(--space-6)}
.result-recovery .mailto{margin-top:0}

/* Opaque fallbacks: no backdrop-filter support, reduced transparency or more contrast. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav-in,.theme-toggle{background:var(--color-surface-elevated);border-color:var(--color-border);box-shadow:var(--elevation-sm)}
  .nav-in::before,.theme-toggle::after{display:none}
}
@media (prefers-contrast:more),(prefers-reduced-transparency:reduce){
  .nav-in,.theme-toggle{background:var(--color-surface-elevated);border-color:var(--color-border);box-shadow:var(--elevation-sm);backdrop-filter:none;-webkit-backdrop-filter:none}
  .nav-in::before,.theme-toggle::after{display:none}
}
@media (forced-colors:active){
  .nav-in::before,.theme-toggle::after{display:none}
}

/* Glow motion: anchored ambient breathing — no lateral travel. The pulse is
   intentionally subtle and slow, with a wider minimum scale so the glow never collapses.
   Reduced motion removes every animation (blanket rule at the end of this file). */
@keyframes glowDriftHero{
  0%{opacity:.64;transform:scale(.94)}
  25%{opacity:.80;transform:scale(1.03)}
  50%{opacity:.88;transform:scale(1.08)}
  75%{opacity:.78;transform:scale(1.03)}
  100%{opacity:.66;transform:scale(.95)}
}
@media (prefers-reduced-motion:no-preference){
  .hero .wrap::after{animation:glowDriftHero var(--glow-drift-duration) ease-in-out infinite alternate}
}

/* Responsive: marketing sections stack at the approved compact/expanded boundary. */
@media (max-width:1023.98px){
  .nav{left:0;right:64px;padding-left:20px;padding-right:8px}
  .nav-in{width:100%;display:grid;grid-template-columns:1fr auto 1fr;align-items:center}
  .nav-in>.logo{grid-column:1;grid-row:1}
  .nav-links{display:none}
  .nav-menu{display:block;grid-column:3;grid-row:1;justify-self:end}
  .nav .pill{grid-column:2;grid-row:1;margin:0}
  .theme-picker{right:12px}
  .person,.k-grid{grid-template-columns:1fr}
  .portrait{width:min(100%,340px);margin-inline:auto}
  .person .ptext{order:-1}
  .career{grid-template-columns:minmax(0,1fr);margin-top:var(--space-16)}
  .career-intro p{margin-top:calc(-1 * var(--space-2))}
  /* Late entries would squeeze into the last third: they end at their point on the axis instead. */
  .career-line{gap:var(--space-6)}
  .career-line .is-late{align-items:flex-end;text-align:end;padding-inline-start:0;
    padding-inline-end:calc((var(--career-span) - var(--career-end)) / var(--career-span) * 100%)}
  .career-line .is-late .career-bar{align-self:flex-end;width:calc((var(--career-end) - var(--career-start)) / var(--career-end) * 100%);max-width:none}
  .career-line .is-cert>*{margin-inline:0 var(--space-4)}
  .career-line .is-cert::before{inset-inline-start:auto;inset-inline-end:calc((var(--career-span) - var(--career-start)) / var(--career-span) * 100%);margin-inline:0 -1px}
}
@media (max-width:767.98px){
  .zeile{grid-template-columns:1fr}
  form{padding:var(--space-5)}
}
@media (max-width:560px){
  .section{padding-block:var(--space-10)}
  /* The compact block above already sets the bar width, inset and logo margin; only the
     tighter top offset and the CTA's own metrics are new at this width. */
  .nav{top:10px}
  .theme-picker{top:18px;right:12px}
  .nav-in{padding-left:16px}
  /* Logo, CTA and menu share one row and the CTA stays centred, so the logo column may be no
     wider than the menu column: no link padding, letters about as tall as the old text mark. */
  .nav .logo{margin-right:0;padding-inline:0}
  .nav .logo-mark{--logo-height:30px}
  .nav .pill{padding-inline:12px;font-size:13px}
  .hero-cta{flex-direction:column;align-items:stretch}
  .hero-cta .pill{width:100%}
}
/* At the narrowest width, hide the navbar CTA: logo stays left and the menu stays right. */
@media (max-width:427.98px){
  :root{--nav-height:54px}
  .nav{right:64px;padding:0 8px 0 10px;top:10px}
  .theme-picker{top:10px;right:12px}
  .nav-in{grid-template-columns:1fr auto;gap:2px;padding:4px 6px}
  .nav .pill{display:none}
  .nav .logo-mark{--logo-height:36px}
  .nav-menu{grid-column:2}
  .nav-menu-toggle{padding:0}
  .nav-menu-toggle>span:last-child{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
}
/* The Frozen Reflection Shift / reflection travel must be disabled under reduced motion.
   This blanket rule implements that requirement; do not narrow the selector without ensuring
   .nav-in and the AuroraFields remain covered. */
@media (prefers-reduced-motion:reduce){
  /* The Frozen Reflection Shift / reflection travel must be disabled under reduced motion.
     This blanket selector ensures no future narrowing of this rule regresses the requirement. */
  *,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
  .reveal-ready .rv{opacity:1;transform:none}
  .word span{transform:none}
  .hero-sub,.hero-cta,.nav-in{opacity:1;transform:none}
}
