/* ==========================================================================
   the world of dizzi - one stylesheet, no framework, no JavaScript.
   Palette taken verbatim from _netzwerk/SYSTEM_KARTE.html in the public
   repository, so the site and the map read as one piece of work.
   ========================================================================== */

:root{
  --bg:#080b12; --bg2:#0d1119; --panel:#111726; --panel2:#151c2d; --panelh:#1a2440;
  /* The lit top edge of every card. A surface a reader can see but a gate
     cannot is a gate that passes for the wrong reason, so the brightest value
     the card gradient actually reaches is a token like any other - and
     pruefung/kontrast.py measures text against it, not only against --panel. */
  --panel-hi:#181f30;
  --tx:#e8ebf2; --tx2:#9aa4b6; --tx3:#6a7488;
  --line:rgba(255,255,255,.09); --line2:rgba(255,255,255,.16);
  --mag:#ff3ea5; --cy:#2ee6d6; --vio:#8b6cff; --amber:#f5b942; --coral:#f2645a;
  --green:#36d399; --orange:#fb923c; --blue:#38bdf8;
  --grad:linear-gradient(90deg,#ff3ea5 0%,#8b6cff 50%,#2ee6d6 100%);
  --rad:14px; --rad-s:9px; --rad-l:20px; --maxw:1220px;

  /* One surface recipe for every raised box on the site: the panel colour with
     a light source above it. Two stops, three percent - enough that a card has
     a top and a bottom, far too little to read as a gradient. */
  --flaeche:linear-gradient(180deg,var(--panel-hi) 0,var(--panel) 46%);
  --flaeche-h:linear-gradient(180deg,var(--panelh) 0,var(--panel2) 62%);
  /* Shadows sit under the box, never around it: on a dark page a glow reads as
     fog, an offset shadow reads as height. */
  --schatten:0 1px 2px rgba(0,0,0,.34),0 10px 26px -20px rgba(0,0,0,.9);
  --schatten-h:0 2px 4px rgba(0,0,0,.36),0 20px 44px -26px rgba(0,0,0,1);
  /* One curve and one duration for the whole site. Everything that moves on
     hover or focus borrows them, so nothing is faster or slower than the rest
     for no reason. */
  --ueb:.18s cubic-bezier(.22,.61,.36,1);

  --display:"Orbitron","Orbitron Fallback",system-ui,sans-serif;
  --text:"Chakra Petch","Chakra Fallback",system-ui,sans-serif;
  --mono:ui-monospace,"Cascadia Mono","Consolas",monospace;
  --spur:clamp(48px,8vw,96px);   /* vertical section rhythm */
}

/* --- fonts ---------------------------------------------------------------
   Subset from the SIL OFL sources (see werkzeug/schriften.ps1); the licence
   texts sit next to the files in /schrift/. The two Fallback faces carry
   metrics measured from the real fonts, so the swap costs no layout shift. */

@font-face{
  font-family:"Orbitron";
  src:url("/schrift/orbitron-var.woff2") format("woff2");
  font-weight:400 900; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Chakra Petch";
  src:url("/schrift/chakra-400.woff2") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Chakra Petch";
  src:url("/schrift/chakra-600.woff2") format("woff2");
  font-weight:600; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Chakra Fallback";
  src:local("Arial"),local("Helvetica"),local("Liberation Sans");
  size-adjust:104.1%; ascent-override:95.3%; descent-override:29.6%; line-gap-override:0%;
}
@font-face{
  font-family:"Orbitron Fallback";
  src:local("Arial"),local("Helvetica"),local("Liberation Sans");
  size-adjust:129%; ascent-override:78.4%; descent-override:18.8%; line-gap-override:0%;
}

/* --- base ---------------------------------------------------------------- */

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
/* Der Fliesstext in rem, nicht in px. 15px waren an die Schriftgroesse des
   Geraetes NICHT gebunden: wer im Browser oder im Telefon eine groessere
   Grundschrift einstellt - der haeufigste Handgriff aelterer Augen -, bekam
   auf dieser Seite trotzdem 15px und musste zoomen. .9375rem sind bei der
   Voreinstellung von 16px exakt dieselben 15px, also aendert sich fuer alle
   anderen nichts; bei 20px Grundschrift werden daraus 18,75px. Ueberschriften,
   Chips und Etiketten bleiben absichtlich in px: sie sind Gestalt, nicht Text,
   und sollen die Zeile nicht sprengen, wenn der Fliesstext waechst. */
body{
  margin:0; background:var(--bg); color:var(--tx);
  font:400 .9375rem/1.6 var(--text);
  text-rendering:optimizeLegibility;
  overflow-wrap:break-word;
}
img,svg{max-width:100%;height:auto}
a{color:var(--cy);text-decoration-thickness:1px;text-underline-offset:3px;
  transition:color var(--ueb)}
a:hover{color:var(--tx)}
:focus-visible{outline:2px solid var(--cy);outline-offset:2px;border-radius:3px}
h1,h2,h3,h4{font-family:var(--display);font-weight:700;line-height:1.14;margin:0;
  letter-spacing:-.01em;
  /* Headings are short and set large, which is exactly where a lone word on
     the last line shows. `balance` is a hint: a browser that does not know it
     wraps as before, so nothing depends on it. */
  text-wrap:balance}
h1{font-size:clamp(30px,6vw,52px)}
h2{font-size:clamp(26px,4.2vw,34px)}
h3{font-size:clamp(20px,2.6vw,24px)}
/* A heading used to sit flush against the text under it - zero margin, so the
   first line of a section began where its title ended and the page read as one
   undifferentiated column. The space belongs to the heading, not to a utility
   class on whatever follows: adjacent margins collapse, so where a .mt-* class
   already sets a larger gap it still wins and no page shifts. */
h2:not(:last-child){margin-bottom:18px}
h3:not(:last-child){margin-bottom:10px}
p{margin:0 0 1em;text-wrap:pretty}
ul,ol{margin:0 0 1em;padding-left:1.25em}
li{margin:.3em 0}
code,kbd{font-family:var(--mono);font-size:.88em}
hr{border:0;border-top:1px solid var(--line);margin:var(--spur) 0}
::selection{background:var(--vio);color:#fff}

.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}

.skip{position:absolute;left:12px;top:-60px;z-index:20;background:var(--panelh);
  color:var(--tx);border:1px solid var(--cy);border-radius:var(--rad);
  padding:10px 16px;font-family:var(--display);font-size:13px;transition:top .15s}
.skip:focus{top:12px}

/* --- layout -------------------------------------------------------------- */

.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:clamp(16px,4vw,32px)}
section,.band{padding-block:var(--spur)}
section+section,section+.band,.band+section,.band+.band{border-top:1px solid var(--line)}
/* WCAG 2.2 target-size: a stand-alone source link is not "inline in a
   sentence", so it needs a real 24px hit area rather than the 19px the glyphs
   happen to occupy. max-width keeps the inline-block from ever pushing past
   its container - without it a long label could not wrap and would force a
   sideways scroll, which would trade one finding for a worse one. */
.quellen a,.fuss-status a,a.zielgross{display:inline-block;max-width:100%;
  min-height:24px;padding-block:2px}
.eng{max-width:72ch}
.lang{font-size:1.0625rem;line-height:1.68}   /* 17px bei 16px Grundschrift */
.lang p{margin-bottom:1.15em}

/* The eyebrow above a section heading carries the house gradient as a short
   rule. It is the one ornament that repeats on every page, which is what makes
   twelve pages read as one site rather than twelve. Decoration only - it is a
   pseudo-element, so it is not in the accessibility tree and not in the text. */
.kicker{display:flex;align-items:center;gap:11px;
  font-family:var(--display);font-size:12px;font-weight:600;letter-spacing:.18em;
  text-transform:uppercase;color:var(--cy);margin:0 0 14px}
.kicker::before{content:"";flex:none;width:22px;height:2px;border-radius:2px;
  background:var(--grad)}
.lede{font-size:clamp(17px,2.2vw,20px);line-height:1.55;color:var(--tx2);max-width:66ch;
  margin:0 0 1.2em}
.dach{color:var(--tx2);max-width:72ch}

/* --- header / nav -------------------------------------------------------- */

.kopf{border-bottom:1px solid var(--line);background:var(--bg2)}
.kopf .wrap{display:flex;flex-wrap:wrap;gap:10px 24px;align-items:center;
  padding-block:14px}
.marke{font-family:var(--display);font-weight:800;font-size:17px;letter-spacing:.01em;
  text-decoration:none;background:var(--grad);-webkit-background-clip:text;
  background-clip:text;color:transparent;padding:6px 2px;white-space:nowrap}
.marke:hover{filter:brightness(1.18)}
@supports not (background-clip:text){.marke{color:var(--tx);background:none}}
/* The wordmark paints its glyphs with the house gradient, so its `color` is
   transparent. Rendered it measures 5.11:1 to 12.05:1 against the header - but
   a contrast checker sees only the transparent value, and forced-colors mode
   would drop the painted gradient and leave nothing behind. Both cases get a
   real colour back. */
@media (forced-colors:active){
  .marke{color:CanvasText;background:none;-webkit-text-fill-color:CanvasText}
  .led{forced-color-adjust:none}
}

.nav{margin-left:auto}
.nav ul{display:flex;flex-wrap:wrap;gap:2px 4px;list-style:none;margin:0;padding:0}
.nav a{display:block;padding:8px 11px;min-height:24px;border-radius:9px;
  font-size:14px;color:var(--tx2);text-decoration:none;position:relative}
.nav a:hover{color:var(--tx);background:var(--panel)}
.nav a[aria-current]{color:var(--tx)}
.nav a[aria-current]::after,.nav a:hover::after{content:"";position:absolute;
  left:11px;right:11px;bottom:3px;height:2px;background:var(--grad);border-radius:2px}
.nav a:hover::after{opacity:.55}
.nav .nav-karte{color:var(--cy)}
.nav-lang{color:var(--tx2)}

/* --- footer -------------------------------------------------------------- */

/* The footer closes the page, so its top edge is the one hairline on the site
   that carries the house gradient instead of a grey line - dimmed far enough
   that it reads as a seam and not as a rule. */
.fuss{position:relative;border-top:1px solid var(--line);background:var(--bg2);
  padding-block:36px;margin-top:var(--spur);font-size:14px}
.fuss::before{content:"";position:absolute;left:0;right:0;top:-1px;height:1px;
  background:var(--grad);opacity:.34}
.fuss .wrap{display:grid;gap:22px}
.fuss-oben{display:flex;flex-wrap:wrap;gap:14px 28px;align-items:center}
.fuss nav ul{display:flex;flex-wrap:wrap;gap:2px 6px;list-style:none;margin:0;padding:0}
.fuss nav a{display:block;padding:6px 8px;min-height:24px;color:var(--tx2);
  text-decoration:none}
