/* IntoInside, Startseite (index.html, bis 21.10.2026 als Vorschau index-neu.html)
   Wird NUR von der Startseite (index.html) geladen, nach app.css, und baut auf deren Tokens (:root) und
   Komponenten auf. Neue Klassen tragen ein Modul-Praefix (ex- Hero, lm- Live-Messung,
   ww- Wegweiser, bt- Bento, tl- Ablauf, qr- Zitat und Schriftring, cnt- Zaehler, sp Fortschritt),
   weil .badge, .row und .step in app.css schon global belegt sind.
   Reihenfolge: Basis -> Hero -> Module -> reduzierte Bewegung -> Druck. Der Block fuer
   reduzierte Bewegung muss hinten stehen: @media erhoeht die Spezifitaet nicht, er gewinnt
   nur ueber die Reihenfolge. */

/* ===== Basis ===== */
/* Die Ersatzschrift 'Manrope Fallback' (gegen Layout-Spruenge beim Schriftwechsel) steht in
   app.css und gilt fuer alle Seiten. Versal-Elemente dieser Seite bekommen wie dort die
   Versal-Ersatzschrift. */
.ex-kn,.bt-pill,.bt-wtop span,.ww-pk span{font-family:var(--font-caps)}

/* Anker-Spruenge und der Browser-Fokus landen sonst unter dem festen 78px-Header (WCAG 2.4.11).
   Nie overflow an html setzen: Dann wandert body{overflow-x:hidden} nicht mehr an den Viewport
   weiter, body wird selbst zum Scroll-Container und position:sticky im Hero bricht. */
html{scroll-padding-top:78px}
/* Den deckenden Header schaltet app.js beim Scrollen ein. Ohne JS bliebe er durchsichtig, und der
   Text liefe lesbar unter Logo und Navigation durch. */
@media(scripting:none){header{background:rgba(255,255,255,.92);border-bottom-color:var(--line)}}

/* Fortschrittsbalken unter dem Header, reines CSS ueber eine Scroll-Timeline. Browser ohne
   Unterstuetzung (derzeit Firefox) zeigen ihn einfach nicht, er bleibt auf scaleX(0). */
.sp{position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--accent);transform-origin:0 50%;transform:scaleX(0);pointer-events:none}
@supports (animation-timeline:scroll()){
  /* animation-timeline muss NACH der Kurzschreibweise stehen, animation setzt sie sonst zurueck. */
  .sp{animation:sp auto linear both;animation-timeline:scroll(root)}
}
@keyframes sp{to{transform:scaleX(1)}}

/* ===== Hero: Explosionszeichnung ===== */
/* 2.5D statt gemeinsamer 3D-Szene: Jede Platte wird einzeln gleich gedreht (kein preserve-3d),
   die Explosion ist eine reine senkrechte Verschiebung im Bildschirmraum. Bei Isometrie ist das
   exakt und erspart Tiefensortierung, z-Fighting und das Flattening in Safari.
   Gesteuert ueber Variablen auf .ex-scene: --t Kippung 0..1, --x Explosion 0..1 (start.js
   schreibt sie pro Frame), je Schicht --i = 2..-2. Die DOM-Reihenfolge l5..l1 regelt die
   Ueberdeckung. Ohne JS, im statischen Modus und bei reduzierter Bewegung steht die
   Zeichnung explodiert da (--t:1;--x:1). */
.ex{padding:78px 0 0}
/* Das Wechselwort steht schon vor app.js in eigener Zeile, wie danach als Grid (.rot-ready).
   Sonst bricht die H1 erst zweizeilig, dann dreizeilig um, und das sichtbare Intro springt
   (die alte Startseite versteckte das hinter dem Einblenden; hier ist die H1 sofort sichtbar). */
.ex h1 .rot{display:block}
/* Platzhalter fuer die Breite: das breiteste Wechselwort als Pseudo-Element in derselben Grid-Zelle
   (start.js setzt data-ghost). Anders als Blindwoerter kein DOM-Text: In der H1 steht auch fuer
   Suchmaschinen nur der sichtbare Satz. */
.ex h1 .rot.rot-ready::after{content:attr(data-ghost);grid-area:1/1;visibility:hidden;overflow-wrap:anywhere;hyphens:auto}
.ex-intro{display:flex;align-items:center;padding:36px 0 40px}
.ex-intro-in{width:100%}
.ex-hint{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;margin-top:22px;font-size:14px;color:var(--muted)}
.ex-mouse{position:relative;flex:0 0 auto;width:18px;height:28px;border:2px solid var(--field);border-radius:10px}
/* Nur zwei Durchlaeufe (3,6 s): Endlose Bewegung braeuchte einen Schalter zum Anhalten (WCAG 2.2.2). */
.ex-mouse::after{content:'';position:absolute;left:50%;top:5px;width:3px;height:6px;margin-left:-1.5px;border-radius:2px;background:var(--accent);animation:ex-wheel 1.8s ease-in-out 2}
@keyframes ex-wheel{0%{transform:none;opacity:1}70%{transform:translateY(8px);opacity:0}100%{transform:none;opacity:0}}
/* padding: Klickflaeche mindestens 24px hoch (WCAG 2.5.8). */
.ex-skip{padding:2px 0;color:var(--accent);font-weight:700;text-decoration:underline;text-underline-offset:3px}

/* Buehne. Grundzustand = statischer Modus (geringe Hoehe): feste Zeichnung, kein Sticky.
   Das Live-Layout setzen die Media-Queries weiter unten. */