.fuss nav a:hover{color:var(--tx);text-decoration:underline}
.fuss-status{color:var(--tx2)}
.fuss-fein{color:var(--tx2);font-size:13px;display:grid;gap:6px;
  border-top:1px solid var(--line);padding-top:18px}

/* --- buttons ------------------------------------------------------------- */

.knopf{display:inline-flex;align-items:center;gap:9px;min-height:44px;
  padding:11px 20px;border-radius:var(--rad);border:1px solid var(--line2);
  background:var(--flaeche);color:var(--tx);font-family:var(--display);
  font-weight:600;font-size:14px;text-decoration:none;box-shadow:var(--schatten);
  transition:border-color var(--ueb),background var(--ueb),box-shadow var(--ueb),
    translate var(--ueb)}
/* The secondary button used to set its hover border to the colour it already
   had, so only the fill moved. Both move now, and the arrow moves with them. */
.knopf:hover{color:var(--tx);border-color:var(--tx3);background:var(--flaeche-h);
  box-shadow:var(--schatten-h);translate:0 -1px}
.knopf.prim{border-color:var(--cy);color:var(--tx)}
.knopf.prim:hover{background:var(--flaeche-h);border-color:var(--cy);
  box-shadow:0 0 0 1px var(--cy),0 12px 32px -14px var(--cy)}
.knopf .pfeil{color:var(--cy);font-size:15px;transition:translate var(--ueb)}
.knopf:hover .pfeil{translate:2px 0}
.knoepfe{display:flex;flex-wrap:wrap;gap:12px;margin:26px 0 0}

/* --- status strip -------------------------------------------------------- */

.leiste{display:flex;flex-wrap:wrap;align-items:center;gap:6px 14px;
  margin-top:30px;padding:12px 16px;border:1px solid var(--line);
  border-radius:var(--rad);background:var(--bg2);
  box-shadow:inset 0 1px 0 rgba(0,0,0,.35);
  font-family:var(--mono);font-size:12.5px;color:var(--tx2);
  font-variant-numeric:tabular-nums}
.leiste b{font-family:var(--display);font-size:11px;letter-spacing:.16em;
  color:var(--tx);font-weight:700}
.leiste span{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}
.led{width:7px;height:7px;border-radius:50%;background:var(--green);
  box-shadow:0 0 0 3px rgba(54,211,153,.14);flex:none}
.led.vio{background:var(--vio);box-shadow:0 0 0 3px rgba(139,108,255,.14)}
.leiste .trenner{color:var(--tx3)}

/* --- proof links: every number on this site is one click from its source -- */

.beleg{display:inline-flex;align-items:center;justify-content:center;
  min-width:24px;min-height:24px;margin-left:2px;border-radius:6px;
  font-family:var(--mono);font-size:12px;line-height:1;color:var(--tx2);
  text-decoration:none;vertical-align:baseline;translate:0 3px}
.beleg:hover,.beleg:focus-visible{color:var(--cy);background:var(--panel)}

/* --- number tiles -------------------------------------------------------- */

.zahlen{display:grid;gap:14px;
  grid-template-columns:repeat(auto-fit,minmax(min(190px,100%),1fr));
  list-style:none;margin:0;padding:0}
.zahl{padding:20px 18px;border:1px solid var(--line);border-radius:var(--rad);
  background:var(--flaeche);box-shadow:var(--schatten);margin:0;
  transition:border-color var(--ueb),box-shadow var(--ueb)}
.zahl:hover{border-color:var(--line2);box-shadow:var(--schatten-h)}
/* Figures are set in a face with proportional digits by default, so a column
   of them staggers. Tabular numerals line the digits up under each other -
   the one typographic setting a page of measurements cannot do without. */
.zahl b{display:block;font-family:var(--display);font-weight:800;
  font-size:clamp(28px,4vw,36px);line-height:1.05;color:var(--tx);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.zahl span{display:block;margin-top:7px;font-size:13.5px;color:var(--tx2)}

/* --- cards / bento ------------------------------------------------------- */

.karten{display:grid;gap:16px;
  grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr))}
.karte{padding:22px 20px;border:1px solid var(--line);border-radius:var(--rad);
  background:var(--flaeche);box-shadow:var(--schatten);
  transition:border-color var(--ueb),box-shadow var(--ueb)}
.karte:hover{border-color:var(--line2);box-shadow:var(--schatten-h)}
.karte h3{font-size:19px;margin-bottom:10px}
.karte p:last-child{margin-bottom:0}
.karte p{color:var(--tx2)}

.bento{display:grid;gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(min(215px,100%),1fr));
  list-style:none;margin:0;padding:0}
.bento li{margin:0}
.kachel{display:flex;flex-direction:column;gap:8px;height:100%;padding:16px 16px 14px;
  border:1px solid var(--line);border-left:3px solid var(--akz,var(--cy));
  border-radius:var(--rad);background:var(--flaeche);color:var(--tx);
  text-decoration:none;box-shadow:var(--schatten);
  transition:background var(--ueb),border-color var(--ueb),translate var(--ueb),
    box-shadow var(--ueb)}
/* The lift is small on purpose. A tile that jumps announces itself; a tile that
   rises by three pixels answers. The shadow it casts is tinted with the app's
   own accent, which is the only place the accent leaves its edge. */
.kachel:hover,.kachel:focus-visible{background:var(--flaeche-h);color:var(--tx);
  border-color:var(--line2);translate:0 -3px;
  box-shadow:var(--schatten-h),0 16px 34px -24px var(--akz,var(--cy))}
.kachel .kn{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
  font-family:var(--display);font-weight:700;font-size:15px}
.kachel .kt{font-size:13.5px;color:var(--tx2);flex:1}
.kachel .kf{font-family:var(--mono);font-size:12px;color:var(--akz,var(--cy));
  font-variant-numeric:tabular-nums}
.chip{display:inline-block;padding:2px 8px;border:1px solid var(--line2);
  border-radius:999px;background:var(--bg2);font-family:var(--mono);
  font-size:11.5px;color:var(--tx2);white-space:nowrap;
  font-variant-numeric:tabular-nums}

/* --- law quote ----------------------------------------------------------- */

.gesetz{position:relative;margin:0 0 20px;padding:18px 20px 18px 24px;
  border:1px solid var(--line);border-radius:var(--rad);background:var(--flaeche);
  box-shadow:var(--schatten);overflow:hidden}
.gesetz::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--grad)}
.gesetz .lk{display:block;font-family:var(--display);font-size:11px;font-weight:700;
  letter-spacing:.18em;text-transform:uppercase;color:var(--cy);margin-bottom:9px}
/* A quoted law is the one place on this site where the text itself is the
   object being shown, so it gets the larger size and the looser line. */
.gesetz p{font-size:17px;line-height:1.55;margin:0}
.gesetz footer{margin-top:11px;font-size:13px;color:var(--tx2)}

/* --- timeline ------------------------------------------------------------ */

.strahl{list-style:none;margin:0;padding:0 0 0 26px;position:relative}
.strahl::before,.strahl::after{content:"";position:absolute;left:5px;top:6px;bottom:6px;
  width:1px}
.strahl::before{background:linear-gradient(180deg,var(--line2),var(--line))}
/* The chronicle had a rail drawn by nothing while the method page next door had
   one drawn by the reader. Same mechanism, same guard, same two exits below -
   the dated list is exactly the kind of thing a reader walks down, so it gets
   the same treatment instead of a second invention. Hidden until a scroll
   timeline exists: a rail frozen at zero states something untrue. */
.strahl::after{display:none;transform-origin:top center;
  background:linear-gradient(180deg,var(--mag),var(--vio) 55%,var(--cy))}
.strahl>li{position:relative;margin:0 0 26px;padding:0}
.strahl>li::before{content:"";position:absolute;left:-24px;top:7px;z-index:1;
  width:9px;height:9px;
  border-radius:50%;background:var(--bg);border:2px solid var(--akz,var(--vio))}
.strahl .datum{display:inline-block;margin-bottom:7px;padding:2px 9px;
  border:1px solid var(--line2);border-radius:999px;background:var(--bg2);
  font-family:var(--mono);font-size:11.5px;color:var(--tx2)}
.strahl h3{font-size:18px;margin-bottom:6px}
.strahl p{color:var(--tx2);margin-bottom:0;max-width:68ch}

/* --- the track: one work package, station by station ---------------------
   A rail with stations beside it, and a second rail on top of the first that
   is drawn by the reading position instead of by a clock. The drawn part sits
   in the motion section further down, behind the same @supports guard as
   everything else here: a browser without scroll timelines shows the dim rail
   and every station, which is the entire content. Nothing in this block
   carries text - take the whole rail away and the list still reads. */

.bahn{list-style:none;margin:0;padding:0 0 0 30px;position:relative}
.bahn::before,.bahn::after{content:"";position:absolute;left:7px;top:12px;bottom:12px;
  width:2px;border-radius:2px}
.bahn::before{background:var(--line2)}
/* hidden until a timeline exists - see the note at .fortschritt */
.bahn::after{display:none;transform-origin:top center;
  background:linear-gradient(180deg,var(--mag),var(--vio) 55%,var(--cy))}
.bahn>li{position:relative;margin:0 0 14px;padding:18px 18px;
  border:1px solid var(--line);border-radius:var(--rad);background:var(--flaeche);
  box-shadow:var(--schatten)}
.bahn>li:last-child{margin-bottom:0}
/* the ring is the page colour, so the rail is interrupted at every station
   without a second element to do it; z-index keeps the dots above the rail,
   which is painted after them as the parent's ::after */
.bahn>li::before{content:"";position:absolute;left:-28px;top:23px;z-index:1;
  width:12px;height:12px;border-radius:50%;background:var(--akz,var(--vio));
  box-shadow:0 0 0 4px var(--bg)}
.bahn h3{font-size:17px;margin-bottom:8px}
.bahn p{margin-bottom:0;font-size:14.5px;color:var(--tx2);max-width:70ch}
.bahn p+p{margin-top:.85em}
.bahn .quellen{margin-top:11px;font-size:13.5px}

/* --- stage list (roadmap without promised dates) ------------------------- */

.stufen{display:grid;gap:12px;
  grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr));
  list-style:none;margin:24px 0 0;padding:0;counter-reset:none}
.stufe{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;align-items:start;
  margin:0;padding:18px 18px;border:1px solid var(--line);border-radius:var(--rad);
  background:var(--flaeche);box-shadow:var(--schatten)}
.stufe.fertig{border-color:rgba(54,211,153,.42)}
.stufe.jetzt{border-color:var(--line2);background:var(--flaeche-h)}
.st-n{grid-row:1/3;display:flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:50%;border:1px solid var(--line2);
  background:var(--bg2);font-family:var(--display);font-size:13px;font-weight:700;
  color:var(--tx2)}
.stufe.fertig .st-n{border-color:var(--green);color:var(--green)}
.stufe.jetzt .st-n{border-color:var(--cy);color:var(--cy)}
.st-t{font-family:var(--display);font-weight:700;font-size:15.5px;align-self:center}
.st-b{font-size:14px;color:var(--tx2)}

/* --- measured figures, each with its method folded away ------------------
   A wall of text nobody reads is not honesty, it is cover. The figure stays
   in the open; the method sits one click behind a native <details>, which
   needs no script and keeps its state without one. The text stays in the
   document either way, and the print rules at the bottom of this file unfold
   every one of them - a page of figures that printed as six closed headings
   would be hiding exactly what it exists to show. */

.messung{display:grid;gap:14px;list-style:none;margin:0;padding:0}
.mess{margin:0;padding:20px 18px;border:1px solid var(--line);
  border-radius:var(--rad);background:var(--flaeche);box-shadow:var(--schatten);
  transition:border-color var(--ueb),box-shadow var(--ueb)}
.mess:hover{border-color:var(--line2);box-shadow:var(--schatten-h)}
.mess-kopf{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 14px;margin:0}
.mess-wert{font-family:var(--display);font-weight:800;line-height:1.1;color:var(--tx);
  font-size:clamp(26px,3.6vw,32px);font-variant-numeric:tabular-nums;
  letter-spacing:-.02em}
.mess-was{color:var(--tx2);font-size:15px}

.methode{margin-top:15px;border-top:1px solid var(--line);padding-top:13px}
.methode>summary{display:flex;align-items:center;gap:10px;min-height:24px;
  list-style:none;cursor:pointer;color:var(--cy);font-family:var(--display);
  font-size:11.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase}
.methode>summary::-webkit-details-marker{display:none}
.methode>summary::before{content:"+";display:inline-flex;align-items:center;
  justify-content:center;flex:none;width:19px;height:19px;border-radius:5px;
  border:1px solid var(--line2);background:var(--bg2);color:var(--tx2);
  font-family:var(--mono);font-size:14px;line-height:1}
.methode[open]>summary::before{content:"\2013"}
.methode>summary:hover{color:var(--tx)}
.methode>summary:hover::before{border-color:var(--cy);color:var(--cy)}
.methode-inhalt{margin-top:13px;font-size:14px;color:var(--tx2);max-width:72ch}
.methode-inhalt b{color:var(--tx)}
.methode-inhalt p:last-child{margin-bottom:0}

/* --- reading position ----------------------------------------------------
   Two pixels at the top edge that follow the document scroller. It is hidden
   by default and only switched on where a scroll timeline exists, so a
   browser without one never shows a bar frozen at zero. */

.fortschritt{display:none;position:fixed;inset:0 0 auto;height:2px;z-index:15;
  background:var(--grad);transform-origin:left center}
@media (forced-colors:active){
  .fortschritt{background:Highlight;forced-color-adjust:none}
}

/* --- code / contract block ---------------------------------------------- */

.code{margin:0 0 20px;padding:16px 18px;border:1px solid var(--line);
  border-radius:var(--rad);background:var(--bg2);overflow-x:auto;
  /* The one recessed surface on the site. Cards are lit from above and cast a
     shadow downward; a block of code is a hole in the page, so the light falls
     the other way. That is the whole distinction, and one inset line carries it. */
  box-shadow:inset 0 1px 0 rgba(0,0,0,.45)}
.code pre{margin:0;font-family:var(--mono);font-size:13px;line-height:1.7;
  color:var(--tx2);white-space:pre}
.code pre b{color:var(--tx);font-weight:400}
.code pre .co{color:var(--tx2)} /* was --tx3: measured 4.2:1 on --bg2, below the 4.5:1 that informing text needs; kontrast.py does not measure this pair yet */
.code figcaption{margin-top:12px;font-size:13px;color:var(--tx2)}

/* --- misc ---------------------------------------------------------------- */

address{font-style:normal;line-height:1.75}
.hinweis{padding:16px 18px;border:1px solid var(--line2);border-radius:var(--rad);
  background:var(--flaeche-h);color:var(--tx2);font-size:14.5px;
  box-shadow:var(--schatten)}
.hinweis b{color:var(--tx)}
/* min(...,100%) rather than a bare track floor: below the floor a bare minmax
   keeps the column at its full width and pushes the page sideways, and there is
   no overflow-x:hidden anywhere to catch it. Measured at a 280px viewport - the
   outer screen of a folding phone - six of the twelve pages scrolled sideways by
   16px and up to 24 boxes were cut off with no way to reach them. With the
   min() the track can never be wider than its container, so the grid falls to
   one column instead of overflowing. Nothing changes at 320px and above, where
   the floor still wins; this only decides what happens below it. */
.raster{display:grid;gap:26px 34px;
  grid-template-columns:repeat(auto-fit,minmax(min(280px,100%),1fr))}
.liste{list-style:none;padding:0;margin:0 0 1em}
.liste li{position:relative;padding-left:19px;color:var(--tx2)}
.liste li::before{content:"";position:absolute;left:2px;top:.65em;width:6px;height:1px;
  background:var(--cy)}
/* Ten sections built from one template read as one section printed ten times -
   the flattest stretch of the whole site. Each of them already carries its
   application's accent, because the tiles above need it, so the header borrows
   what is already there: a short bar in front of the name and the port chip
   outlined in the same colour, over a corner of tinted air. No markup changes,
   and the ten stop looking like one.
   The accent stays out of the TEXT on purpose. A bar and a border are non-text
   indicators and clear 3:1 against the page, which kontrast.py proves for all
   nine accents; several of them would not clear the 4.5:1 that 11.5px chip text
   needs, and no gate here measures a colour that is only set on a class. */
.app-abschnitt{position:relative;scroll-margin-top:24px;padding-block:var(--spur)}
.app-abschnitt+.app-abschnitt{border-top:1px solid var(--line)}
.app-abschnitt::before{content:"";position:absolute;z-index:-1;
  inset:0 auto auto 0;width:min(540px,100%);height:260px;pointer-events:none;
  background:radial-gradient(80% 70% at 0 0,
    color-mix(in srgb,var(--akz,var(--cy)) 7%,transparent),transparent 70%)}
.app-kopf{position:relative;display:flex;flex-wrap:wrap;align-items:baseline;
  gap:10px 14px;margin-bottom:16px;padding-left:17px}
.app-kopf::before{content:"";position:absolute;left:0;top:.14em;bottom:.14em;
  width:3px;border-radius:2px;background:var(--akz,var(--cy))}
.app-kopf h2{font-size:clamp(21px,3vw,26px);margin-bottom:0}
.app-kopf .chip:first-of-type{border-color:var(--akz,var(--cy))}
.figur{margin:0 0 8px;padding:0}
.figur .buehne{overflow-x:auto;overscroll-behavior-x:contain}
.figur svg{display:block;width:100%;min-width:520px;height:auto}
.figur figcaption{margin-top:12px;font-size:13.5px;color:var(--tx2)}

/* layer stack - reflows instead of scaling, so it stays legible at 320px */
.schichten{display:grid;gap:0}
.sch{padding:16px 18px;border:1px solid var(--line);
  border-left:3px solid var(--akz,var(--cy));border-radius:var(--rad);
  background:var(--flaeche);box-shadow:var(--schatten)}
.sch+.sch{margin-top:26px;position:relative}
.sch+.sch::before{content:"";position:absolute;left:50%;top:-21px;width:1px;height:16px;
  background:var(--line2)}
.sch+.sch::after{content:"";position:absolute;left:50%;top:-8px;translate:-50% 0;
  border:5px solid transparent;border-top-color:var(--line2);border-bottom:0}
.sch-t{font-family:var(--display);font-weight:700;font-size:16px;margin:0 0 6px;
  display:flex;flex-wrap:wrap;align-items:center;gap:9px}
.sch-b{margin:0;font-size:14.5px;color:var(--tx2);max-width:74ch}
.sch-g{display:flex;flex-wrap:wrap;gap:6px 10px;list-style:none;margin:10px 0 12px;
  padding:0;font-size:14px}
.sch-g li{display:flex;align-items:center;gap:7px;margin:0;color:var(--tx2)}
.trenn{color:var(--tx3);padding-inline:2px}

/* --- hero ---------------------------------------------------------------- */