.ex-stage{position:relative;height:300px;max-width:720px;margin:0 auto 24px;container-type:size}
.ex-scene{position:absolute;inset:0;
  /* --f = Flach-Groesse : explodierte Groesse. Im statischen Modus wird nie flach, die Platten
     brauchen dort nur ihre explodierte Groesse (sonst ragen ihre Boxen bei 320px seitlich hinaus). */
  --t:1;--x:1;--f:1;--k:.5;--stack:2.96;--lab:150px;--shift:-75px;
  /* --he = Plattenhoehe im explodierten Zustand. Breite: Raute (1.83 he) plus Platz fuer die
     Beschriftung; Hoehe: Stapel (2.96 he bei k=.5) in 86 % der Buehne. Die Variablen sitzen auf
     .ex-scene, einem Kind der Container-Buehne, damit die cq-Einheiten greifen. Nicht per
     @property registrieren, sonst wuerden sie hier schon ausgerechnet. --hmax deckelt nur das
     mobile Live-Layout (siehe dort), sonst greift der Ersatzwert. */
  --he:min(calc((95cqw - var(--lab))/1.83),calc(86cqh/var(--stack)),var(--hmax,9999px));
  --ph:calc(var(--he)*var(--f));--pw:calc(var(--ph)*1.55);--gap:calc(var(--he)*.986*var(--k));
  /* Reihenfolge stimmt: CSS wendet von rechts nach links an, also erst Skalierung und Drehung in
     der Ebene, dann das Zurueckkippen. Gerastert wird in Flach-Groesse und zum Explodieren
     herunterskaliert, deshalb bleiben die Platten scharf. */
  --rot:perspective(2400px) rotateX(calc(var(--t)*56deg)) rotateZ(calc(var(--t)*-40deg)) scale(calc(1 - var(--t)*(1 - 1/var(--f))));
  /* Beim Kippen nach links ruecken, damit rechts Platz fuer die Positions-Pins bleibt. */
  translate:calc(var(--t)*var(--shift)) 0}
/* Eine ungueltige Variable faellt nicht auf eine fruehere Deklaration zurueck, deshalb der
   px-Ersatz ausdruecklich fuer Browser ohne Container-Einheiten. */
@supports not (width:1cqw){.ex-scene{--he:220px}}
@container (max-width:520px){.ex-scene{--lab:40px;--shift:-20px}.ex-tag i{display:none}}
@media(max-width:980.98px){
  .ex-scene{--k:.35;--stack:2.37}
  @supports not (width:1cqw){.ex-scene{--he:120px}.ex-tag i{display:none}}
}

.ex-layer{position:absolute;left:50%;top:50%;width:var(--pw);height:var(--ph);margin:calc(var(--ph)/-2) 0 0 calc(var(--pw)/-2);translate:0 calc(var(--i)*var(--gap)*var(--x)*-1)}
.l1,.t1{--i:2}.l2,.t2{--i:1}.l3,.t3{--i:0}.l4,.t4{--i:-1}.l5,.t5{--i:-2}
/* Im flachen Zustand liegt alles unter der UI-Platte. Ohne Ausblenden blitzt an ihrem Rand die
   dunkle Code-Platte durch (Kantenglaettung). */
.ex-layer:not(.l1){opacity:clamp(0,var(--x)*6,1)}
/* translate wirkt NACH transform, also im Bildschirmraum: Die Schublade (--open, mit Transition)
   kommt der Explosion (pro Frame auf .ex-layer) nicht in die Quere. Nie eine Eigenschaft per
   Transition animieren, die pro Frame geschrieben wird, sonst hinkt sie hinterher. */