.hero{position:relative;padding-block:clamp(52px,9vw,104px);overflow:hidden}
.hero::before{content:"";position:absolute;inset:0;z-index:-1;
  background:
    repeating-linear-gradient(90deg,rgba(255,255,255,.028) 0 1px,transparent 1px 68px),
    repeating-linear-gradient(0deg,rgba(255,255,255,.028) 0 1px,transparent 1px 68px),
    radial-gradient(120% 90% at 12% -10%,rgba(139,108,255,.16),transparent 62%),
    radial-gradient(90% 80% at 95% 0%,rgba(46,230,214,.10),transparent 60%);
  mask-image:linear-gradient(180deg,#000 0%,#000 45%,transparent 100%)}
.hero .strich{width:min(280px,52%);height:3px;background:var(--grad);border-radius:2px;
  margin:0 0 26px;transform-origin:left center}
/* Display type set this large needs its tracking pulled in; the value the rest
   of the headings use is measured for 26-34px, not for 52px. */
.hero h1{max-width:16ch;letter-spacing:-.024em}
.hero .lede{margin-top:20px}
/* The eyebrow in a hero sits under the gradient bar, which already says the
   same thing louder. It keeps its colour and drops its own rule. */
.hero .kicker::before{display:none}
.hero .kicker{gap:0}

/* --- motion: opt-in, compositor only, killed under reduced-motion --------
   Two mechanisms, no JavaScript, nothing that moves on its own. The top of a
   page arrives once when it loads; everything below it arrives as the reader
   reaches it and follows the scroll in both directions, so it can never
   surprise anyone. Only opacity and translate are animated - both run on the
   compositor and neither reflows the page. Nothing loops, nothing parallaxes,
   and no text moves while it is meant to be read. */

@media (prefers-reduced-motion:no-preference){
  .hero .strich{animation:strich .45s cubic-bezier(.22,.61,.36,1) both}
  .hero .kicker{animation:auf .45s cubic-bezier(.22,.61,.36,1) .04s both}
  /* the headline is the LCP element: it may glide, it may not hide - an
     opacity start of 0 plus delay pushed LCP a measured half second past FCP */
  .hero h1{animation:auf-gleit .45s cubic-bezier(.22,.61,.36,1) both}
  .hero .lede{animation:auf .45s cubic-bezier(.22,.61,.36,1) .18s both}
  .hero .dach,.hero .knoepfe{animation:auf .45s cubic-bezier(.22,.61,.36,1) .26s both}
  .hero .leiste{animation:auf .45s cubic-bezier(.22,.61,.36,1) .3s both}
  .hero .notiz{animation:auf .45s cubic-bezier(.22,.61,.36,1) .34s both}
}
@keyframes strich{from{opacity:0;scale:0 1}to{opacity:1;scale:1 1}}
@keyframes auf{from{opacity:0;translate:0 10px}to{opacity:1;translate:0 0}}
@keyframes auf-gleit{from{translate:0 12px}to{translate:0 0}}

@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    /* Each item carries its own timeline, so the stagger comes out of the
       layout instead of a list of hand-tuned delays: a row of cards arrives
       together, the row beneath it arrives when it is reached. */
    .reveal>*{animation:auftauchen linear both;animation-timeline:view();
      animation-range:entry 6% cover 30%}
    /* A section announces itself just before it explains itself. Direct
       children of .wrap only - the legal pages keep their headings still. */
    :is(section:not(.hero),.band)>.wrap>:is(.kicker,h2),
    .app-abschnitt>.app-kopf{animation:auftauchen linear both;
      animation-timeline:view();animation-range:entry 4% cover 24%}
    @keyframes auftauchen{from{opacity:0;translate:0 14px}to{opacity:1;translate:0 0}}

    .fortschritt{display:block;animation:balken linear both;
      animation-timeline:scroll(root block)}
    @keyframes balken{from{scale:0 1}to{scale:1 1}}

    /* The track is drawn by the reader rather than by a clock. Over the
       `contain` range the leading edge sits at the top of the screen when the
       list's first line reaches the top and at the bottom when its last line
       reaches the bottom - so it stays level with the station being read
       instead of running ahead of it. Same reason as the reading bar for
       being switched on only here: a rail frozen at zero says something
       untrue about where the reader is. */
    .bahn::after,.strahl::after{display:block;animation:schiene linear both;
      animation-timeline:view(block);animation-range:contain}
    @keyframes schiene{from{scale:1 0}to{scale:1 1}}
    /* Each station lights as it is reached, and dims again on the way back:
       opacity and scale only, both on the compositor, nothing that reflows. */
    .bahn>li::before,.strahl>li::before{animation:halt linear both;
      animation-timeline:view();animation-range:entry 18% cover 38%}
    @keyframes halt{from{opacity:.22;scale:.5}to{opacity:1;scale:1}}
  }
}
/* No cross-document view transitions - REMOVED 08.08.2026, and deliberately so.
   `@view-transition{navigation:auto}` made every link click snapshot the old
   page and blend to the new one. Inside in-app browsers (Instagram and its
   kind render this site inside a WebView) that snapshot path is exactly where
   the engine freezes: the old frame stays up, the new page never paints, and
   to the reader the site "hangs when a link is tapped" - observed live from
   Instagram on 08.08.2026. A page that reliably NAVIGATES beats a page that
   beautifully transitions, in every browser this site cannot choose. The
   in-page animations are unaffected; they never block navigation.           */

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:1ms!important;animation-iteration-count:1!important;
    transition-duration:1ms!important;scroll-behavior:auto!important}
  /* A full exit, not a fast version of the same thing: the animations are
     removed outright, so nothing ever starts at opacity 0 and nothing can be
     caught mid-flight. The reading bar goes with them - the browser's own
     scrollbar already says the same thing without moving anything - and so
     does the drawn rail of the track, for the same reason: what is left is
     the dim rail, every station lit, and no motion anywhere. */
  .hero>.wrap>*,.reveal>*,.wrap>.kicker,.wrap>h2,.app-kopf,
  .bahn>li::before,.strahl>li::before{animation:none!important}
  .fortschritt,.bahn::after,.strahl::after{display:none!important}
  .kachel:hover{translate:none}
}

/* Paper has no scroll, so a scroll timeline never advances on it and anything
   waiting to be revealed would print at zero opacity. The same exit as above,
   for the one medium that cannot scroll. */
@media print{
  .hero>.wrap>*,.reveal>*,.wrap>.kicker,.wrap>h2,.app-kopf,
  .bahn>li::before,.strahl>li::before{animation:none!important}
  .fortschritt,.bahn::after,.strahl::after{display:none!important}
  /* and every folded method is unfolded: on paper there is nothing to click */
  .methode-inhalt{display:block!important}
  .methode>summary::before{content:"\2013"}
}

/* ==========================================================================
   THE VISUAL TURN (07.08.2026) - everything the two start pages needed to
   stop being columns of prose. One block on purpose: it was written while
   another lane was rewriting /apps/ and /numbers/ in a second working tree,
   and a change that sits in one place is a change that merges in one place.
   Nothing above this line was touched.

   Three components and one animation:
     .tafel      a comparison that stopped being a paragraph
     .fluss      three nodes and the gate between them
     .ring-halo  the one thing on this site that breathes
   ========================================================================== */

/* --- tables --------------------------------------------------------------
   Which figure can still be checked, and what each of the ten applications
   does, are comparisons. Written as sentences they make the reader hold the
   columns in their own head; a real <table> with real <th scope> hands that
   work to the browser and to the screen reader instead. The rows carry the
   same nine accents the tiles carried, so the colour system survives the
   change of element.
   The frame scrolls, never the page: two columns wrap down to 320px on their
   own, and the overflow rule is the net under the table that will not. */

.tafel-rahmen{overflow-x:auto;overscroll-behavior-x:contain;
  border:1px solid var(--line);border-radius:var(--rad);
  background:var(--flaeche);box-shadow:var(--schatten);overflow-x:auto;
  overscroll-behavior-x:contain}
.tafel{width:100%;border-collapse:collapse;margin:0;font-size:14.5px}
.tafel caption{padding:15px 18px 3px;text-align:left;color:var(--tx2);font-size:13.5px}
.tafel th,.tafel td{text-align:left;padding:12px 16px;vertical-align:top;
  border-bottom:1px solid var(--line)}
.tafel tbody tr:last-child :is(th,td){border-bottom:0}
.tafel thead th{font-family:var(--display);font-size:11px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--cy);
  border-bottom:1px solid var(--line2)}
.tafel tbody th{position:relative;padding-left:19px;font-family:var(--display);
  font-weight:700;font-size:14.5px;color:var(--tx)}
/* The accent is a bar, never the text. A bar is a non-text indicator and
   clears the 3:1 that applies to it; several of the nine accents would not
   clear the 4.5:1 that text needs. Same decision, same reason, as the
   application sections on /apps/. */
.tafel tbody th::before{content:"";position:absolute;left:2px;top:14px;bottom:14px;
  width:3px;border-radius:2px;background:var(--akz,var(--cy))}
.tafel tbody td{color:var(--tx2)}
.tafel tbody tr{transition:background var(--ueb)}
.tafel tbody tr:hover{background:var(--panelh)}
.tafel .chip{margin-left:8px}
.tafel a{text-decoration:none}
.tafel a:hover{text-decoration:underline}

/* --- flow: three nodes, and the gate between them -------------------------
   The quoted law above it says that a person decides before anything
   irreversible happens. This draws that sentence instead of repeating it.
   Grid and two pseudo-elements, not an SVG, and that is the rule this whole
   pass followed: an SVG scales its type down with its box, so a drawing that
   reads at 1200px arrives on a 360px phone with nine-pixel labels. Text that
   has to stay legible belongs in HTML, where it reflows. A picture with no
   text in it belongs in an SVG.
   Below 760px the nodes stack and the connector turns with them, so the arrow
   never points sideways at a box that now sits underneath. */

.fluss{display:grid;gap:26px;grid-template-columns:1fr;list-style:none;
  margin:0;padding:0}
.fluss>li{position:relative;margin:0;padding:18px;border:1px solid var(--line);
  background:var(--flaeche);box-shadow:var(--schatten);
  /* One corner wider than the other three, alternating along the row: the
     smallest asymmetry that stops three boxes reading as a form. */
  border-radius:var(--rad-l) var(--rad) var(--rad) var(--rad)}
.fluss>li:nth-child(2n){border-radius:var(--rad) var(--rad-l) var(--rad) var(--rad)}
.fluss .fl-t{display:flex;align-items:center;gap:10px;margin:0 0 7px;
  font-family:var(--display);font-weight:700;font-size:15.5px;color:var(--tx)}
.fluss .fl-t::before{content:"";flex:none;width:10px;height:10px;border-radius:50%;
  background:var(--akz,var(--cy));
  box-shadow:0 0 0 4px color-mix(in srgb,var(--akz,var(--cy)) 18%,transparent)}
.fluss .fl-b{margin:0;font-size:14px;color:var(--tx2)}
.fluss>li+li::before{content:"";position:absolute;left:50%;top:-21px;
  width:1px;height:16px;background:var(--line2)}
.fluss>li+li::after{content:"";position:absolute;left:50%;top:-8px;translate:-50% 0;
  border:5px solid transparent;border-top-color:var(--line2);border-bottom:0}
@media (min-width:760px){
  .fluss{grid-template-columns:repeat(3,1fr);gap:38px}
  .fluss>li+li::before{left:-27px;top:29px;width:18px;height:1px}
  .fluss>li+li::after{left:-10px;top:24px;translate:0 0;
    border:5px solid transparent;border-left-color:var(--line2);border-right:0}
}

/* --- the ring that breathes ----------------------------------------------
   The violet ring around the core of the network diagram widens as the
   diagram is reached. It is drawn by the reader's scroll position and not by
   a clock, which is the rule the track and the reading bar already follow:
   nothing on this site moves on its own, so nothing can move while a sentence
   is being read. `transform-box:fill-box` is what makes a scale inside an SVG
   grow from the middle of the circle rather than from the origin of the
   drawing.
   Its own two exits, rather than a line added to the two lists further up -
   same reason as the block heading: this way the whole pass is one hunk.
   Paper never scrolls and a reader who switched motion off never supplies a
   scroll position either, so both would otherwise keep a ring frozen at
   three-quarter size, which is a size the diagram never means. */

@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .ring-halo{animation:atem linear both;animation-timeline:view();
      animation-range:entry 12% cover 46%;
      transform-box:fill-box;transform-origin:center}
    @keyframes atem{from{opacity:.2;scale:.72}to{opacity:1;scale:1}}
  }
}
@media (prefers-reduced-motion:reduce){.ring-halo{animation:none!important}}
@media print{.ring-halo{animation:none!important}}

/* --- utilities ----------------------------------------------------------
   Deliberately a small, closed set: the site carries no inline style
   attributes, so `style-src 'self'` needs no 'unsafe-inline' escape hatch. */

.a-vio{--akz:var(--vio)}    .a-cy{--akz:var(--cy)}       .a-mag{--akz:var(--mag)}
.a-amber{--akz:var(--amber)} .a-coral{--akz:var(--coral)} .a-green{--akz:var(--green)}
.a-orange{--akz:var(--orange)} .a-blue{--akz:var(--blue)} .a-steel{--akz:var(--tx2)}

.mt-s{margin-top:10px}  .mt-m{margin-top:20px}  .mt-l{margin-top:28px}
.mt-xl{margin-top:36px} .mb-m{margin-bottom:18px} .mb-0{margin-bottom:0}
.oben{align-self:start}

.hero.klein{padding-block:clamp(40px,7vw,76px)}
.hero.gross{padding-block:clamp(56px,11vw,120px)}
.hinweis.balken{display:flex;flex-wrap:wrap;gap:14px 24px;align-items:center;
  justify-content:space-between}
.hinweis.balken p{margin:0;max-width:58ch}
.hinweis.warn{border-color:var(--coral)}
.notiz{border:0;padding:0;margin:10px 0 0;color:var(--tx2);font-size:13px}
.code.hell{background:var(--panel)}
.dach.klein{font-size:14px}

/* --- Aufklapp-Menue (schmale Bildschirme) --------------------------------- *
 * Gemessen vor dem Umbau: die Kopfzeile brauchte 171 px bei 390 px Breite und
 * 220 px bei 360 px - ein Viertel des Bildschirms, bevor ein Wort Inhalt kommt
 * (der Befund aus worktree 59, hier unabhaengig nachgemessen).
 * Traeger des Zustands ist ein Kontrollkaestchen, kein Skript: die Seiten dieser
 * Website versprechen in datenschutz.html "Kein JavaScript", und das ist ein
 * Rechtstext. Ausserhalb der Media Query steht hier nichts, was den grossen
 * Bildschirm beruehrt - der Knopf ist dort schlicht nicht vorhanden.
 * Bewusst kein <details>/<summary>: das laesst sich auf dem grossen Bildschirm nur
 * dann als normale Navigation zeigen, wenn man den zugeklappten Zustand per CSS
 * ueberschreibt, und ob das gelingt, haengt am Browser. Gemessen hat es in Chromium
 * funktioniert - fuer Firefox und Safari konnte ich es hier nicht belegen, und ein
 * Fehlschlag haette die Navigation auf dem Schreibtisch verschwinden lassen. Beim
 * Kontrollkaestchen ist der schlimmste Fall ein dauerhaft offenes Menue.          */

.menu-schalter{position:absolute;width:1px;height:1px;margin:0;opacity:0}
.menu-knopf{display:none}

@media (max-width:768px){
  .menu-knopf{display:inline-flex;align-items:center;gap:9px;margin-left:auto;
    padding:9px 13px;min-height:44px;border:1px solid var(--line2);border-radius:9px;
    font-size:14px;color:var(--tx2);cursor:pointer;user-select:none}
  /* drei Striche, rein aus Rahmen gebaut - kein Bild, kein Zeichensatz */
  .menu-knopf::before{content:"";width:16px;height:2px;background:currentColor;
    box-shadow:0 5px 0 currentColor,0 -5px 0 currentColor}
  .menu-schalter:checked ~ .menu-knopf{color:var(--tx);border-color:var(--tx3)}
  /* Das Kaestchen ist unsichtbar, seine Tastatur-Markierung darf es nicht sein. */
  .menu-schalter:focus-visible ~ .menu-knopf{outline:2px solid var(--cy);outline-offset:2px}

  .nav{margin-left:0;width:100%;display:none}
  .menu-schalter:checked ~ .nav{display:block}
  .nav ul{flex-direction:column;flex-wrap:nowrap;gap:0}
  .nav li{border-top:1px solid var(--line)}
  .nav a{padding:12px 6px;min-height:44px;display:flex;align-items:center;font-size:15px}
  /* Der Unterstrich sass auf 11 px Einzug und passt nicht zur vollen Zeilenbreite. */
  .nav a[aria-current]::after,.nav a:hover::after{left:6px;right:auto;width:22px;bottom:6px}

  /* Tippziele. Die Regeln oben treffen WCAG 2.2 AA (2.5.8, 24 px) - das war eine
   * Entscheidung und keine Nachlaessigkeit. Ein Daumen ist trotzdem keine Maus:
   * auf einem Beruehrungsschirm gehen die freistehenden Links und die Bedien-
   * elemente auf 44 px. Gemessen bei 320 px, vor dieser Regel: Fuss-Navigation
   * 34,4 px, Quell-Links 26,4 px, die Methoden-Klappen auf /numbers/ 24,0 px.
   * Die Menue-Eintraege standen schon auf 44 px.
   *
   * BEWUSST NICHT angehoben: a.beleg, der Quell-Pfeil mitten im Satz. 2.5.8 nimmt
   * Ziele aus, deren Groesse durch die Zeilenhoehe des Textes um sie herum
   * begrenzt ist, und genau das ist er. Ein 44-px-Kasten inline wuerde entweder
   * den Zeilenrhythmus zerreissen oder - als unsichtbare Trefferflaeche - ueber
   * den Zeilen darueber und darunter liegen und deren Taps abfangen. Das waere
   * ein kleines Ziel gegen gestohlene Beruehrungen getauscht.                   */
  .quellen a,.fuss-status a,a.zielgross{min-height:44px;padding-block:10px}
  .fuss nav a{display:flex;align-items:center;min-height:44px;padding:10px 8px}
  .methode>summary{min-height:44px}
  /* Measured at 320px after the design pass: the wordmark - a free-standing
     link to the start page, in the header and again in the footer - stood at
     39.2px while every other free-standing link on a touch screen had been
     raised to 44. It clears WCAG 2.2 AA either way; the rule this site wrote
     for itself two lines above is the stricter one, and it applies here too. */
  .marke{display:inline-flex;align-items:center;min-height:44px}
}

/* --- narrow viewports ---------------------------------------------------- */

@media (max-width:560px){
  .kopf .wrap{padding-block:12px}
  .nav a{padding:12px 6px}
  .leiste{font-size:12px}
  .knoepfe .knopf{width:100%;justify-content:center}
}

/* ==========================================================================
   apps+numbers visual pass
   --------------------------------------------------------------------------
   One appended block, nothing above it rewritten. It carries what /apps/ and
   /numbers/ needed in order to stop being two walls of prose: a table, ten
   drawn marks, and two sets of bars. Everything here obeys the three rules the
   rest of the sheet already obeys - no script, no external request, and no
   inline style attribute, because `style-src 'self'` governs those too and a
   bar whose width lives in the markup would need the escape hatch this site
   does not have. So every width below is a class.

   Nothing here animates. The hover transitions borrow --ueb, which the
   reduced-motion block above already caps at 1ms for the whole document, so
   this block needs no exit of its own - and there is nothing to exit from.
   ========================================================================== */

/* --- the application table (/apps/) --------------------------------------
   A table, because the ten really are one kind of thing measured four ways,
   and a reader comparing two of them should not have to scroll past four
   paragraphs to do it. The container scrolls, never the page: at 360px the
   four columns do not fit, and a document that scrolls sideways as a whole is
   a broken document rather than a wide table.                              */

.tafel-buehne{overflow-x:auto;overscroll-behavior-x:contain;
  border:1px solid var(--line);border-radius:var(--rad);
  background:var(--flaeche);box-shadow:var(--schatten)}
.tafel{width:100%;min-width:560px;border-collapse:collapse;
  font-size:14.5px;text-align:left}
/* The caption stays short on purpose. It sits inside the scrolling container -
   a caption belongs to its table - so a long one would be text a phone reader
   has to drag sideways to finish. What the table needs said in full is said in
   the note under the container instead, where it wraps to the screen. */
.tafel caption{caption-side:bottom;padding:13px 16px 15px;
  font-family:var(--display);font-size:11px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--tx3);text-align:left}
.tafel thead th{padding:13px 16px;border-bottom:1px solid var(--line2);
  font-family:var(--display);font-size:11px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--tx2);white-space:nowrap}
.tafel tbody th,.tafel tbody td{padding:12px 16px;border-top:1px solid var(--line);
  vertical-align:middle;color:var(--tx2);font-weight:400}
.tafel tbody tr:first-child th,.tafel tbody tr:first-child td{border-top:0}
/* The accent of the row is the same --akz the section below it carries, so the
   table and the ten sections read as one colour system rather than two. */
.tafel tbody tr{transition:background var(--ueb)}
.tafel tbody tr:hover{background:var(--panel2)}
.tafel tbody th{color:var(--tx);white-space:nowrap}
.tafel tbody th a{color:var(--tx);text-decoration:none;
  font-family:var(--display);font-weight:700;font-size:14px}
.tafel tbody th a:hover{color:var(--akz,var(--cy));text-decoration:underline}
.tafel .zahl-z{font-family:var(--mono);font-size:12.5px;white-space:nowrap;
  font-variant-numeric:tabular-nums;color:var(--akz,var(--cy))}
.tafel code{color:var(--tx2)}

/* The ten marks. Drawn here, abstract on purpose: they stand for a shape of
   work, never for anything inside the machine. They carry no information the
   row does not already carry in words, which is why they are aria-hidden -
   a screen reader that reads "Dizz Money" twice is worse served than one that
   reads it once. currentColor is the row's accent, so the mark is coloured by
   the same token as the section it links to. */
.vig{display:inline-block;vertical-align:-6px;width:26px;height:26px;
  margin-right:10px;color:var(--akz,var(--cy));opacity:.9}
/* Stroke, width and joins live here rather than on every shape: ten marks with
   the same four presentation attributes repeated is a kilobyte of markup that
   says one thing, and one thing said once is easier to change than ten. */
.vig :is(path,circle,rect){fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}

/* --- the figure tiles (/numbers/) ----------------------------------------
   Six figures, two per row where there is room. Each tile leads with the
   numeral, because that is what the page is about; the method stays folded
   underneath it and prints unfolded like every other <details> here.       */

.messung.tafeln{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  align-items:start}
.messung.tafeln .mess-kopf{display:block}
.messung.tafeln .mess-wert{display:block;font-size:clamp(38px,6vw,54px);
  color:var(--akz,var(--tx))}
.messung.tafeln .mess-was{display:block;margin-top:6px;line-height:1.45}
.mess-stand{margin:12px 0 0}
/* Two states, and they are the page's own words: a figure a reader can go and
   count, and a figure that describes something withdrawn or private. The
   colours sit on the border and the text stays --tx2, so nothing here depends
   on a hue to be read. */
.chip.gut{border-color:rgba(54,211,153,.5)}
.chip.alt{border-color:rgba(242,100,90,.55)}

/* --- bars ----------------------------------------------------------------
   Both sets below are drawn to scale from figures the pages state in words,
   and the words stay next to them. The arithmetic, written out so nobody has
   to trust it:
       966 -> 100%    38 -> 3.93%             (the folder before and after)
       579/2667 = 21.71%   46/2667 = 1.72%
       3/2667 = 0.11%      2039/2667 = 76.45%  (sum 2667, the stated total)
   Two of those are smaller than a hairline at any sensible width, so the bars
   carry a min-width of 3px. That makes 3 visible; it does not make it look
   bigger than 46, because the number stands beside it and the number is the
   claim. A bar nobody can see would round a real value to nothing, which is
   the one thing a page about honest figures must not do.                   */