.ex-face,.ex-edge{position:absolute;inset:0;border-radius:14px;transform:var(--rot);translate:0 calc(var(--open,0)*-18px);transition:translate .45s cubic-bezier(.2,.7,.2,1)}
.ex-face{overflow:hidden;background:#fff;box-shadow:0 0 0 1px rgba(20,22,27,.07),0 24px 50px -30px rgba(20,22,27,.35);transition:translate .45s cubic-bezier(.2,.7,.2,1),box-shadow .3s}
.l2 .ex-face{background:#14161B}.l3 .ex-face{background:#EAF0FF}.l5 .ex-face{background:#F5F6F8}
.ex-face svg{width:100%;height:100%}
/* Plattendicke: dieselbe Drehung, im Bildschirmraum 7px nach unten versetzt. */
.ex-edge{background:#C9D6F5;transform:translateY(calc(var(--t)*7px)) var(--rot);opacity:clamp(0,var(--t)*5,1)}
.ex-layer.on .ex-face{box-shadow:0 0 0 3px var(--accent),0 0 0 10px rgba(42,98,240,.14),0 24px 50px -30px rgba(20,22,27,.35)}
/* Schublade: Die Schichten ueber der aktiven heben sich um 18px. */
.ex-layer.on~.ex-layer{--open:1}
/* will-change nur, solange der Hero in der Naehe ist (start.js setzt .is-near). */
.ex.is-near .ex-face,.ex.is-near .ex-edge{will-change:transform}

/* Positions-Pins wie in einer echten Explosionszeichnung: an der rechten Plattenecke
   (.915 he rechts und .064 he ueber der Plattenmitte), mit 1px-Fuehrungslinie. */
.ex-tags{position:absolute;inset:0;pointer-events:none}
.ex-tag{position:absolute;left:calc(50% + .915*var(--he));top:calc(50% - .064*var(--he));translate:0 calc(var(--i)*var(--gap)*var(--x)*-1);opacity:clamp(0,(var(--x) - .4)*3,1)}
.ex-tag-in{display:flex;align-items:center;gap:8px;margin-top:-12px;translate:0 calc(var(--open,0)*-18px);transition:translate .45s cubic-bezier(.2,.7,.2,1)}
.ex-tag-in::before{content:'';width:16px;height:1px;background:var(--field)}
/* Aktiv: weisse Zahl auf Akzent 5.12:1; Name in --muted 5.07:1 auf weiss. */
.ex-tag b{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:#fff;border:1px solid var(--field);color:var(--ink);font-size:11px;font-weight:800;transition:background .3s,border-color .3s,color .3s}
.ex-tag i{font-style:normal;font-size:13px;font-weight:700;color:var(--muted);white-space:nowrap;transition:color .3s}
.ex-tag.on b{background:var(--accent);border-color:var(--accent);color:#fff}
.ex-tag.on i{color:var(--ink)}
.ex-tag.on~.ex-tag{--open:1}

/* Schritte */
.ex-body{padding:28px 0 0}
/* em statt ch wie .hero .sub in app.css (21.10.2026): 1ch = 0.61em in Manrope, 0.57em in der
   Ersatzschrift. 28.06em = 46ch und 31.72em = 52ch in Manrope, dort aendert sich nichts. */
.ex-body>.lead{max-width:28.06em}
.ex-steps{list-style:none;margin-top:30px;border-top:1px solid var(--line)}
.ex-step{padding:26px 0 30px;border-bottom:1px solid var(--line)}
.ex-step h3{display:grid;grid-template-columns:auto 1fr;align-items:center;column-gap:12px;font-size:clamp(22px,2.4vw,28px);letter-spacing:-.02em}
.ex-n{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;border:2px solid var(--accent);color:var(--accent);font-size:12px;font-weight:800;letter-spacing:0;transition:background .3s,color .3s}
.ex-kn{font-size:13px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--accent)}
.ex-tt{grid-column:1/-1;margin-top:12px}
.ex-step p{margin-top:10px;max-width:31.72em}
.ex-step .ex-links{display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:14px}
.ex-links a{display:inline-flex;align-items:center;gap:7px;font-size:15px;font-weight:700;color:var(--accent)}
.ex-links a::after{content:'';width:6px;height:6px;border:solid currentColor;border-width:2px 2px 0 0;transform:rotate(45deg)}
.ex-links a:hover{text-decoration:underline;text-underline-offset:3px}
.ex-step.on .ex-n{background:var(--accent);color:#fff}
.ex-outro{display:flex;flex-direction:column;justify-content:center;padding:52px 0 76px}
.ex-claim{font-size:clamp(32px,3.8vw,50px);font-weight:800;line-height:1.1;letter-spacing:-.03em;color:var(--ink)}
.ex-claim em{font-style:normal;color:var(--accent)}
.ex-claim-sub{margin-top:16px;font-size:clamp(17px,1.8vw,19px);color:var(--muted)}
.ex-outro .btns{display:flex;flex-wrap:wrap;gap:14px;margin-top:30px}

/* Live-Layout Desktop: Buehne in Spalte 2 ueber beide Zeilen, sticky. Ein sticky Grid-Item nutzt
   seine Grid-Flaeche als Rahmen und laeuft am Ende mit dem Schluss aus dem Bild.
   Dieselbe Abfrage steht als LIVE in start.js. */
@media(min-width:981px) and (min-height:520px){
  .ex-grid{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);column-gap:clamp(32px,4vw,56px)}
  .ex-intro{grid-column:1;grid-row:1;min-height:calc(100vh - 78px);min-height:calc(100svh - 78px);padding:24px 0}
  .ex-stage{grid-column:2;grid-row:1/span 2;align-self:start;position:sticky;top:78px;height:calc(100vh - 78px);height:calc(100svh - 78px);max-width:none;margin:0}
  .ex-body{grid-column:1;grid-row:2}
  .ex-scene{--f:1.6}
  /* Eigene Groesse fuer die schmale Intro-Spalte: Bei 72px bricht "Schnittstellen" sonst um. */
  .ex h1{font-size:clamp(34px,3.7vw,56px)}
  .ex-js .ex-step{min-height:64vh;min-height:64svh}
  .ex-outro{min-height:calc(100vh - 78px);min-height:calc(100svh - 78px)}
}
/* Live-Layout mobil: Buehne sticky unter dem Header, randlos und deckend, die Schritte laufen
   darunter durch. Klicks treffen die Buehne, nicht die verdeckten Links. */
@media(max-width:980.98px) and (min-height:620px){
  .ex{background:#fff}
  /* overflow-x:clip als Sicherheitsnetz: Was seitlich ueber die (randlose) Buehne hinausragt, macht
     die Seite nicht breiter. clip erzeugt anders als hidden keinen Scroll-Container, sticky und die
     Explosion nach oben und unten bleiben also unberuehrt (overflow-y bleibt visible). */
  .ex-stage{position:sticky;top:78px;z-index:2;height:46vh;height:clamp(260px,46svh,420px);max-width:none;margin:0 -24px;background:#fff;overflow-x:clip}
  .ex-stage::after{content:'';position:absolute;left:0;right:0;top:100%;height:24px;background:linear-gradient(#fff,rgba(255,255,255,0));pointer-events:none}
  /* Flach ist jede Platte 1.55 * 1.7 = 2.635 he breit. Auf hohen, schmalen Handys (z. B. 320x900)
     waren das 380px, die Seite wurde 350px breit und der Burger rutschte aus dem Bild. --hmax haelt
     die flache Platte bei hoechstens 95 % der Buehnenbreite. */
  .ex-scene{--f:1.7;--hmax:calc(95cqw/1.55/var(--f))}
  /* Fokus und Anker im Hero nie unter der Buehne: Der Browser rechnet die Buehnenhoehe beim
     Hineinscrollen gleich mit ein (der Fokus-Waechter in start.js ist nur die Rueckfallebene). */
  .ex-body a,.ex-step{scroll-margin-top:calc(46vh + 8px);scroll-margin-top:calc(clamp(260px,46svh,420px) + 8px)}
  .ex-js .ex-step{min-height:46vh;min-height:46svh}
  /* Abstand vor dem Schluss, damit Schritt 05 so lange aktiv bleibt wie die anderen. */
  .ex-js .ex-outro{padding-top:32vh;padding-top:32svh}
}
/* Mit JS startet die Buehne flach, ohne Sprung beim Laden. Nur im Live-Layout; Browser ohne
   (scripting)-Abfrage zeigen kurz den Zustand ohne JS. */
@media(scripting:enabled){
  @media(min-width:981px) and (min-height:520px),(max-width:980.98px) and (min-height:620px){
    .ex-scene{--t:0;--x:0}
  }
}

/* ===== Live-Messung ===== */
.lm-grid{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);gap:clamp(32px,5vw,64px);align-items:center}
/* minmax(0,1fr) statt 1fr: Sonst bestimmt der laengste Messwert die Spaltenbreite, und mobil
   wurde die ganze Seite breiter als das Display (z. B. mit Hostnamen nach Bing-Zustimmung). */
@media(max-width:900px){.lm-grid{grid-template-columns:minmax(0,1fr)}}
.lm h2 em{font-style:normal;color:var(--accent)}
.lm-facts{list-style:none;display:grid;gap:12px;margin-top:26px}
.lm-facts li{display:flex;gap:11px;align-items:flex-start;font-size:15.5px}
.lm-facts .ico{width:19px;height:19px;flex:0 0 auto;margin-top:3px;color:var(--accent)}
/* Farben auf dem dunklen Terminal (#14161B), Kontrast in Klammern:
   Bewertung gut #6fd3a0 (9.9) / mittel #f2c46b (11.1) / schwach #ff8f8f (8.3),
   Beschriftung #9aa1ac (7.0). Die Bewertung steht immer auch als Wort da, nie nur als Farbe.
   Bewusst eigene lm-Klassen: .c, .s und .k faerbt app.css innerhalb von .code ein. */
.lm-term .top em{background:rgba(111,211,160,.12);border-color:rgba(111,211,160,.35);color:#6fd3a0}
.lm-bd{padding:20px 22px 22px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13.5px;line-height:1.7;color:#e6e8ec}
.lm-p{color:#9fc1ff}
.lm-list{margin:12px 0 14px}
/* Mindesthoehe statt fester Hoehe: Der Text steht schon fest, bevor das Terminal ins Bild kommt
   (CLS bleibt 0). Lange Werte brechen um, statt abgeschnitten zu werden; auch mit vergroesserten
   Textabstaenden und bei 320px geht nichts verloren (WCAG 1.4.10 und 1.4.12). */
.lm-row{display:flex;align-items:baseline;justify-content:space-between;gap:2px 16px;min-height:30px;padding:4px 0;line-height:1.6;border-bottom:1px dashed rgba(255,255,255,.09)}
.lm-row dt{flex:0 0 auto;color:#9aa1ac;white-space:nowrap}
.lm-row dd{min-width:0;overflow-wrap:anywhere;text-align:right;font-weight:700;color:#fff}
.lm-r{margin-left:8px}
.lm-good{color:#6fd3a0}.lm-mid{color:#f2c46b}.lm-bad{color:#ff8f8f}
.lm-dim{color:#9aa1ac;font-weight:400}
.lm-foot{color:#9aa1ac;font-size:12.5px}
.lm-end{margin-top:6px}
/* Der Cursor blinkt nur viermal (4,4 s), sobald das Terminal ins Bild kommt, dann steht er:
   Blinken ueber 5 s braeuchte einen Schalter zum Anhalten (WCAG 2.2.2). */
.lm-cur{display:inline-block;width:8px;height:15px;background:#e6e8ec;vertical-align:-2px}
.lm.is-vis .lm-cur{animation:lm-blink 1.1s steps(1) 4}
@keyframes lm-blink{50%{opacity:0}}
.lm-nojs{color:#9aa1ac;margin:12px 0}
/* Ohne JS misst hier nichts: keine leeren Zeilen, kein "Live"-Schild, keine Fussnote zur Messung. */
@media(scripting:none){.lm-list,.lm-term .top em,.lm-foot{display:none}}
@media(max-width:420px){
  /* Titelzeile umbrechen statt mit ... kuerzen (app.css kuerzt Panel-Titel). */
  .lm-term .top span{white-space:normal}
  .lm-bd{padding:18px 16px 20px;font-size:13px}
  .lm-row{flex-direction:column;align-items:flex-start;justify-content:center;gap:0;min-height:52px}
  .lm-row dt{white-space:normal}
  .lm-row dd{max-width:100%;text-align:left}
}
/* Aufdecken wie getippt, nur mit JS (.lm-js) und nur, wenn der Text schon endgueltig steht.
   clip-path versteckt nichts vor Vorlesehilfen. */
.lm-js .lm-row{clip-path:inset(0 100% 0 0);transition:clip-path .5s steps(24) calc(var(--n)*.12s)}
.lm-js.is-vis .lm-row{clip-path:inset(0)}

/* ===== Zahlen ===== */
/* Im DOM steht nur der echte Wert (.cnt-wert), fuer Vorlesehilfen und Suchmaschinen. Beim
   Hochzaehlen (.cnt-anim, start.js) ist er unsichtbar, bleibt aber lesbar (opacity, nicht
   visibility) und haelt die Breite des Endwerts. Darueber zeigt ein Pseudo-Element die laufende
   Zahl, linksbuendig in derselben Grid-Zelle: Das "+" bzw. "%" daneben verrutscht nicht (CLS).
   Der leere Alternativtext ("") haelt die laufende Zahl von Vorlesehilfen fern; Browser ohne diese
   Schreibweise zaehlen gar nicht erst hoch (start.js prueft das mit CSS.supports). */
.cnt{display:inline-grid;justify-items:start}
.cnt>*{grid-area:1/1}
@supports (content:"x" / ""){
  .cnt.cnt-anim>*{opacity:0}
  .cnt.cnt-anim::after{content:attr(data-show) / "";grid-area:1/1}
}

/* ===== Ueber mich mit Schriftring ===== */
.ab{background:var(--bg)}
.ab .lead{margin-top:20px}
.ab .quote{margin-top:26px}
.qr-av{position:relative;display:flex;align-items:center;justify-content:center;width:224px;height:224px;margin:0 auto}
.qr-av .avatar{width:132px;height:132px;border-radius:50%;font-size:46px;object-fit:cover}
/* Text in Akzent auf weiss: 5.12:1. Der Kreis hat r=80, also 2*pi*80 = 502 Einheiten. */
.qr-ring{position:absolute;inset:0;width:100%;height:100%;fill:var(--accent);font-size:12.5px;font-weight:800;letter-spacing:.04em}
/* Der Ring dreht sich nur mit dem Scrollen (Scroll-Timeline): Die Bewegung folgt dem Nutzer, nichts
   laeuft von selbst (WCAG 2.2.2). Browser ohne Scroll-Timelines zeigen ihn ruhig. */
@supports (animation-timeline:view()){.qr-ring{animation:qr-spin linear both;animation-timeline:view()}}
@keyframes qr-spin{to{transform:rotate(1turn)}}
@media(max-width:760px){.qr-av{width:184px;height:184px}.qr-av .avatar{width:108px;height:108px;font-size:38px}}
/* Zitat Wort fuer Wort: von --muted (5.07:1) zu --ink bzw. Akzent (5.12:1). Beide Zustaende
   erfuellen AA, es wird keine Transparenz verwendet. Das Original bleibt fuer Vorlesehilfen. */
.quote.qr-copy .qr-w{color:var(--muted);transition:color .25s}
.quote.qr-copy .qr-w.on{color:var(--ink)}
.quote.qr-copy em .qr-w.on{color:var(--accent)}

/* ===== Leistungen als Bento ===== */
/* Im DOM in Lesereihenfolge (tempo, sich, wart, shop, soft, api), damit die Fokus-Reihenfolge
   zum Raster passt. */
.bt-grid{display:grid;grid-template-columns:1fr;grid-auto-rows:minmax(200px,auto);gap:20px}
@media(min-width:640px){.bt-grid{grid-template-columns:1fr 1fr}.bt-a-tempo,.bt-a-api{grid-column:span 2}}
@media(min-width:1100px){
  .bt-grid{grid-template-columns:repeat(4,minmax(0,1fr));grid-template-areas:"tempo tempo sich wart" "tempo tempo shop shop" "soft soft api api"}
  .bt-a-tempo{grid-area:tempo}.bt-a-sich{grid-area:sich}.bt-a-wart{grid-area:wart}.bt-a-shop{grid-area:shop}.bt-a-soft{grid-area:soft}.bt-a-api{grid-area:api}
}
.bt-cell{display:flex;min-width:0}
/* Hover ueber translate statt transform: transform gehoert schon dem Einblenden ([data-reveal]),
   sonst gewinnt dessen transform:none und die Kachel hebt sich nie. */
.bt{position:relative;display:flex;flex:1;flex-direction:column;min-width:0;padding:24px 26px 26px;overflow:hidden;background:#fff;border:1px solid var(--line);border-radius:20px;transition:translate .18s,box-shadow .18s,border-color .18s}
.bt:hover{translate:0 -4px;box-shadow:var(--shadow);border-color:transparent}
.bt h3{position:relative;font-size:20px;margin-bottom:8px}
.bt p{position:relative;font-size:15px;max-width:46ch}
.bt-tempo{background:linear-gradient(165deg,#fff 45%,var(--accent-soft))}
.bt-tempo h3{font-size:clamp(22px,2.4vw,28px)}
/* Lichtschimmer unter der Maus (nur hover:hover und pointer:fine), Position aus --mx/--my. */
.bt::before{content:'';position:absolute;inset:0;background:radial-gradient(260px circle at var(--mx,50%) var(--my,50%),rgba(42,98,240,.10),transparent 70%);opacity:0;transition:opacity .3s;pointer-events:none}
@media(hover:hover) and (pointer:fine){.bt:hover::before{opacity:1}}
/* Mini-Animationen: Dekoration (aria-hidden), nur transform und opacity, keine Zahlen.
   Jede spielt genau einmal, sobald ihre Kachel ins Bild kommt, endet nach spaetestens 5 s und
   bleibt im Endbild stehen (WCAG 2.2.2: laengere Bewegung braeuchte einen Schalter zum Anhalten).
   Bis dahin wartet sie pausiert im Startbild. Ohne Animation steht immer das Endbild da. */
.bt-viz{position:relative;flex:1;display:flex;align-items:center;justify-content:center;min-height:92px;margin-bottom:18px}
/* Spezifischer als die einzelnen Regeln (.bt-checks i ...): Deren Kurzschreibweise animation
   setzt animation-play-state sonst stillschweigend wieder auf running. */
@media(scripting:enabled){.bt .bt-viz *,.bt .bt-viz *::before{animation-play-state:paused}.bt.is-vis .bt-viz *,.bt.is-vis .bt-viz *::before{animation-play-state:running}}
.bt-tempo .bt-viz{padding-top:36px}
.bt-pill{position:absolute;top:0;right:0;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);background:#fff;border:1px solid var(--line);padding:3px 9px;border-radius:99px}
/* Tempo: zwei Mini-Browser laden um die Wette, "nachher" ist zuerst fertig. */
.bt-race{display:grid;grid-template-columns:1fr 1fr;gap:16px;width:100%;max-width:520px}
.bt-win{min-width:0;overflow:hidden;background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:0 18px 40px -28px rgba(20,22,27,.35)}
.bt-wtop{display:flex;align-items:center;gap:5px;padding:8px 10px;background:var(--soft);border-bottom:1px solid var(--line)}
.bt-wtop i{width:7px;height:7px;border-radius:50%;background:#D8DCE2}
/* --muted auf --soft: 4.68:1 */
.bt-wtop span{margin-left:auto;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.bt-load{display:block;height:3px;background:var(--line)}
.bt-load i{display:block;height:100%;transform-origin:0 50%}
.bt-slow{background:#9aa1ac;animation:bt-bar 4.2s cubic-bezier(.3,.1,.3,1) both}
.bt-fast{background:var(--accent);animation:bt-bar 1.4s cubic-bezier(.2,.7,.2,1) both}
@keyframes bt-bar{from{transform:scaleX(0)}}
.bt-sk{display:grid;gap:7px;padding:12px}
.bt-sk b{display:block;height:7px;border-radius:99px;background:var(--line)}
.bt-sk .bt-h{height:30px;border-radius:7px;background:var(--accent-soft)}
.bt-sk .bt-s{width:60%}
.bt-cards{display:grid;grid-template-columns:1fr 1fr;gap:7px}
.bt-sk .bt-cards b{height:26px;border-radius:6px;background:var(--soft)}
/* Der Inhalt erscheint, wenn der jeweilige Ladebalken voll ist. */
.bt-sk-slow{animation:bt-sk .4s ease 4s both}
.bt-sk-fast{animation:bt-sk .4s ease 1.2s both}
@keyframes bt-sk{from{opacity:.15}}
/* In der grossen Kachel (2x2) duerfen die Fenster mehr Flaeche nutzen. */
@media(min-width:1100px){.bt-tempo .bt-sk{gap:10px;padding:16px}.bt-tempo .bt-sk .bt-h{height:58px}.bt-tempo .bt-sk .bt-cards b{height:44px}}
.bt-checks{list-style:none;display:grid;gap:10px;width:100%;max-width:200px}
.bt-checks li{display:flex;align-items:center;gap:10px}
.bt-checks i{position:relative;flex:0 0 auto;width:22px;height:22px;border-radius:50%;background:var(--ok-bg);animation:bt-chk .5s cubic-bezier(.2,.7,.2,1) .2s both}
.bt-checks i::after{content:'';position:absolute;left:8px;top:4px;width:5px;height:10px;border:solid var(--ok);border-width:0 2.5px 2.5px 0;transform:rotate(45deg)}
.bt-checks span{flex:1;height:7px;border-radius:99px;background:var(--line)}
.bt-checks li:nth-child(2) span{flex:0 0 70%}.bt-checks li:nth-child(3) span{flex:0 0 82%}
.bt-checks li:nth-child(2) i{animation-delay:.7s}.bt-checks li:nth-child(3) i{animation-delay:1.2s}
@keyframes bt-chk{from{transform:scale(0);opacity:0}}
/* Wartung: In einer kleinen Plugin-Liste kippt "Update" nacheinander zu "Aktuell".
   --warn auf --warn-bg 4.89:1, --ok auf --ok-bg 4.9:1. */
.bt-upds{list-style:none;display:grid;gap:9px;width:100%;max-width:230px}
.bt-upds li{display:flex;align-items:center;gap:9px}
.bt-upds i{flex:0 0 auto;width:20px;height:20px;border-radius:6px;background:var(--accent-soft)}
.bt-upds span{flex:1;height:7px;border-radius:99px;background:var(--line)}
.bt-flip{display:grid;justify-items:end;font-style:normal}
.bt-flip>*{grid-area:1/1;font-size:11.5px;font-weight:800;border-radius:99px;padding:4px 9px}
.bt-upd{background:var(--warn-bg);color:var(--warn);opacity:0;animation:bt-upd 2.2s ease-in-out both}
.bt-ok{background:var(--ok-bg);color:var(--ok);animation:bt-ok 2.2s ease-in-out both}
.bt-upds li:nth-child(2) .bt-flip>*{animation-delay:.35s}.bt-upds li:nth-child(3) .bt-flip>*{animation-delay:.7s}
@keyframes bt-upd{0%,55%{transform:none;opacity:1}100%{transform:perspective(200px) rotateX(90deg);opacity:0}}
@keyframes bt-ok{0%,60%{transform:perspective(200px) rotateX(-90deg);opacity:0}100%{transform:none;opacity:1}}
.bt-path{position:relative;width:calc(100% - 84px);max-width:420px;height:58px}
.bt-st{position:absolute;top:0;font-size:12.5px;font-weight:700;color:var(--muted);transform:translateX(-50%);white-space:nowrap}
.bt-st:nth-child(1){left:0}.bt-st:nth-child(2){left:50%}.bt-st:nth-child(3){left:100%}
.bt-track{position:absolute;left:0;right:0;top:40px;height:2px;border-radius:2px;background:var(--line)}
.bt-track::before,.bt-track::after{content:'';position:absolute;top:-3px;width:8px;height:8px;border-radius:50%;background:var(--line)}
.bt-track::before{left:-4px}.bt-track::after{right:-4px}
.bt-run{position:absolute;inset:0;transform:translateX(100%);animation:bt-run 3.2s cubic-bezier(.5,0,.3,1) both}
.bt-run i{position:absolute;left:0;top:50%;width:14px;height:14px;margin:-7px 0 0 -7px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 5px rgba(42,98,240,.16)}
@keyframes bt-run{0%,10%{transform:none}45%,55%{transform:translateX(50%)}100%{transform:translateX(100%)}}
.bt-dash{display:flex;align-items:flex-end;gap:8px;width:100%;max-width:220px;height:88px;padding:12px 14px;background:#fff;border:1px solid var(--line);border-radius:12px}
.bt-dash i{flex:1;height:60%;border-radius:4px 4px 2px 2px;background:var(--accent-soft);transform-origin:50% 100%;animation:bt-grow 1s cubic-bezier(.2,.7,.2,1) both}
.bt-dash i:nth-child(2){height:78%;animation-delay:.15s}.bt-dash i:nth-child(3){height:52%;animation-delay:.3s}.bt-dash i:nth-child(4){height:88%;animation-delay:.45s;background:var(--accent)}.bt-dash i:nth-child(5){height:66%;animation-delay:.6s}.bt-dash i:nth-child(6){height:96%;animation-delay:.75s}
@keyframes bt-grow{from{transform:scaleY(.12)}}
.bt-net{display:flex;align-items:center;width:100%;max-width:440px}
.bt-node{flex:0 0 auto;font-size:13px;font-weight:800;color:var(--ink);background:#fff;border:1px solid var(--line);border-radius:10px;padding:8px 12px}
.bt-wire{position:relative;flex:1;height:2px;margin:0 8px;background:var(--line)}
/* Ein Datenpaket laeuft Shop -> DATEV, danach eins DATEV -> ERP, und bleibt am Ziel liegen. */
.bt-wire>i{position:absolute;inset:0;transform:translateX(100%);animation:bt-pkt 1.6s ease-in-out .2s both}
.bt-wire>i::before{content:'';position:absolute;left:0;top:-3px;width:8px;height:8px;margin-left:-4px;border-radius:2px;background:var(--accent)}
.bt-wire~.bt-wire>i{animation-delay:1.1s}
@keyframes bt-pkt{0%{transform:none;opacity:0}15%{opacity:1}100%{transform:translateX(100%);opacity:1}}
/* Chip-Zeile zu den uebrigen Leistungsseiten */
.bt-more-h{margin-top:42px;text-align:center;font-size:15px;color:var(--muted)}
.bt-more{list-style:none;margin-top:14px}
.strip a.chip{display:inline-block;transition:border-color .15s,color .15s}
.strip a.chip:hover{border-color:var(--accent);color:var(--accent)}

/* ===== Wegweiser ===== */
.ww-box{max-width:960px;margin:0 auto}
.ww-q{display:flex;flex-wrap:wrap;justify-content:center;gap:10px}
.ww-q label{position:relative;cursor:pointer}
/* Unsichtbar, aber so gross wie der Chip (nicht width:0, damit VoiceOver das Radio zuverlaessig
   ansteuert). Beim Tabben scrollt der Browser so den ganzen Chip ins Bild, nicht nur eine Ecke. */
.ww-q input{position:absolute;left:0;top:0;width:100%;height:100%;margin:0;opacity:0;pointer-events:none}
.ww-q span{display:inline-block;padding:10px 18px;border:1px solid var(--field);border-radius:100px;background:#fff;font-size:15px;font-weight:700;color:var(--text);transition:background .15s,color .15s,border-color .15s}
.ww-q label:hover span{border-color:var(--accent);color:var(--accent)}
.ww-q input:checked+span{background:var(--ink);border-color:var(--ink);color:#fff}
.ww-q input:focus-visible+span{outline:2px solid var(--accent);outline-offset:3px}
.ww-ans{margin-top:30px}
.ww-a,.ww-empty{background:#fff;border:1px solid var(--line);border-radius:20px;padding:clamp(24px,3vw,34px)}
.ww-a+.ww-a{margin-top:16px}
.ww-a h3{font-size:clamp(20px,2.2vw,24px)}
.ww-a p{margin-top:10px;max-width:66ch}
.ww-a .ww-pk{margin-top:16px;font-size:15px;font-weight:600;color:var(--ink)}
.ww-pk span{display:inline-block;margin-right:8px;font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--accent)}
.ww-act{display:flex;flex-wrap:wrap;align-items:center;gap:12px 22px;margin-top:22px}
.ww-more{font-size:15px;color:var(--muted)}
.ww-more a{font-weight:700;color:var(--accent);text-decoration:underline;text-underline-offset:3px}
/* Browser ohne :has() sehen die Antworten als schlichte Liste, der Leerzustand ergibt dort keinen Sinn. */
.ww-empty{display:none}
.ww-empty a{color:var(--accent);font-weight:700;text-decoration:underline;text-underline-offset:3px}
/* Alle Antworten liegen in EINER Grid-Zelle: Die Hoehe richtet sich nach der laengsten, beim
   Wechsel springt also nichts. Funktioniert auch ohne JS. */
@supports selector(:has(*)){
  .ww-ans{display:grid}
  .ww-ans>*{grid-area:1/1}
  .ww-a+.ww-a{margin-top:0}
  .ww-empty{display:flex;align-items:center;justify-content:center;text-align:center;background:transparent;border-style:dashed;border-color:var(--field);color:var(--muted);transition:opacity .3s,visibility 0s}
  .ww-a{visibility:hidden;opacity:0;translate:0 8px;transition:opacity .3s,translate .3s,visibility 0s .3s}
  .ww:has(#ww-r1:checked) #ww-a1,.ww:has(#ww-r2:checked) #ww-a2,.ww:has(#ww-r3:checked) #ww-a3,.ww:has(#ww-r4:checked) #ww-a4,.ww:has(#ww-r5:checked) #ww-a5,
  .ww:has(#ww-r6:checked) #ww-a6,.ww:has(#ww-r7:checked) #ww-a7,.ww:has(#ww-r8:checked) #ww-a8,.ww:has(#ww-r9:checked) #ww-a9,.ww:has(#ww-r10:checked) #ww-a10,.ww:has(#ww-r11:checked) #ww-a11{visibility:visible;opacity:1;translate:none;transition-delay:0s}
  .ww:has(.ww-q input:checked) .ww-empty{visibility:hidden;opacity:0;transition:opacity .2s,visibility 0s .2s}
}
/* Vorbelegung im Formular: kurze Meldung (--ok auf weiss 5.35:1) und ein Puls am Chip. */
/* Themen-Chips im Formular: Das versteckte Input (app.css: width:0) ueber den ganzen Chip legen,
   damit der Browser beim Tabben den Chip vollstaendig ins Bild scrollt. */
.scope label{position:relative}
.scope input{left:0;top:0;width:100%;height:100%}
.ww-note{margin-top:10px;font-size:13.5px;font-weight:600;color:var(--ok)}
.ww-note:empty{display:none}
.scope span.is-prefilled{animation:ww-pulse .9s ease-out 2}
@keyframes ww-pulse{from{box-shadow:0 0 0 0 rgba(42,98,240,.45)}to{box-shadow:0 0 0 10px rgba(42,98,240,0)}}

/* ===== Projekte ===== */
/* Nach dem Filtern blenden die Karten ein. Auf den Kind-Elementen, damit es nicht mit dem
   transform von [data-reveal] auf der Karte selbst kollidiert. */
.proj:not(.hide)>*{animation:proj-in .4s cubic-bezier(.2,.7,.2,1) backwards}
@keyframes proj-in{from{opacity:0;transform:translateY(12px)}}
/* Ohne JS tun die Filter nichts, also gar nicht erst anbieten. */
@media(scripting:none){.tabs{display:none}}

/* ===== Pakete: umlaufender Lichtrand ===== */
/* Das Licht wandert mit dem Scrollen um die Karte (Scroll-Timeline), nicht von selbst (WCAG 2.2.2).
   Nur mit .pk-live: start.js setzt die Klasse, wenn @property und Scroll-Timelines unterstuetzt
   werden und keine reduzierte Bewegung gilt; sonst bleibt der statische Akzent-Rand aus app.css.
   Der Ersatzwert in var() haelt den Hintergrund gueltig, falls @property doch fehlt. */
@property --ang{syntax:'<angle>';inherits:false;initial-value:0deg}
.plan.pop.pk-live{border-color:transparent;background:linear-gradient(#fff,#fff) padding-box,conic-gradient(from var(--ang,0deg),#2A62F0,#9fc1ff,#2A62F0 40%) border-box;animation:pk-spin linear both;animation-timeline:view()}
@keyframes pk-spin{to{--ang:360deg}}

/* ===== Ablauf als Zeitleiste ===== */
/* --p fuellt die Linie (start.js), Standard 1 = voll: ohne JS und bei reduzierter Bewegung.
   Es aendert sich nur Dekoration, nie der Textkontrast. */
.tl{position:relative;max-width:980px;margin:0 auto;--p:1}
.tl-track{position:absolute;top:10px;bottom:10px;left:11px;width:2px;border-radius:2px;background:var(--line)}
.tl-fill{position:absolute;inset:0;border-radius:inherit;background:var(--accent);transform-origin:50% 0;scale:1 var(--p)}
.tl-list{list-style:none;display:grid;gap:30px}
.tl-i{position:relative;padding-left:46px}
.tl-dot{position:absolute;left:4px;top:5px;width:16px;height:16px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 5px var(--accent-soft);transition:background .3s,box-shadow .3s}
.tl-js .tl-dot{background:#fff;box-shadow:inset 0 0 0 2px var(--field),0 0 0 5px #fff}
.tl-js .tl-dot.lit{background:var(--accent);box-shadow:inset 0 0 0 2px var(--accent),0 0 0 5px var(--accent-soft)}
.tl-n{font-size:15px;font-weight:800;color:var(--accent)}
.tl-c h3{font-size:19px;margin:6px 0}
.tl-c p{font-size:15px;max-width:44ch}
@media(min-width:821px){
  .tl-track{left:50%;margin-left:-1px}
  .tl-list{gap:0}
  .tl-i{width:50%;padding:0 0 0 52px;margin-left:50%}
  .tl-i:nth-child(odd){margin-left:0;padding:0 52px 0 0;text-align:right}
  .tl-i:nth-child(odd) p{margin-left:auto}
  .tl-i{min-height:112px}
  .tl-i+.tl-i{margin-top:-34px}
  .tl-dot{left:-8px}
  .tl-i:nth-child(odd) .tl-dot{left:auto;right:-8px}
}

/* ===== Reduzierte Bewegung (muss nach allen Modulen stehen) ===== */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  /* Statisch explodiert, ohne Schublade; die Hervorhebung wechselt nur die Farbe. */
  .ex-scene{--t:1;--x:1}
  .ex-face,.ex-edge,.ex-tag-in{translate:none;transition:none}
  .ex-tag b,.ex-tag i,.ex-n{transition:none}
  /* .bt .bt-viz * statt .bt-viz *: Regeln wie .bt-checks i sind sonst spezifischer und liefen weiter. */
  .ex-mouse::after,.lm-cur,.qr-ring,.bt .bt-viz *,.bt .bt-viz *::before,.proj:not(.hide)>*,.plan.pop.pk-live,.scope span.is-prefilled{animation:none}
  .bt,.bt::before,.ww-q span,.ww-a,.ww-empty,.qr-w,.tl-dot,.strip a.chip{transition:none}
  .lm-js .lm-row{clip-path:none;transition:none}
}

/* ===== Druck ===== */
@media print{
  .sp,.ex-stage,.ex-hint,.bt-viz,.cc,.lm-end{display:none!important}
  .ex-grid{display:block!important}
  .ex-intro,.ex-js .ex-step,.ex-outro{min-height:0!important}
  [data-reveal]{opacity:1!important;transform:none!important}
  .lm-row{clip-path:none!important}
  .ww-ans{display:block!important}
  .ww-empty{display:none!important}
  .ww-a{visibility:visible!important;opacity:1!important;translate:none!important}
  .ww-a+.ww-a{margin-top:16px!important}
}