/* Every bar spans the full width of its own row, so all of them resolve their
   percentage against the same track. That is not a layout preference: the first
   version put the number and the bar side by side, and because "2,039" is a
   wider label than "3", the track behind the longest bar was the narrowest one.
   Measured, the 579 bar came out 0.7% too long against the 2,039 bar. A picture
   of a ratio that is off by a measurable amount is a wrong number drawn large,
   and this page of all pages cannot carry one. */

.vergleich{margin:16px 0 0;padding:0}
.vgl-z{display:grid;grid-template-columns:1fr;gap:5px;margin:0 0 9px;
  font-family:var(--mono);font-size:12.5px;color:var(--tx2);
  font-variant-numeric:tabular-nums}
.vgl-b{height:9px;min-width:3px;border-radius:999px;
  background:var(--akz,var(--cy));opacity:.75}
.vergleich figcaption{font-size:13px;line-height:1.6;color:var(--tx2)}
.q-966{width:100%}
.q-038{width:3.93%}

.anteil{list-style:none;margin:0;padding:0;display:grid;gap:16px}
.anteil li{display:grid;grid-template-columns:auto 1fr;gap:7px 14px;margin:0;
  align-items:baseline}
.an-k{grid-column:1;grid-row:1;font-family:var(--display);font-weight:800;
  font-size:20px;color:var(--akz,var(--tx));font-variant-numeric:tabular-nums}
.an-w{grid-column:2;grid-row:1;font-size:14.5px;line-height:1.6;color:var(--tx2)}
.an-b{grid-column:1/-1;grid-row:2;height:11px;min-width:3px;border-radius:999px;
  background:var(--akz,var(--cy));opacity:.75}
.q-579{width:21.71%}
.q-046{width:1.72%}
.q-003{width:.11%}
.q-2039{width:76.45%}

@media (max-width:560px){
  .anteil li{grid-template-columns:1fr}
  .an-k{grid-column:1;grid-row:1}
  .an-w{grid-column:1;grid-row:2}
  .an-b{grid-column:1;grid-row:3}
  .messung.tafeln{grid-template-columns:1fr}
}

/* On paper a background is usually dropped, and a proportion drawn as a
   background would print as four empty lines. The figures survive either way -
   they are words on the same line - but the picture may as well survive too. */
@media print{
  .an-b,.vgl-b{print-color-adjust:exact;-webkit-print-color-adjust:exact}
  .tafel-buehne{overflow-x:visible}
}

/* ==========================================================================
   THE MOBILE PASS (07.08.2026) - the same components, on the screen most
   people arrive on. The block above was drawn at desk width. One block again,
   for its reason - another lane is rewriting /apps/ and /numbers/ next door.
   Nothing above this line was touched, and every rule below is scoped to a
   width or to a pointer, so the desktop is never handed one of them.

   Measured at 360x640 against the built site, before any of it:
     - the diagram is pinned at 520px inside a 328px column, so 192 pixels of
       it - three of the nine services - sat behind a swipe nothing announces
     - the ten applications stood in a 168/158 split, and 158 pixels is 21
       characters, so every description of them broke over five lines
     - the link on an application name was an 85x19 touch target, here, where
       a free-standing link on a touch screen is 44
     - body text was 15px, and the four headline figures spent 483 pixels of
       column saying four numbers
   Afterwards: the start page is 8,100 pixels tall instead of 7,762, the
   German one 10,851 instead of 10,683. Longer, and every line full width. At
   769px and above both measure what they measured before this block existed.
   ========================================================================== */

/* --- the diagram on a phone ----------------------------------------------
   Below 566px the stage scrolls sideways: the drawing is pinned at 520px so
   its labels stay readable, the column is 0.92 of the viewport, and the two
   meet at 565.2. Hence 565, and the property it buys is exact - at no width
   does that stage have anything left to scroll to.

   The picture asserts one core, nine services on one ring around it, and a
   line from each to the core and to no other. The labels carry none of that:
   the names and ports stand in the table directly below, where they reflow,
   and in the <desc> a screen reader is given. So on a phone the labels go and
   the drawing takes the column - text that must stay legible lives in HTML,
   a picture with no text in it lives in an SVG.

   The rest is a window on the drawing, not a scale of it: laid out 145.45%
   wide (640 over 440) and pulled left by 100 of those 440 units, framing
   viewBox x 100..540, y 42..330. That is the nine nodes - they sit on an
   ellipse of 404 by 240, not on the faint circle at r=120, which only the top
   one touches. `aspect-ratio` states the window, so the numbers can be read
   against each other rather than trusted.

   The core keeps its label, and it is the one size in this file that is not a
   size on screen: inside a viewBox, 17 units arrive as 12.7 pixels. 23 and 18
   arrive as 17 and 13, the size the desktop drawing shows. */

@media (max-width:565px){
  .figur .buehne{overflow:hidden;aspect-ratio:440/288}
  .figur .buehne>svg{width:145.45%;min-width:0;max-width:none;
    margin:-9.64% 0 0 -22.73%}
  .figur .buehne svg>g:last-of-type>text{display:none}
  /* drawn for 0.81 scale, arriving at 0.75, where a 1-unit spoke is under a
     pixel. Widened, not moved: the nodes stay on the ring the caption names. */
  .figur .buehne svg>g:last-of-type>circle{stroke-width:3.5}
  .figur .buehne svg>g:first-of-type{stroke-width:1.6}
  .figur .buehne svg>text{transform-box:fill-box;transform-origin:center}
  .figur .buehne svg>text:first-of-type{font-size:23px;transform:translateY(-2px)}
  .figur .buehne svg>text:nth-of-type(2){font-size:18px;transform:translateY(6px)}
  /* And the one thing here that breathes stops breathing. A scroll-driven
     `scale` inside an SVG is not a layer on the compositor: it re-renders the
     SVG, and the window above makes that SVG 45% wider than the stage showing
     it - a repaint per scroll frame, for an ornament, on the four-gigabyte
     phone this pass is written for. It rests at full size, which is a size the
     diagram means, and is the exit reduced motion and print already take. */
  .figur .buehne .ring-halo{animation:none}
}

/* --- type, and the four figures ------------------------------------------
   15px reads at arm's length on a desk and squints in a hand, so the body
   goes to a full rem and the four things carrying sentences follow. Chips,
   labels and the status strip stay, for the reason at the top of this file:
   they are shape, not text.
   The figure tiles turn on their side. Stacked, the four headline numbers
   needed 483 pixels of a 640-pixel screen; laid across they need 390 and
   arrive together, which is why four numbers are set beside each other at
   all. Not a word changes. */

@media (max-width:560px){
  body{font-size:1rem}
  .tafel{font-size:15.5px}
  .tafel caption,.figur figcaption{font-size:14px}
  .zahl span{font-size:14.5px}
  .fluss .fl-b{font-size:15px}
  .notiz{font-size:13.5px}
  .zahl{display:grid;grid-template-columns:auto 1fr;gap:2px 14px;
    align-items:center;padding:16px}
  .zahl span{margin-top:0}
}

/* --- the two tables, as a stack ------------------------------------------
   A two-column comparison stops being one when a column is 21 characters
   wide. Below 480px each row becomes its own block: row header on top, the
   sentence under it at the full width of the card. At 480 the description
   column measures 239 pixels - 32 characters, which reads - so that is where
   the split sits, and above it nothing changes.
   The cost, stated rather than hidden: `display:block` takes the table role
   with it and no CSS hands it back. It is affordable in these two tables and
   nowhere else here, because both are two columns whose first is the row
   header - a shape that linearises into what it already looked like. The
   header row is hidden the way this file hides things rather than deleted, so
   its two words survive as text for a reader who no longer gets them as
   headers, and `scope` stays in the markup for any browser keeping the roles.
   The `::before` label technique was the alternative, and wanted `data-`
   attributes on sixty cells to repeat two words the caption already says. */

@media (max-width:479px){
  .tafel,.tafel tbody,.tafel tbody tr,.tafel tbody th,.tafel tbody td{display:block}
  /* the stacked rows must also drop the 560px table floor, or the stack keeps
     the table's width inside a narrower frame and the overflow hides text -
     measured live at 360px: 234px of every sentence stood outside the frame */
  .tafel{min-width:0}
  .tafel thead{position:absolute;width:1px;height:1px;overflow:hidden;
    clip-path:inset(50%)}
  .tafel caption{padding:15px 16px 11px}
  .tafel tbody tr{padding:12px 16px 14px;border-bottom:1px solid var(--line)}
  .tafel tbody tr:last-child{border-bottom:0}
  .tafel tbody th,.tafel tbody td{padding:0;border-bottom:0}
  .tafel tbody th{padding-left:15px}
  /* the accent bar was inset 14px against a cell two lines tall; against a
     row header one line tall that leaves a bar with no height */
  .tafel tbody th::before{top:.2em;bottom:.2em}
}

/* --- touch ---------------------------------------------------------------
   The ten application names became links in the pass above and arrived as
   19-pixel targets. The rule further up - a free-standing link on a touch
   screen is 44, stricter than the 24 WCAG 2.2 asks - lists the ones it
   raised; this is the eleventh, at the width the rest of them use. The proof
   arrow inside a sentence stays exempt, for the reason given there.

   A finger has no hover, but a browser hands a tap to `:hover` and leaves it
   there. Two states here are strong enough to read afterwards as a selection:
   a lit table row and a lifted tile. Both answer a pointer that is not
   present, and both repaint a card-sized box to say nothing.
   `:focus-visible` keeps the lift, because a keyboard does mean it. */

@media (max-width:768px){
  .tafel tbody th a{display:inline-flex;align-items:center;min-height:44px}
}
@media (hover:none){
  .tafel tbody tr:hover{background:none}
  .kachel:hover{translate:none;box-shadow:var(--schatten)}
}

/* ==========================================================================
   the mobile pass, subpages (07.08.2026)
   --------------------------------------------------------------------------
   One appended block again, and nothing above this line was touched. It carries
   the seven subpages - /apps/, /numbers/, /method/, /system/, /about/,
   /vision/, /journey/ and the 404 - on the screen most readers arrive with: a
   two- to four-year-old Android in the middle of the range, 360 to 412 pixels.
   Every figure below was measured in a browser at that width, not estimated.
   Nothing here animates, and nothing here paints a filter: the target device is
   slow at both.

   Three things this pass deliberately did NOT do, because a pass that only
   lists its wins is a marketing document:
     - `content-visibility:auto` on the ten sections of /apps/. It would save
       layout work on a slow phone, and the page's whole navigation is ten
       in-page anchors into exactly those sections. A speed-up that risks the
       one interaction a page exists for is not a speed-up.
     - the shared rail. `.bahn` and `.strahl` carry /method/ and /journey/ and
       also the two start pages, which another lane was rewriting for phones at
       the same time. Measured at 360: the rail sits at x=23..25 and the station
       dot at x=18..30, so it already runs through the middle of every dot.
       Nothing to fix - and a second lane's rule over one construct is how two
       green lanes merge into one broken one.
     - the comment colour in the contract listing on /system/. `--tx3` on
       `--bg2` is about 4.2:1, and those comments are text that carries meaning
       rather than decoration, so they are short of the 4.5:1 that applies to
       them. True on the desktop too, older than this pass, and a colour token
       is not something a mobile block should quietly redefine. Named here so
       the next reader does not have to find it twice.
   ========================================================================== */

/* --- the guard the one grid was missing -----------------------------------
   Every other auto-fit grid here floors its track with min(<floor>,100%),
   because a bare floor keeps the column at full width below it and pushes the
   document sideways. `.messung.tafeln` was the one that did not: its 300px
   floor is wider than the 288px a 320px screen leaves. The one-column rule at
   560px catches it today - which is the exact shape of a check that passes
   because something else happened to be in the way. Guarded at the source now.
   Above 300px of content the two are identical, so no wide screen moves.      */
.messung.tafeln{grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr))}

/* --- the application table becomes ten cards ------------------------------
   /apps/ opens with a table of ten rows and four columns: name, what it does,
   port, tests. It carries `min-width:560px` and its frame scrolls - the right
   net under a wide table, the wrong resting state for a phone. Measured at 360:
   the frame is 326px and the table 560, so a reader arrives looking at two of
   the four columns with nothing on the screen saying the other two exist. A
   table you have to guess at is worse than no table.

   The crossover is arithmetic, not a guess. The wrap pads clamp(16px,4vw,32px)
   on each side and the 4vw term wins between 400 and 800 pixels, so content is
   0.92 of the viewport; 0.92V >= 562 gives V >= 611. Hence 610px, and not one
   of the round numbers already in this file.

   Below it every row becomes a card, and what lets this table survive losing
   its grid is that every cell says what it is on its own: ":8200" reads as a
   port, "344 tests" reads as tests, and the sentence between them is a
   sentence. So the column heads are not restated per card - `display:none`
   takes them out of the accessibility tree along with the table semantics that
   changing `display` ends anyway. A screen reader gets ten clean blocks in
   reading order instead of a table to walk cell by cell on a phone.
   Scoped to `.tafel-buehne`: the start pages build their tables in
   `.tafel-rahmen` and belong to another lane.                                */

@media (max-width:610px){
  /* the frame stops being a frame - a card inside a bordered box is two boxes */
  .tafel-buehne{overflow-x:visible;border:0;border-radius:0;background:none;
    box-shadow:none}
  .tafel-buehne>.tafel{display:block;min-width:0;width:100%}
  .tafel-buehne>.tafel thead{display:none}
  .tafel-buehne>.tafel caption{display:block;padding:0 0 11px}
  .tafel-buehne>.tafel tbody{display:grid;gap:11px}
  .tafel-buehne>.tafel tbody tr{display:block;padding:14px 16px 13px;
    border:1px solid var(--line);border-left:3px solid var(--akz,var(--cy));
    border-radius:var(--rad);background:var(--flaeche);box-shadow:var(--schatten)}
  .tafel-buehne>.tafel tbody :is(th,td){display:block;padding:0;border:0;
    white-space:normal}
  /* the accent already draws the left edge of the card, so the bar the row
     header carries inside the table would be the same statement twice */
  .tafel-buehne>.tafel tbody th{display:flex;align-items:center;gap:0;
    margin:0 0 5px;position:static}
  .tafel-buehne>.tafel tbody th::before{display:none}
  .tafel-buehne>.tafel tbody th a{display:flex;align-items:center;flex:1;
    min-height:44px;font-size:16px}
  .tafel-buehne>.tafel tbody td:first-of-type{color:var(--tx2);font-size:15px;
    line-height:1.5}
  /* port and test count close the card on one line, in the order they had */
  .tafel-buehne>.tafel tbody td:nth-of-type(2),
  .tafel-buehne>.tafel tbody td:nth-of-type(3){display:inline-block;
    margin-top:9px}
  .tafel-buehne>.tafel tbody td:nth-of-type(2){margin-right:13px}

  /* --- the contract listing unfolds ---------------------------------------
     /system/ lists the mandatory endpoints in monospace, each with its comment
     aligned to the right of it. Measured at 360 the block needs 452px inside a
     326px box, and `white-space:pre` means it can never wrap its way out. The
     alignment is pleasant; the claim is the list of endpoints. So each comment
     moves under the line it annotates and the block stops scrolling sideways -
     the same content, one axis fewer. Two things the first attempt got wrong,
     both caught by measuring: the comments inherit `pre` and so could not wrap
     out of an overflow of their own (one ran 10px past the padding), and the
     first comment in a block is not an annotation but its title line, with
     nothing above it to be indented under.                                   */
  .code pre .co{display:block;padding-left:2.2em;white-space:normal}
  .code pre .co:first-child{padding-left:0}
}

/* --- reading size, touch size --------------------------------------------
   The body text was .9375rem - 15px where the browser is left alone - which is
   a desktop measurement. It goes to a full rem on narrow screens. It stays in
   rem, so a reader who raised their device font still gets the multiple they
   asked for; this only moves the floor. Under it sit the components that carry
   sentences at 14 to 14.5px, fine beside a 15px base and small beside a 16px
   one; only components that appear on these seven pages are touched. Labels
   stay where they are - chips, dates, kickers and table heads are shape rather
   than text, which the base rules at the top of this file said first.        */

@media (max-width:768px){
  body{font-size:1rem}
  .methode-inhalt,.dach.klein,.sch-b,.sch-g,.an-w,.st-b,.mess-was{font-size:15px}
  /* Short titles in the display face wrap like headings and were never told
     so - the headings at the top take `text-wrap:balance` and these do not. At
     360px "Website & first product editions" put "editions" on a line alone.
     A hint like the one above it: a browser that does not know it wraps as
     before, so nothing depends on it. */
  .st-t,.sch-t{text-wrap:balance}
  /* 11.5px of letter-spaced capitals is a small thing to aim a thumb at, and
     on a phone this label is a control. It already has its 44px of height. */
  .methode>summary{font-size:12.5px}
  /* Between 611 and 768 the table is still a table, and there its row header
     is a free-standing link at 17px - the target the house rule two hundred
     lines up was written about. */
  .tafel-buehne>.tafel tbody th a{display:inline-flex;align-items:center;
    min-height:44px}
  /* and the name beside it may wrap there. Measured at 620px, before this pass
     and unchanged by it, so a finding rather than a regression: the header is
     `nowrap`, "Dizz Communication" with its mark needs 209px, the table hands
     that column 201, and eight pixels of the longest name lay over its
     neighbour. A second line on one row is the cheapest thing to spend. */
  .tafel-buehne>.tafel tbody th{white-space:normal}
  /* the last free-standing control still short of the 44px this site set
     itself: measured 42.8. It is absolutely positioned and only visible while
     focused, so the correction moves nothing on any page. */
  .skip{min-height:44px;padding-block:12px}
  /* a swipe along a code block that runs out of block should stop there rather
     than become a page gesture or a step backwards in history */
  .code{overscroll-behavior-x:contain}
}

/* --- what a finger leaves behind -----------------------------------------
   A pointer query rather than a width query: it can never reach a desktop, and
   it does reach a large tablet a width query would miss. Chrome on Android
   applies :hover to whatever was tapped last and leaves it applied, so a card
   the reader only touched on the way past stays lit as though a cursor were
   resting on it. These hover states are decoration and the tap has already
   happened, so on a screen with no pointer they do not run.                  */

@media (hover:none){
  .tafel-buehne>.tafel tbody tr:hover{background:none}
  .mess:hover{border-color:var(--line);box-shadow:var(--schatten)}
}

/* ==========================================================================
   LAYER 3 - the grove: the organic front layer
   ==========================================================================
   Lives only on the two front pages (index, /de/) - every rule hangs off the
   .hain markup those two pages carry, so the inner pages stay plain by
   construction, not by discipline.

   The whole layer is decoration in the strict sense: no text, no links, no
   information. It sits behind the content (z-index), never intercepts a
   pointer (pointer-events:none), and every SVG in it is aria-hidden. If all
   of it were deleted the page would say exactly the same things.

   Motion doctrine, same as the site's: compositor-only properties (transform,
   opacity), every animation inside prefers-reduced-motion:no-preference, and
   a hard kill under reduced-motion and print. Scroll-driven depth uses the
   same @supports gate as the reveals - browsers without it get a calm,
   static grove.

   Two transforms cannot share one element, so every leaf is a pair: the
   outer div takes the scroll-driven drift, the inner svg takes the slow
   sway. Depth is drawn, not computed: farther fronds are smaller, dimmer
   and drift a shorter distance - cheap on a mid-range phone, no filters,
   no blur, at most six small composited layers.                             */

.hain{position:relative}
.hain-buehne{position:absolute;inset:0;z-index:-1;overflow:hidden;pointer-events:none}
.hain-buehne>div{position:absolute}
.hain-buehne svg{display:block;width:100%;height:auto}

/* the fronds: three depths, two sides */
.zweig-a{left:max(-40px,-3vw);top:-28px;width:clamp(150px,22vw,300px);opacity:.5}
.zweig-b{right:max(-56px,-4vw);top:16%;width:clamp(180px,26vw,360px);opacity:.62}
.zweig-c{left:12%;bottom:-40px;width:clamp(110px,15vw,210px);opacity:.34}

/* the singing bowl: rings behind the headline, barely there */
.schale-lage{left:50%;top:44%;width:min(70vw,560px);margin-left:min(-35vw,-280px);margin-top:-160px;opacity:.5}

/* the two halves in balance, right of the headline, small and quiet */
.einklang-lage{right:clamp(8px,6vw,84px);top:60px;width:clamp(64px,8vw,104px);opacity:.7}

@media (prefers-reduced-motion:no-preference){
  .zweig-a svg{animation:wiegen 12s ease-in-out infinite alternate;transform-origin:18% 6%}
  .zweig-b svg{animation:wiegen 15s ease-in-out -6s infinite alternate-reverse;transform-origin:86% 8%}
  .zweig-c svg{animation:wiegen 10s ease-in-out -3s infinite alternate;transform-origin:30% 100%}
  .schale-ring{animation:schalen-klang 14s cubic-bezier(.23,.6,.34,1) infinite;transform-origin:center;transform-box:fill-box}
  .schale-ring.r2{animation-delay:4.5s}
  .schale-ring.r3{animation-delay:9s}
}
@keyframes wiegen{from{transform:rotate(-2.1deg)}to{transform:rotate(2.3deg)}}
@keyframes schalen-klang{0%{opacity:0;transform:scale(.62)}12%{opacity:.55}70%{opacity:0;transform:scale(1.12)}100%{opacity:0;transform:scale(1.12)}}

/* scroll-driven depth: while the hero leaves the viewport each depth falls
   at its own speed and the balance mark turns once - read direction on the
   root scroller, drawn only where the browser can drive it from scroll */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .zweig-a{animation:sinken-nah linear both;animation-timeline:view(block);animation-range:exit 0% exit 100%}
    .zweig-b{animation:sinken-mitte linear both;animation-timeline:view(block);animation-range:exit 0% exit 100%}
    .zweig-c{animation:sinken-fern linear both;animation-timeline:view(block);animation-range:exit 0% exit 100%}
    .einklang-lage{animation:wenden linear both;animation-timeline:view(block);animation-range:exit 0% exit 100%}
    @keyframes sinken-nah{to{transform:translateY(84px) rotate(3deg)}}
    @keyframes sinken-mitte{to{transform:translateY(48px) rotate(-2deg)}}
    @keyframes sinken-fern{to{transform:translateY(22px)}}
    @keyframes wenden{to{transform:rotate(150deg)}}
  }
}

/* the water: one narrow band under the hero, two runs of the same long wave
   sliding against each other. Each svg is three viewports wide and animates
   a third of its width, so the loop closes without a visible seam.          */
.welle{position:relative;height:76px;overflow:hidden;pointer-events:none;margin-top:-8px}
.welle svg{position:absolute;left:0;top:0;width:300%;height:100%}
.welle .w2{opacity:.5}
@media (prefers-reduced-motion:no-preference){
  .welle .w1{animation:wandern 38s linear infinite}
  .welle .w2{animation:wandern 26s linear infinite reverse}
}
@keyframes wandern{to{transform:translateX(-33.3333%)}}

/* the deep reveal: the front pages' sections tilt up out of the page plane
   instead of only rising. Same timeline, same range as .reveal - one added
   axis. perspective on the parent keeps the tilt a real foreshortening.     */
.tief{perspective:900px}
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .tief>*{animation:tief-auf linear both;animation-timeline:view();
      animation-range:entry 0% cover 32%;transform-origin:50% 100%}
    @keyframes tief-auf{from{opacity:0;transform:rotateX(9deg) translateY(26px)}
      to{opacity:1;transform:rotateX(0deg) translateY(0)}}
    /* on a phone the tilt axis goes: rotateX puts fourteen text blocks on 3D
       layers and costs them subpixel antialiasing - the rise alone reads the
       same at that size and keeps the text sharp */
    @media (max-width:560px){
      .tief>*{animation-name:tief-flach}
    }
    @keyframes tief-flach{from{opacity:0;transform:translateY(26px)}
      to{opacity:1;transform:translateY(0)}}
  }
}

/* a mid-range phone: the far frond is gone, the near ones pull outward and
   shrink, the bowl tightens behind the headline, the water narrows. What
   remains is four small layers - the calm survives, the cost does not.      */
@media (max-width:560px){
  .zweig-c{display:none}
  /* presence up, cost down: measured on the phone the fronds sat below the
     perception threshold against the hero glow, so the surviving layers gain
     opacity instead of gaining company - and two of the three bowl rings stop
     animating, because rings are SVG children the compositor cannot lift
     (the same reasoning .ring-halo applies to itself further up) */
  .zweig-a{left:-34px;top:-20px;width:132px;opacity:.55}
  .zweig-b{right:-44px;top:22%;width:158px;opacity:.68}
  .schale-lage{width:88vw;margin-left:-44vw;margin-top:-120px;opacity:.5}
  .einklang-lage{right:6px;top:34px;width:56px;opacity:.65}
  .schale-ring.r2,.schale-ring.r3{animation:none;opacity:.11}
  .welle{height:48px}
}

/* stillness is the fallback everywhere: under reduced motion the grove holds
   one honest pose, and paper gets no motion and no water band at all        */
@media (prefers-reduced-motion:reduce){
  .zweig-a,.zweig-b,.zweig-c,.zweig-a svg,.zweig-b svg,.zweig-c svg,
  .einklang-lage,.schale-ring,.welle .w1,.welle .w2{animation:none!important}
  .tief>*{animation:none!important}
  .schale-ring{opacity:.14}
}
@media print{
  .hain-buehne,.welle{display:none}
  .tief>*{animation:none!important;opacity:1}
}

/* ==========================================================================
   die Mini-Karte und das Paar aus zwei Zielen (07.08.2026)
   --------------------------------------------------------------------------
   Wieder ein angehaengter Block, und ueber dieser Linie wurde nichts angefasst.

   Die grosse System-Karte unter /karte/ ist eine einzelne, sehr schwere Seite
   fuer einen breiten Bildschirm - auf dem Telefon war sie nie zu gebrauchen.
   Statt sie dort zu reparieren, was fremder Code in einem Spiegel ist, den
   diese Website nicht bearbeiten darf, steht neben ihr eine kleine, hier
   geschriebene Seite: /mini-karte/. Wer auf einem schmalen Schirm auf die
   Karte tippt, bekommt sie.

   Die Weiche ist CSS und sonst nichts: kein Skript, keine Weiterleitung, kein
   Erraten des Geraets. Beide Links stehen immer im Markup - eine Suchmaschine,
   ein Lesemodus und ein Ausdruck sehen also beide, und der Ausfall des einen
   nimmt dem Leser nicht den anderen.

   BEWUSST NICHT `display:revert` fuer das Wiedereinblenden, obwohl das die
   kuerzeste Zeile waere: `revert` faellt auf den Wert des Browsers zurueck und
   wirft dabei JEDE Angabe dieser Datei weg. Ein `a.knopf` ist hier
   `inline-flex` - `revert` macht daraus ein `inline`, und ein Knopf ohne
   Flex-Kasten verliert Hoehe, Abstand und die Ausrichtung seines Pfeils. Darum
   steht hier je Element der Wert, den es ausserhalb der Media Query ohnehin
   haette. Zwei Zeilen statt einer, dafuer keine, die etwas anderes tut als sie
   sagt.

   Das Paar sitzt bei einer Kachel am <li> und nicht am <a>: die Liste ist ein
   Raster, und ein verstecktes <a> in einem sichtbaren <li> haette eine leere
   Zelle im Raster hinterlassen.                                             */

.nur-schmal{display:none}

@media (max-width:768px){
  .nur-breit{display:none}
  li.nur-schmal{display:list-item}
  a.nur-schmal.knopf{display:inline-flex}
}

/* Das Schichten-Bild der Mini-Karte. Anders als `.figur` bekommt es KEINE
   Mindestbreite und keine seitwaerts schiebbare Buehne: es ist fuer 320
   Pixel gezeichnet, nicht dafuer verkleinert. Der viewBox ist 320 breit, der
   Deckel 360 - auf einem 360-px-Telefon steht der Inhalt der Seite auf rund
   328 px, das Bild wird also eher leicht vergroessert als geschrumpft, und
   die 12 px der kleinsten Zeile bleiben 12 px. */
.figur-klein{margin:0;padding:0}
.schichten{display:block;width:100%;max-width:360px;height:auto;margin:0 auto}
.figur-klein figcaption{margin-top:12px;font-size:13.5px;color:var(--tx2);
  text-align:center}

/* ==========================================================================
   LAYER 4 - phone polish: warmth and feedback where a thumb does the reading
   ==========================================================================
   Findings of the three phone review lanes (M2 charm, M3 technique, M4
   reading flow), applied in one pass. Everything here is narrow-screen or
   touch scoped; the desktop reads exactly as before.                        */

/* Every anchor target keeps a breath of air above it when jumped to - nine
   of the twenty targets used to land flush on pixel zero.                   */
[id]{scroll-margin-top:24px}

/* A tap is not a hover: kill the grey system rectangle Android paints over
   rounded corners, and answer the finger with the element itself instead.   */
a,button,summary,label{-webkit-tap-highlight-color:transparent}
@media (hover:none){
  .knopf:active{transform:scale(.97)}
  .nav a:active,.fuss nav a:active,.methode>summary:active{background:var(--panel2)}
  .karte:active,.mess:active{translate:0 1px}
}

/* The seam: subpages open with a small hero and used to fall straight into
   the page. The footer closes with a rule; the small hero now answers it
   with the house gradient, so front and subpage read as one fabric.         */
.hero.klein::after{content:"";position:absolute;left:0;right:0;bottom:0;
  height:2px;background:var(--grad);opacity:.35}

/* The menu arrives instead of appearing: `display` cannot animate, but the
   entries can - each list item runs its short rise when the checkbox puts
   the nav into the tree, lightly staggered. The burger turns to say the
   state changed. Both sit inside the motion gate; the reduced-motion block
   below stops them with everything else.                                    */
@media (max-width:768px){
  .menu-knopf::before{transition:transform .25s cubic-bezier(.22,.61,.36,1)}
  .menu-schalter:checked ~ .menu-knopf::before{transform:rotate(90deg)}
  @media (prefers-reduced-motion:no-preference){
    .menu-schalter:checked ~ .nav li{animation:menu-tritt .3s cubic-bezier(.22,.61,.36,1) both}
    .menu-schalter:checked ~ .nav li:nth-child(2){animation-delay:.03s}
    .menu-schalter:checked ~ .nav li:nth-child(3){animation-delay:.06s}
    .menu-schalter:checked ~ .nav li:nth-child(4){animation-delay:.09s}
    .menu-schalter:checked ~ .nav li:nth-child(5){animation-delay:.12s}
    .menu-schalter:checked ~ .nav li:nth-child(6){animation-delay:.15s}
    .menu-schalter:checked ~ .nav li:nth-child(n+7){animation-delay:.18s}
  }
}
@keyframes menu-tritt{from{opacity:0;translate:0 -6px}to{opacity:1;translate:0 0}}
@media (prefers-reduced-motion:reduce){
  .menu-schalter:checked ~ .nav li{animation:none!important}
  .menu-knopf::before{transition:none!important}
}

/* A reservation in a hero is a side note, not a second lede: on a phone the
   long carefulness paragraphs step back to a quiet left-ruled aside. Body
   `.hinweis` boxes (the band closers) are untouched - they are not in a hero. */
@media (max-width:610px){
  .hero .hinweis,.hero .dach.mt-m{
    font-size:14px;line-height:1.55;color:var(--tx2);
    border:0;border-left:2px solid var(--line2);border-radius:0;
    padding:2px 0 2px 14px;background:none}
  .hero .hinweis b{color:var(--tx)}
}

/* Full-width buttons made every choice look the same; the primary one now
   carries a filled surface, so the stack has one clear first step.          */
@media (max-width:560px){
  .knoepfe .knopf.prim{background:var(--flaeche-h);
    box-shadow:var(--schatten)}
}
