/* =========================================================
   ModuloOne — Theme Tokens
   ========================================================= */
:root{
  --bg-0:#0a0a0b;
  --bg-1:#121214;
  --bg-2:#202024;
  --bg-3:#2a2a30;
  --line:#35353c;
  --text-hi:#eceef0;
  --text-lo:#8b8b95;

  --audio:#00ffcc;
  --audio-dim:#0a4a42;
  --mod:#ff0055;
  --mod-dim:#4a0a20;
  --warn:#ffb020;
  --notiz:#b8a6ff;

  --radius:10px;
  --font-ui: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", "SF Mono", Consolas, monospace;
}

*{ box-sizing:border-box; }
html{ height:100%; }
body{
  margin:0;
  min-height:100%;
  display:flex;
  flex-direction:column;
  background:var(--bg-0);
  color:var(--text-hi);
  font-family:var(--font-ui);
}

/* Scroll-Modus je Tab, gesetzt von js/tabs.js.
   app — Arbeitsfläche statt Seite: das Panel füllt den Rest unter der
         Leiste, Palette und Arbeitsfläche scrollen intern. overflow:hidden
         unterdrückt zugleich die Scrollleiste, die bei gebrochener
         Viewport-Höhe entsteht (gemessen: Viewport 428,5px, Element 429px).
   doc — native Seitenscrollung, damit Drucken, Textsuche und Sprungmarken
         funktionieren. */
body[data-mode="app"]{
  height:100%;
  overflow:hidden;
}

/* =========================================================
   Tab-Leiste und Panels
   ========================================================= */
/* Der Streifen trägt die Wortmarke und die Navigation. Beides zusammen
   in ein <nav> zu legen wäre falsch ausgezeichnet — die Marke navigiert
   nicht —, deshalb umschließt ein <header> das <nav>. */
.topstrip{
  flex:0 0 auto;
  /* Ohne sticky scrollt der Streifen im doc-Modus weg (gemessen: bei
     scrollY 400 stand er bei top -400). Im app-Modus wirkungslos. */
  position:sticky;
  top:0;
  z-index:5;
  display:flex;
  align-items:center;
  gap:22px;
  padding:0 14px;
  background:var(--bg-0);
  border-bottom:1px solid var(--line);
}
.tabbar{
  display:flex;
  gap:2px;
  /* Bei schmalen Fenstern lieber seitlich scrollen als Einträge
     unerreichbar werden lassen. */
  overflow-x:auto;
}
.tabbar__link{
  flex:0 0 auto;
  white-space:nowrap;
  padding:10px 16px;
  font-size:13px;
  letter-spacing:.3px;
  color:var(--text-lo);
  text-decoration:none;
  border-bottom:2px solid transparent;
}
.tabbar__link:hover{ color:var(--text-hi); }

/* Marke, fünf Einträge und die Sprachwahl brauchen zusammen 726px
   (gemessen). Darunter bliebe nur seitliches Scrollen in der Tab-Leiste;
   umbrechen kostet Höhe, zeigt dafür alles. Der Punkt lag bei 640px,
   solange die Leiste ohne Umschalter und mit vier Einträgen auskam. */
@media (max-width:780px){
  .topstrip{ flex-wrap:wrap; gap:6px 14px; padding:6px 10px; }
  .tabbar{ flex-wrap:wrap; overflow-x:visible; }
  .tabbar__link{ padding:6px 10px; font-size:12px; }
  /* Neben der Marke in die erste Zeile statt allein in eine dritte: die
     Tab-Leiste bekommt dafür eine eigene Zeile (flex-basis:100%), der
     Umschalter bleibt durch margin-left:auto rechts außen. */
  .tabbar{ order:1; flex-basis:100%; }
}
@media (pointer:coarse), (max-width:640px){
  .langswitch__btn{ min-height:40px; min-width:40px; }
  /* Die Einträge der Tab-Leiste waren mit 30px die kleinsten Tippziele der
     Seite — ausgerechnet die Hauptnavigation, während Sprungleiste,
     Sprachwahl und Kachelfelder längst 40px bekommen. Die Regel oben setzt
     nur die Polsterung herunter; die Mindesthöhe fehlte. flex zentriert den
     Text in der gewachsenen Fläche, ohne die Unterstreichung des aktiven
     Eintrags zu verschieben.

     36px statt der 40px der übrigen Ziele: die Leiste klebt oben und
     bricht auf Handybreite auf zwei Zeilen um, jeder Pixel zählt hier
     doppelt und dauerhaft. 36 liegt weiterhin deutlich über den 24px,
     die WCAG 2.2 AA (2.5.8) verlangt. */
  .tabbar__link{
    min-height:36px;
    display:flex;
    align-items:center;
  }
}
/* Telefonbreite: mit 10px Polsterung brauchten die fünf Einträge 399px
   und „Datenschutz“ stand allein in einer dritten Zeile (gemessen bei
   375px). Mit 4px passen sie ab etwa 340px Platz in eine Zeile; wird es
   noch enger, bricht die Leiste wie bisher um. Die Tippziele bleiben
   36px hoch und liegen ohne Lücke aneinander. */
@media (max-width:440px){
  .tabbar{ gap:0; }
  .tabbar__link{ padding:6px 4px; }
}
.tabbar__link[aria-current="page"]{
  color:var(--text-hi);
  border-bottom-color:var(--audio);
}

/* Sprachwahl rechts außen. margin-left:auto schiebt sie ans Ende der
   Zeile, ohne dass die Leiste ein Raster bräuchte. */
.langswitch{
  margin-left:auto;
  flex:0 0 auto;
  display:flex;
  gap:2px;
}
/* Auf den eigenständigen Rechtstexten sind dieselben Kästchen ein <a>
   auf die andere Fassung und ein <span> für die aktuelle: dort läuft
   kein JavaScript, das umschalten könnte. Deshalb inline-flex und
   text-decoration statt der Knopf-Vorgaben. */
.langswitch__btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding:5px 9px;
  font:inherit;
  font-size:12px;
  letter-spacing:.3px;
  color:var(--text-lo);
  background:none;
  border:1px solid var(--line);
  border-radius:4px;
  text-decoration:none;
  cursor:pointer;
}
a.langswitch__btn:hover,
button.langswitch__btn:hover{ color:var(--text-hi); }
/* aria-pressed am Knopf in index.html, aria-current am <span> der
   Rechtstexte — dieselbe Auszeichnung wäre an beiden Stellen falsch. */
.langswitch__btn[aria-pressed="true"],
.langswitch__btn[aria-current="true"]{
  color:var(--bg-0);
  background:var(--audio);
  border-color:var(--audio);
  cursor:default;
}

/* Hinweis über den englischen Rechtstexten: Übersetzung, maßgeblich
   bleibt die deutsche Fassung. */
.doc-note{
  margin:0 0 26px;
  padding:10px 14px;
  border-left:2px solid var(--warn);
  background:var(--bg-1);
  border-radius:0 var(--radius) var(--radius) 0;
  font-size:13.5px;
  color:var(--text-lo);
}

/* Höhere Spezifität nötig: .app-shell setzt display:flex und würde das
   hidden-Attribut sonst aushebeln. */
[data-tab-mode][hidden]{ display:none; }

/* Generator-Panel. flex statt height:100%, sonst müsste die Höhe der
   Leiste in einer Rechnung gepflegt werden. min-height:0 ist Pflicht:
   ohne das schrumpft das Panel nicht unter seine Inhaltshöhe und
   .canvas-wrap verliert seinen Scrollbalken. */
.app-shell{
  flex:1 1 auto;
  min-height:0;
  display:flex;
  flex-direction:column;
  overflow:hidden;
}

/* Dokument-Panel: wächst mit dem Inhalt, die Seite scrollt. */
.tabpanel--doc{
  flex:1 1 auto;
  width:100%;
  max-width:800px;
  margin:0 auto;
  padding:32px 20px 64px;
}

/* Typografie der Dokumentseiten. Gilt für den eingebetteten Tab wie für
   impressum.html und datenschutz.html — beide tragen die Klasse am <main>. */
.tabpanel--doc h1{ font-size:26px; margin:0 0 20px; }
.tabpanel--doc h2{ font-size:19px; margin:34px 0 12px; }
.tabpanel--doc h3{ font-size:16px; margin:30px 0 10px; }
.tabpanel--doc h1,
.tabpanel--doc h2,
.tabpanel--doc h3{ color:var(--text-hi); line-height:1.3; }
.tabpanel--doc p,
.tabpanel--doc ul,
.tabpanel--doc ol{ color:var(--text-lo); line-height:1.65; margin:0 0 14px; }
.tabpanel--doc ul,
.tabpanel--doc ol{ padding-left:22px; }
.tabpanel--doc li{ margin-bottom:5px; }
.tabpanel--doc strong{ color:var(--text-hi); font-weight:600; }
.tabpanel--doc a{ color:var(--audio); }
.tabpanel--doc a:hover{ text-decoration:none; }

button, select, input{ font-family:inherit; color:inherit; }

/* =========================================================
   Top bar
   ========================================================= */
.topbar{
  height:56px;
  flex:0 0 auto;   /* feste Höhe: darf in der Flex-Spalte nicht schrumpfen */
  display:flex;
  align-items:center;
  gap:24px;
  padding:0 18px;
  background:var(--bg-1);
  border-bottom:1px solid var(--line);
}

/* Wortmarke im .topstrip: sie gilt für die ganze Seite, nicht für den
   Generator allein. */
.brand__mark{
  flex:0 0 auto;
  white-space:nowrap;
  font-weight:600; letter-spacing:.5px; font-size:15px;
}
.brand__mark b{ color:var(--audio); font-weight:700; }

/* Bereichsbezeichnung des Generators, im Duktus von .palette__title, damit
   sie nicht mit der Wortmarke darüber konkurriert. */
.topbar__section{
  flex:0 0 auto;
  white-space:nowrap;
  font-size:11px;
  letter-spacing:1px;
  text-transform:uppercase;
  color:var(--text-lo);
}

/* Bei Platzmangel als Erstes verzichtbar: benennt nur, bedient nichts. */
@media (max-width:640px){
  .topbar__section{ display:none; }
  /* Transport, Verlauf und Master passen auf dem Telefon nicht in eine
     Zeile (gemessen 526 px bei 376 px Breite); ohne Umbruch lief der
     Master-Regler aus dem Bild. */
  .topbar{ height:auto; min-height:56px; flex-wrap:wrap; gap:8px 10px; padding:8px 12px; }
  /* Engere Abstände: so passt die erste Zeile auch bei 360px Breite mit
     „Audio stoppen“ und dem Download-Pfeil (gemessen 334 von 336px). */
  .topbar .topbar__transport{ gap:8px; }
  /* Zwei statt drei Zeilen (gemessen 104 statt 133px): oben Palette und
     Transport, unten Verlauf und Master, der Regler nimmt den Rest der
     Zeile. Dafür zeigt Aufnahme nur den roten Punkt, der beim Aufnehmen
     pulsiert; die Beschriftung („Aufnahme“/„Stop“) bleibt für Vorleser.
     Mit Beschriftung rutschte der Transport beim Aufnehmen samt Zeit in
     eine dritte Zeile. */
  .topbar__master{ flex:1 1 120px; min-width:0; }
  .topbar .topbar__master input[type=range]{ width:auto; flex:1 1 auto; min-width:60px; }
}
/* Ebenso im Querformat: bei 667×320 brach sonst „Audio starten“ um und
   der Master-Regler lief aus dem Bild. */
@media (max-width:640px), (max-height:500px){
  #btn-record{ position:relative; padding:0; width:40px; justify-content:center; }
  #btn-record .btn__dot{ background:var(--mod); }
  #btn-record .btn__label{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
  }
}
/* Nach einer Aufnahme: nur der Pfeil, sonst bräche der Link (143px) in
   eine dritte Zeile um, bis 760 px liefe die Leiste mit ihm über. Der
   Text bleibt als Name des Links erhalten. .topbar__transport davor:
   .btn steht weiter unten und gewönne sonst. */
@media (max-width:760px), (max-height:500px){
  .topbar__transport .btn--download{ width:40px; height:40px; padding:0; justify-content:center; font-size:0; line-height:1; }
  .topbar__transport .btn--download::before{ content:"⬇"; content:"⬇" / ""; font-size:17px; }
}

.topbar__transport{ display:flex; align-items:center; gap:10px; }

/* Zwischen Telefon und Desktop lief die Leiste über, der Master-Regler
   rutschte aus dem Bild (gemessen bis 1051 px Inhalt mit Download-Link).
   Zuerst entfällt die Bereichsbezeichnung, unter 900 px werden die
   Abstände enger und der Regler nimmt den Rest wie im Querformat. */
@media (max-width:1060px){ .topbar__section{ display:none; } }
@media (min-width:641px) and (max-width:900px){
  .topbar{ gap:12px; padding:0 12px; }
  .topbar .topbar__transport{ gap:8px; }
  .topbar__master{ flex:1 1 100px; min-width:0; }
  .topbar .topbar__master input[type=range]{ width:auto; flex:1 1 auto; min-width:60px; }
  .topbar .btn{ white-space:nowrap; }
}

/* Querformat auf dem Telefon: jede Zeile Kopf fehlt der Fläche in der
   Höhe. Die Bereichsbezeichnung entfällt wie unter 640 px, die Leiste
   wird 48 statt 56 px hoch (Knöpfe bleiben 40; unter 640 px Breite
   bricht sie weiter in zwei Zeilen um). Der Streifen bricht unter 780 px
   nicht um, die Reiter scrollen dann seitlich. */
@media (max-height:500px){
  .topbar__section{ display:none; }
  .topstrip{ flex-wrap:nowrap; padding-top:0; padding-bottom:0; }
  .tabbar{ order:0; flex-basis:auto; flex-wrap:nowrap; overflow-x:auto; }
}
@media (max-height:500px) and (min-width:641px){
  .topbar{ height:48px; gap:12px; padding:0 12px; }
  .topbar .topbar__transport{ gap:8px; }
  .topbar__master{ flex:1 1 100px; min-width:0; }
  .topbar .topbar__master input[type=range]{ width:auto; flex:1 1 auto; min-width:60px; }
  .topbar .btn{ white-space:nowrap; }
}

.btn{
  display:inline-flex; align-items:center; gap:8px;
  padding:8px 14px;
  border-radius:999px;
  border:1px solid var(--line);
  background:var(--bg-2);
  cursor:pointer;
  font-size:13px;
  font-weight:500;
  text-decoration:none;
  transition:border-color .15s ease, background .15s ease;
}
.btn:hover{ border-color:#4a4a52; }
.btn:disabled{ opacity:.4; cursor:not-allowed; }

.btn__dot{
  width:8px; height:8px; border-radius:50%;
  background:var(--text-lo);
  transition:background .15s ease, box-shadow .15s ease;
}

.btn--power[data-active="true"] .btn__dot{
  background:var(--audio);
  box-shadow:0 0 8px var(--audio);
}
.btn--power[data-active="true"]{ border-color:var(--audio-dim); }

.btn--record[data-active="true"] .btn__dot{
  background:var(--mod);
  box-shadow:0 0 8px var(--mod);
  animation:pulse 1s infinite;
}
.btn--record[data-active="true"]{ border-color:var(--mod-dim); }

@keyframes pulse{
  0%,100%{ opacity:1; } 50%{ opacity:.35; }
}

.btn--download{ background:var(--audio-dim); border-color:var(--audio); color:var(--audio); }

/* Rückgängig/Wiederholen: nur ein Zeichen, deshalb rund statt Pille. */
.topbar__history{ display:flex; align-items:center; gap:6px; }
.btn--icon{
  justify-content:center;
  width:34px; height:34px; padding:0;
  font-size:17px; line-height:1;
}
/* Etwas abgesetzt, damit er nicht für einen dritten Verlaufsschritt gilt. */
.btn--clear{ margin-left:6px; font-size:14px; }
.btn--kabel[aria-pressed="true"]{ border-color:var(--audio); color:var(--audio); background:var(--audio-dim); }
.btn--kuerzel{ flex:0 0 auto; font-size:15px; font-weight:600; }
/* Auf reinen Touch-Geräten ohne Nutzen; unter 860 px liefe die Leiste mit
   ihm über (gemessen 842 px Inhalt), „?“ wirkt trotzdem. */
@media (hover:none) and (pointer:coarse), (max-width:860px){ .btn--kuerzel{ display:none; } }

.record-time{
  font-family:var(--font-mono);
  font-size:12px;
  color:var(--mod);
  min-width:42px;
}

.topbar__master{
  margin-left:auto;
  display:flex; align-items:center; gap:10px;
  font-size:12px; color:var(--text-lo);
}
.topbar__master input[type=range]{ width:120px; }

/* =========================================================
   Layout
   ========================================================= */
/* flex:1 statt height:calc(100% - 56px) — die Topbar-Höhe ist damit nur noch
   an einer Stelle gepflegt statt zusätzlich in einer Rechnung. min-height:0
   ist in einer Flex-Spalte Pflicht: ohne das weigert sich der Bereich, unter
   seine Inhaltshöhe zu schrumpfen, und .canvas-wrap bekäme keinen eigenen
   Scrollbalken mehr. */
.workspace{ display:flex; flex:1 1 auto; min-height:0; position:relative; }

/* =========================================================
   Sidebar palette
   ========================================================= */
.palette{
  width:210px;
  flex-shrink:0;
  background:var(--bg-1);
  border-right:1px solid var(--line);
  padding:16px 12px;
  overflow-y:auto;
}
/* Schublade (Punkt 9c): auf dem Telefon ließ die feste Palette der Fläche
   nur 166 von 375 px. Unter 640 px liegt sie deshalb über der Fläche und
   kommt erst auf den Knopf in der Kopfleiste herein; ein gewähltes Modul
   oder Preset schließt sie wieder. visibility statt nur transform: zu
   ist sie auch für Tab und Vorleser nicht da.
   Ebenso unter 500 px Höhe, also Telefone im Querformat: bei 915×340
   (OnePlus 8 im Chrome) blieben der Fläche sonst 705×245 px. */
.btn--palette{ display:none; }
@media (max-width:640px), (max-height:500px){
  .btn--palette{ display:inline-flex; min-height:40px; order:-1; }
  .palette{
    position:absolute; z-index:6; top:0; bottom:0; left:0;
    width:min(300px, 85vw);
    box-shadow:8px 0 24px rgba(0,0,0,.5);
    transform:translateX(-100%);
    visibility:hidden;
    transition:transform .2s ease, visibility .2s;
  }
  .workspace[data-palette="offen"] .palette{ transform:none; visibility:visible; }
}
@media (max-width:640px) and (prefers-reduced-motion:reduce),
       (max-height:500px) and (prefers-reduced-motion:reduce){
  .palette{ transition:none; }
}

/* Übersicht (Mobil-Idee 2, Stufe A): auf dem Telefon, bei großen Presets
   auch am Desktop, sieht man vom Patch nur einen Ausschnitt. Der Knopf
   verkleinert die ganze belegte Fläche auf den Bildschirm; bedienen lässt
   sie sich dabei nicht (inert), ein Klick springt in Originalgröße an die
   Stelle. Der Rahmen zeigt, wo man war, seine Linie bleibt trotz
   Verkleinerung 2px breit. --balken-r/-u setzt UIManager auf die Breite
   der Scrollbalken, damit der Knopf neben ihnen sitzt, --flaeche-l auf
   den Anfang der Fläche: am Desktop steht links die Palette. */
.btn--uebersicht{
  position:absolute; z-index:5;
  right:calc(12px + var(--balken-r, 0px)); bottom:calc(12px + var(--balken-u, 0px));
  min-height:40px; box-shadow:0 2px 10px rgba(0,0,0,.5);
}
.btn--uebersicht[hidden]{ display:none; }
.btn--uebersicht[aria-pressed="true"]{ border-color:var(--audio); color:var(--audio); }
.uebersicht-hinweis{
  position:absolute; z-index:5; left:calc(12px + var(--flaeche-l, 0px)); right:12px; top:8px; margin:0;
  padding:6px 10px; border-radius:6px; text-align:center; pointer-events:none;
  font-size:12px; color:var(--text-hi); background:rgba(0,0,0,.6);
}
.uebersicht-hinweis[hidden]{ display:none; }
.canvas-wrap[data-uebersicht]{ overflow:hidden; cursor:zoom-in; }
.canvas-wrap[data-uebersicht] .canvas{
  transform:translate(16px, 40px) scale(var(--uebersicht-mass));
  transform-origin:0 0;
}
.uebersicht-rahmen{
  position:absolute; pointer-events:none;
  border:calc(2px / var(--uebersicht-mass)) solid var(--audio);
  border-radius:calc(4px / var(--uebersicht-mass));
}

/* Einstieg: Wer die Seite nicht kannte, sah eine leere Fläche, Main Out
   lag außerhalb des Ausschnitts und die Bibliothek tief in der
   Seitenleiste. Solange nur Main Out da ist, liegt deshalb mittig über der
   Fläche eine Karte mit vier Beispielen. Die Fläche darum bleibt
   bedienbar (pointer-events), beim Ziehen eines Moduls auch die Karte
   selbst, damit es sich auch dort ablegen lässt. Den Knopf „Übersicht“
   blendet UIManager._bindStart so lange aus, auf der leeren Fläche gibt
   es nichts zu überblicken. */
.start{
  position:absolute; z-index:4; top:0;
  left:var(--flaeche-l, 0px); right:var(--balken-r, 0px); bottom:var(--balken-u, 0px);
  display:flex; align-items:center; justify-content:center;
  padding:16px; pointer-events:none;
}
.start[hidden]{ display:none; }
.start__karte{
  pointer-events:auto; width:min(500px, 100%); max-height:100%; overflow-y:auto;
  padding:20px; background:var(--bg-1); border:1px solid var(--line);
  border-radius:12px; box-shadow:0 8px 32px rgba(0,0,0,.5);
}
.start[data-ziehen] .start__karte{ pointer-events:none; }
.start__titel{ margin:0 0 6px; font-size:18px; color:var(--text-hi); }
.start__text{ margin:0 0 14px; font-size:13px; line-height:1.5; color:var(--text-lo); }
/* Steht nur ohne JavaScript da (<noscript> in index.html). */
.start__ohne-js{ font-size:13px; line-height:1.5; color:var(--text-lo); }
.start__ohne-js p{ margin:8px 0 0; }
.start__ohne-js b{ color:var(--text-hi); }
.start__ohne-js a{ color:var(--audio); }
.start__patches{ display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:8px; }
.start__patch{
  display:flex; flex-direction:column; align-items:flex-start; gap:2px;
  padding:10px 12px; background:var(--bg-2); border:1px solid var(--line);
  border-radius:var(--radius); color:var(--text-hi); font:inherit; text-align:left; cursor:pointer;
}
.start__patch:hover, .start__patch:focus-visible{ border-color:var(--audio); background:var(--bg-3); }
.start__gruppe{ font-size:10.5px; text-transform:uppercase; letter-spacing:.8px; color:var(--text-lo); }
.start__name{ font-size:14px; font-weight:600; }
.start__tun{ margin-top:auto; padding-top:4px; font-size:12px; color:var(--audio); }
.start__weiter{ margin:14px 0 0; font-size:12px; line-height:1.7; color:var(--text-lo); }
.start__link{
  padding:0; background:none; border:none; font:inherit;
  color:var(--audio); text-decoration:underline; cursor:pointer;
}
/* Arbeitsstand vom letzten Besuch: über den Beispielen, Verwerfen daneben. */
.start__stand{ display:flex; align-items:center; gap:14px; margin:8px 0 14px; font-size:12px; }
.start__stand[hidden]{ display:none; }
.start__patch--stand{ flex:1; min-width:0; }
.start__patch--stand .start__name{ max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Grundkurs: eine Zeile über die ganze Breite unter den Beispielen; wird
   es zu eng, rutscht „Lektion 1“ in die nächste Zeile. */
.start__patch--kurs{
  grid-column:1 / -1; flex-direction:row; flex-wrap:wrap; align-items:baseline; column-gap:10px;
}
.start__patch--kurs .start__tun{ margin:0 0 0 auto; padding:0; }
@media (max-width:640px){
  .start{ padding:12px; }
  .start__karte{ padding:14px; }
  .start__titel{ font-size:16px; }
  .start__text{ margin-bottom:10px; }
  .start__patch{ padding:8px 10px; }
  /* Mit gesichertem Arbeitsstand ohne Grundkurs: Wer etwas
     wiederherstellen kann, ist selten neu, und bei 375×667 musste man
     sonst in der Karte scrollen (505 px Inhalt auf 442 px). */
  .start__stand:not([hidden]) ~ .start__patches .start__patch--kurs{ display:none; }
}
/* Unter 420 px nur das Dreieck: „Lektion 1“ brach sonst bei 360 px in eine
   zweite Zeile, und die Karte musste scrollen. */
@media (max-width:420px){
  .start__patch--kurs .start__tun [data-i18n]{ display:none; }
}
/* Querformat: die vier Beispiele nebeneinander und beide Hinweise in
   einer Zeile, sonst musste man bei 915×340 in der Karte scrollen (343 px
   Inhalt auf 206 px). Der Punkt steht als inline-block, damit ihn die
   Unterstreichung des Links nicht erfasst. */
@media (max-height:500px){
  .start{ padding:8px 12px; }
  .start__karte{ width:min(780px, 100%); padding:14px; }
  .start__titel{ font-size:16px; }
  .start__text{ margin-bottom:10px; }
  /* Fest vier Spalten: Mit auto-fit legte die durchgehende Grundkurs-Zeile
     sechs Spuren an, die Kacheln wurden schmal und höher. */
  .start__patches{ grid-template-columns:repeat(4, minmax(0, 1fr)); }
  .start__patch{ padding:8px 10px; }
  /* Der Grundkurs flach wie der Arbeitsstand, und nur ohne ihn: Als
     fünfte Kachel rutschte er bei 667×375 in eine zweite Reihe, beide
     Zeilen zusammen ließen die Karte bei 915×340 scrollen. */
  .start__patch--kurs{ padding:3px 10px; }
  .start__stand:not([hidden]) ~ .start__patches .start__patch--kurs{ display:none; }
  /* Der Arbeitsstand in einer Zeile: dreizeilig musste man bei 915×340
     wieder scrollen (272 px Inhalt auf 221 px), so passt es genau. */
  .start__stand{ margin:-2px 0 6px; }
  .start__patch--stand{ flex-direction:row; align-items:baseline; column-gap:10px; padding:4px 10px; }
  .start__patch--stand .start__gruppe{ flex:none; }
  .start__patch--stand .start__name{ min-width:0; }
  .start__patch--stand .start__tun{ flex:none; margin:0 0 0 auto; padding:0; }
  .start__stand .start__link::after{ content:none; }
  .start__weiter{ margin-top:8px; line-height:1.5; }
  .start__weiter br{ display:none; }
  .start__link::after{ content:"·"; display:inline-block; margin-left:6px; color:var(--text-lo); }
}

/* Notiz-Blatt (Mobil-Idee 3): Die Hauptnotizen der Bibliothek sind meist
   600px breit, auf dem Telefon las man sie zeilenweise seitlich scrollend,
   und ein Tipp hinein holte die Bildschirmtastatur. Nach dem Laden kommt
   die Notiz deshalb als Blatt von unten, nur zum Lesen; eingeklappt bleibt
   links unten die Lasche, gegenüber dem Knopf „Übersicht“. visibility wie
   bei der Schublade: zu ist das Blatt auch für Tab und Vorleser nicht da.
   Höhe 60% der Fläche, aber mindestens 300px: Bei 320×568 ist die Fläche
   nur 305px hoch, dem Text blieben sonst drei Zeilen. Über 640px passt die Notiz meist auf den Bildschirm, dort gibt es beides
   nicht. */
.blatt, .btn--blatt{ display:none; }
@media (max-width:640px){
  .blatt{
    position:absolute; z-index:5; left:0; right:0; bottom:0;
    display:flex; flex-direction:column; max-height:max(60%, min(100%, 300px));
    background:var(--bg-1); border-top:2px solid var(--notiz);
    border-radius:10px 10px 0 0; box-shadow:0 -8px 24px rgba(0,0,0,.5);
    transform:translateY(100%); visibility:hidden;
    transition:transform .2s ease, visibility .2s;
  }
  .blatt[data-offen]{ transform:none; visibility:visible; }
  .blatt:focus{ outline:none; }
  .blatt__kopf{ display:flex; align-items:center; gap:8px; padding:6px 6px 6px 16px; border-bottom:1px solid var(--line); }
  .blatt__titel{
    flex:1; min-width:0; margin:0; font-size:15px; color:var(--text-hi);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .blatt__kopf .btn{ flex:0 0 auto; min-height:40px; padding:8px 12px; }
  .blatt__zu{
    flex:0 0 auto; width:40px; height:40px; border:none; border-radius:6px;
    background:none; color:var(--text-lo); font-size:20px; cursor:pointer;
  }
  .blatt__zu:hover, .blatt__zu:focus-visible{ color:var(--notiz); }
  .blatt__text{
    flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain;
    padding:12px 16px; white-space:pre-wrap; overflow-wrap:anywhere;
    font-size:14px; line-height:1.5; color:var(--text-hi);
  }
  .btn--blatt{
    display:inline-flex; position:absolute; z-index:5;
    left:12px; bottom:calc(12px + var(--balken-u, 0px));
    min-height:40px; box-shadow:0 2px 10px rgba(0,0,0,.5);
  }
  .btn--blatt[hidden]{ display:none; }
  .blatt__weiter{ flex:none; align-self:flex-start; margin:0 16px 12px; min-height:40px; border-color:var(--notiz); }
  .blatt__weiter[hidden]{ display:none; }
}
@media (max-width:640px) and (prefers-reduced-motion:reduce){
  .blatt{ transition:none; }
}

.palette__title{ font-size:12px; text-transform:uppercase; letter-spacing:1px; color:var(--text-lo); margin:0 0 4px; }
.palette__hint{ font-size:11px; color:var(--text-lo); margin:0 0 14px; }

.palette__item{
  display:flex; align-items:center; gap:10px;
  width:100%;
  padding:9px 10px;
  margin-bottom:8px;
  background:var(--bg-2);
  border:1px solid var(--line);
  border-radius:var(--radius);
  cursor:grab;
  text-align:left;
}
.palette__item:hover{ border-color:#4a4a52; background:var(--bg-3); }
.palette__item:active{ cursor:grabbing; }

.palette__item-icon{
  width:28px; height:28px; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border-radius:7px;
  font-size:15px;
}
.palette__item-icon--audio{ background:var(--audio-dim); color:var(--audio); }
.palette__item-icon--mod{ background:var(--mod-dim); color:var(--mod); }
.palette__item-icon--scope{ background:var(--bg-0); color:var(--text-hi); }
.palette__item-icon--notiz{ background:var(--bg-0); color:var(--notiz); }

.palette__item-text{ display:flex; flex-direction:column; line-height:1.3; }
.palette__item-text b{ font-size:12.5px; font-weight:600; }
.palette__item-text small{ font-size:10.5px; color:var(--text-lo); }

.palette__divider{ height:1px; background:var(--line); margin:14px 0; }

.palette__legend{
  display:flex; flex-direction:column; gap:6px;
  font-size:11px; color:var(--text-lo);
  margin:0 0 12px;
}
.palette__legend span{ display:flex; align-items:center; gap:7px; }
.legend__dot{
  width:9px; height:9px; border-radius:50%; flex-shrink:0;
  display:inline-block;
}
.legend__dot--audio{ background:var(--audio); box-shadow:0 0 5px rgba(0,255,204,.6); }
.legend__dot--mod{ background:var(--mod); box-shadow:0 0 5px rgba(255,0,85,.6); }
.legend__dot--logic{ background:var(--warn); box-shadow:0 0 5px rgba(255,176,32,.6); }

.palette__footnote{ font-size:10.5px; color:var(--text-lo); line-height:1.5; }
.palette__footnote a{ color:var(--audio); }

.palette__presets{ display:flex; flex-direction:column; gap:6px; }
.palette__presets select{
  width:100%;
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  padding:6px; font-size:12px; color:var(--text-hi);
  cursor:pointer;
}
/* Eigene Patches ohne Einträge: die Liste steht da, ist aber leer. */
.palette__presets select:disabled{ opacity:.5; cursor:default; }
.palette__presets-hint{ font-size:10px; color:var(--text-lo); line-height:1.4; }
/* Suchfeld und Würfel über der Bibliothek, so hoch wie die Auswahl darunter. */
.palette__suche{ display:flex; gap:6px; align-items:center; }
.palette__suche input{
  flex:1 1 auto; min-width:0;
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  padding:6px; font-size:12px; color:var(--text-hi);
}
.palette__suche input::placeholder{ color:var(--text-lo); }
.palette__suche .btn--icon{ flex:0 0 auto; width:30px; height:30px; font-size:16px; }

.palette__preset-actions{ display:flex; gap:6px; }
/* Die Preset-Knöpfe und der Transport in der Kopfzeile blieben mit 28 bzw.
   34px ebenfalls unter dem Maß, das der Rest der Seite auf Touch einhält.
   Die Auswahl darüber braucht zusätzlich 16px Schrift, sonst zoomt iOS
   beim Antippen in die Seite hinein. */
@media (pointer:coarse), (max-width:640px){
  .preset-btn{ min-height:40px; font-size:12px; }
  .palette__presets select{ min-height:40px; font-size:16px; }
  .palette__suche input{ min-height:40px; font-size:16px; }
  .palette__suche .btn--icon{ width:40px; height:40px; }
  .topbar__transport .btn{ min-height:40px; }
  .btn--icon{ width:40px; height:40px; }
}
.preset-btn{
  /* Bezugsrahmen für das versteckte Datei-Feld im Import-Knopf: ohne ihn
     hängt es am Dokument, verlängert es im app-Modus auf ~1200px und
     scrollt beim Tab-Fokus die ganze Ansicht weg. */
  position:relative;
  flex:1;
  display:flex; align-items:center; justify-content:center; gap:4px;
  padding:6px 4px;
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  color:var(--text-hi);
  font-size:11px; font-family:inherit;
  line-height:1.3;
  cursor:pointer;
  text-align:center;
}
.preset-btn:hover{ border-color:#4a4a52; background:var(--bg-3); }
.preset-btn:disabled{ opacity:.4; cursor:not-allowed; }
.preset-btn:disabled:hover{ border-color:var(--line); background:var(--bg-2); }
.preset-btn input[type=file]{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}
/* Nur für Vorlesesoftware, etwa die Note am Sequenzer (UIManager._ansagen). */
.nur-vorlesen{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}

/* =========================================================
   Canvas
   ========================================================= */
.canvas-wrap{
  flex:1;
  position:relative;
  overflow:auto;
  background:
    linear-gradient(var(--bg-1) 1px, transparent 1px) 0 0 / 28px 28px,
    linear-gradient(90deg, var(--bg-1) 1px, transparent 1px) 0 0 / 28px 28px,
    var(--bg-0);
}
.canvas{
  position:relative;
  width:2200px;
  height:2400px;
}

.cable-layer{
  position:absolute;
  top:0; left:0;
  width:100%; height:100%;
  pointer-events:none; /* individual cables re-enable pointer events */
  overflow:visible;
}
.cable-layer path.cable{
  fill:none;
  stroke-width:2.5;
  pointer-events:none; /* angeklickt wird .cable-hit davor */
}
/* Unsichtbare Trefferfläche je Kabel: breiter als der Strich und ohne
   Lücken, auch bei gestrichelten Kabeln. */
.cable-layer path.cable-hit{
  fill:none;
  stroke:transparent;
  stroke-width:14;
  pointer-events:stroke;
  cursor:pointer;
}
.cable-layer path.cable--audio{ stroke:var(--audio); filter:drop-shadow(0 0 3px rgba(0,255,204,.5)); }
.cable-layer path.cable--mod{ stroke:var(--mod); filter:drop-shadow(0 0 3px rgba(255,0,85,.5)); }
.cable-layer path.cable--logic{ stroke:var(--warn); stroke-dasharray:4 4; }
/* Gate offen (js/gatelicht.js): durchgezogen und leuchtend statt gestrichelt. */
.cable-layer path.cable--gate-an{ stroke-dasharray:none; filter:drop-shadow(0 0 4px rgba(255,176,32,.9)); }
.cable-layer path.cable--pending{
  stroke:var(--text-hi); stroke-dasharray:3 3; opacity:.8;
  pointer-events:none; /* Vorschau-Kabel darf beim Loslassen nicht selbst getroffen werden (elementFromPoint) */
}
.cable-layer path.cable--invalid{ stroke:#ff3b3b !important; }
.cable-layer path.cable-hit:hover + path.cable{ stroke-width:4; }

/* Hervorheben: Die Module liegen über den Kabeln. Beim Überfahren eines
   Kabels, Ports oder Modulkopfs kommt eine Kopie der betroffenen Kabel in
   die Ebene darüber; der Schalter neben ✕ hebt die ganze Kabelebene an.
   Beide lassen Klicks durch, damit die Regler darunter bedienbar bleiben. */
.cable-layer--vorn{ z-index:2; }
.cable-layer path.cable--vorn{ stroke-width:3.5; }
/* Per Tipp gewählt: Der nächste Tipp auf das Kabel löscht es. */
.cable-layer path.cable--gewaehlt{ stroke-width:5; filter:drop-shadow(0 0 5px var(--text-hi)); }
.canvas[data-kabel-vorn] #cable-layer{ z-index:2; }
.canvas[data-kabel-vorn] #cable-layer path.cable-hit{ pointer-events:none; }

/* =========================================================
   Module cards
   ========================================================= */
.module{
  position:absolute;
  width:220px;
  background:var(--bg-1);
  border:1px solid var(--line);
  border-radius:var(--radius);
  box-shadow:0 8px 24px rgba(0,0,0,.4);
  user-select:none;
  /* Eigener Stapelkontext: z-index im Modul (schwarze Tasten) bleibt im
     Modul, sonst lägen sie über einem Modul, das darüber liegt. */
  isolation:isolate;
}
.module--audio{ border-top:2px solid var(--audio); }
/* Aus einer Werkzeug-Kachel gezeigt: dreimal kurz aufleuchten */
.module--gezeigt, .palette__item--gezeigt{ animation:gezeigt .8s ease-in-out 3; }
@keyframes gezeigt{ 50%{ box-shadow:0 0 0 3px var(--text-hi), 0 0 18px rgba(255,255,255,.35); } }
@media (prefers-reduced-motion:reduce){
  .module--gezeigt, .palette__item--gezeigt{ animation:none; box-shadow:0 0 0 3px var(--text-hi); }
}
.module--mod{ border-top:2px solid var(--mod); }
/* Amber statt Pink, damit der Kartenrahmen zur eigenen Legende
   "Takt & Noten (Sequenzer)" passt (die schon amber ist). */
.module--seq{ border-top:2px solid var(--warn); width:260px; }
/* Takt-Modul: Lampe neben dem Titel, an auf jeder Viertel
   (UIManager._highlightStep). */
.takt-kopf{ display:flex; align-items:center; gap:8px; }
.takt-lampe{
  width:8px; height:8px; border-radius:50%;
  background:var(--bg-2); border:1px solid var(--line);
}
.takt-lampe[data-an]{ background:var(--warn); border-color:var(--warn); box-shadow:0 0 6px var(--warn); }
.module--out{ border-top:2px solid var(--text-hi); }
.module--scope{ border-top:2px solid var(--text-lo); }
.module--util{ border-top:2px solid var(--text-lo); }

/* Notiz: Text statt Gerät, ohne Ports. Das Textfeld wächst mit
   (UIManager._notizHoehe); eingeklappt bleibt die Kopfzeile mit der
   Überschrift oder der ersten Zeile. */
.module--notiz{ width:260px; border-top:2px solid var(--notiz); }
.module--notiz .module__body{ padding:8px 8px 10px; gap:2px; }
.module--notiz .module__title{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.notiz__kurz{ display:none; }
.module--zu .notiz__name{ display:none; }
.module--zu .notiz__kurz{ display:block; }
.module--zu .module__body{ display:none; }
.module--zu .module__head{ border-bottom:none; }
/* Breite: Griff am rechten Rand (UIManager._bindNotizBreite), die Breite
   selbst setzt _notizZeigen aus dem Parameter "breite". */
.notiz__griff{
  position:absolute; top:0; right:-5px; width:10px; height:100%;
  cursor:ew-resize; touch-action:none;
}
.notiz__griff::after{
  content:""; position:absolute; top:8px; bottom:8px; left:4px; width:2px;
  border-radius:1px; background:var(--notiz); opacity:0;
}
.notiz__griff:hover::after, .notiz__griff:focus-visible::after,
.module--notiz[data-zieht] .notiz__griff::after{ opacity:.8; }
.notiz__griff:focus-visible{ outline:none; }
@media (hover:none){ .notiz__griff{ right:-10px; width:20px; } .notiz__griff::after{ left:9px; opacity:.4; } }
.notiz__knoepfe{ display:flex; align-items:center; gap:2px; flex:none; }
.notiz__klapp{
  background:none; border:none; color:var(--text-lo);
  cursor:pointer; font-size:13px; line-height:1;
  padding:3px 5px;
}
.notiz__klapp:hover{ color:var(--notiz); }
.notiz__titel, .notiz__text{
  width:100%;
  background:none; border:1px solid transparent; border-radius:6px;
  padding:4px 6px;
  color:var(--text-hi); font-family:var(--font-ui);
  user-select:text;
}
.notiz__titel{ font-size:13px; font-weight:600; }
.notiz__text{ font-size:12px; line-height:1.5; resize:none; overflow:hidden; min-height:3em; }
.notiz__titel::placeholder, .notiz__text::placeholder{ color:var(--text-lo); opacity:.7; }
.notiz__titel:hover, .notiz__text:hover{ border-color:var(--line); }
.notiz__titel:focus, .notiz__text:focus{ outline:none; border-color:var(--notiz); background:var(--bg-0); }
/* Knopf zum nächsten Eintrag (Parameter "weiter"), links unter dem Text. */
.notiz__weiter{
  align-self:flex-start; margin:6px 6px 0; padding:6px 12px;
  background:var(--bg-2); border:1px solid var(--notiz); border-radius:var(--radius);
  color:var(--text-hi); font:inherit; font-size:12px; font-weight:600; cursor:pointer;
}
.notiz__weiter:hover, .notiz__weiter:focus-visible{ background:var(--bg-3); }

.module__head{
  display:flex; align-items:center; justify-content:space-between;
  padding:9px 12px;
  cursor:grab;
  border-bottom:1px solid var(--line);
  /* Touch-Drag der Kopfzeile darf nicht als Seiten-/Canvas-Scroll
     interpretiert werden — sonst übernimmt der Browser die Geste, statt
     sie an unseren Pointer-Handler durchzureichen. */
  touch-action:none;
}
.module__head:active{ cursor:grabbing; }
.module__head:focus-visible{ outline:2px solid var(--audio); outline-offset:-2px; border-radius:inherit; }
.module__title{ font-size:12.5px; font-weight:600; letter-spacing:.2px; }
.module__close{
  background:none; border:none; color:var(--text-lo);
  cursor:pointer; font-size:13px; line-height:1;
  padding:3px;
}
.module__close:hover{ color:var(--mod); }
/* Eigener Modulname (UIManager._labelEinbauen): Etikett gleich hinter dem
   Typ; passt es dort nicht hin, bricht es mit dem Menüknopf unter den
   Titel um. Schließen steht dafür fest oben rechts. Der Menüknopf „⋯“
   erscheint beim Überfahren. */
.module__head--name{
  position:relative;
  flex-wrap:wrap; justify-content:flex-start; gap:5px 6px;
  padding-right:31px;
}
.module__head--name .module__title{ white-space:nowrap; }
.module__head--name > .module__close{ position:absolute; top:7px; right:9px; }
.module__name{ display:inline-flex; align-items:center; gap:2px; }
.module__label{
  padding:1px 5px;
  border:1px solid var(--line); border-radius:4px;
  font-size:11px; font-weight:600; color:var(--text-hi);
  white-space:nowrap;
}
.module__label:empty{ display:none; }
.module__label-feld{
  display:none;
  width:9ch; padding:1px 5px;
  font:600 11px var(--font-ui); color:var(--text-hi);
  background:var(--bg-0); border:1px solid var(--text-lo); border-radius:4px;
}
.module__label-feld:focus{ outline:none; border-color:var(--text-hi); }
.module--benennt .module__label{ display:none; }
.module--benennt .module__label-feld{ display:inline-block; }
.module__menue{
  padding:3px 2px;
  background:none; border:none; color:var(--text-lo);
  cursor:pointer; font-size:13px; line-height:1; /* so hoch wie Schließen: der Kopf behält seine Höhe */
  opacity:0; transition:opacity .15s;
}
.module:hover .module__menue, .module__menue:focus-visible,
.module__head:focus-visible .module__menue, .module__menue[aria-expanded="true"]{ opacity:1; }
.module__menue:hover, .module__menue[aria-expanded="true"]{ color:var(--text-hi); }
/* Main Out: Menü an der Stelle, an der sonst Schließen steht, absolut,
   damit der Kopf so hoch bleibt wie ohne Knopf. */
.module--out .module__head{ position:relative; }
.module--out .module__menue{ position:absolute; top:7px; right:9px; }
@media (hover:none){ .module__menue{ opacity:.6; } }
/* Touch: Löschen wandert ins Menü, der Knopf nimmt den Platz von
   Schließen ein und bekommt eine Fläche so hoch wie der Kopf. Das Glyph
   steht dabei genau dort, wo das Kreuz stand (7 + 3 px von oben, 9 + 3 px
   von rechts). */
@media (hover:none) and (pointer:coarse){
  .module__close{ display:none; }
  .module__menue{ opacity:1; }
  .module__head--name .module__menue, .module--out .module__menue{ position:absolute; top:-2px; right:0; padding:12px; }
  /* Platzhalter ohne Breite in der Höhe des Knopfs: der Kopf bleibt so hoch wie mit Knopf in der Zeile. */
  .module__head--name .module__name::after{
    content:"⋯"; width:0; overflow:hidden; visibility:hidden;
    padding:3px 0; font-size:13px; line-height:1;
  }
}
.modul-menue{
  position:fixed; z-index:60;
  display:flex; flex-direction:column; min-width:150px;
  padding:4px;
  background:var(--bg-1);
  border:1px solid var(--line); border-radius:8px;
  box-shadow:0 10px 28px rgba(0,0,0,.45);
}
.modul-menue__eintrag{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:7px 10px;
  background:none; border:none; border-radius:5px;
  color:var(--text-hi); font:500 12.5px var(--font-ui); text-align:left;
  cursor:pointer;
}
.modul-menue__eintrag:hover, .modul-menue__eintrag:focus-visible{ background:var(--bg-3); outline:none; }
.modul-menue__kuerzel{ color:var(--text-lo); font-size:11px; }
@media (hover:none) and (pointer:coarse){
  .modul-menue__eintrag{ padding:11px 12px; font-size:14px; }
  .modul-menue__kuerzel{ display:none; }
}

.module__body{ padding:12px; display:flex; flex-direction:column; gap:10px; }

.field{ display:flex; flex-direction:column; gap:5px; font-size:11px; color:var(--text-lo); }
.field b{ color:var(--text-hi); font-weight:600; font-family:var(--font-mono); }
/* Zahl neben dem Regler: anklicken und eintippen (ui-manager.js, _wertEintippen) */
.readout--tippbar{ cursor:text; text-decoration:underline dotted transparent; text-underline-offset:2px; }
.readout--tippbar:hover{ text-decoration-color:var(--text-lo); }
@media (hover:none){ .readout--tippbar{ text-decoration-color:var(--line); } }
.readout-feld{
  width:8ch; padding:0 4px; margin:-1px 0;
  font:600 11px var(--font-mono); color:var(--text-hi);
  background:var(--bg-0); border:1px solid var(--text-lo); border-radius:4px;
}
.readout-feld:focus{ outline:none; border-color:var(--text-hi); }
.field select{
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  padding:5px 6px; font-size:12px; color:var(--text-hi);
}
.field-row{ display:flex; gap:6px; }
.field-row select{ flex:1; min-width:0; }
/* Zwei kurze Felder nebeneinander (Oscillator: Wellenform + FM-Tiefe), damit
   ein neuer Regler das Modul nicht höher macht — die Bibliothek setzt Module
   auf feste Abstände untereinander. */
.field-pair{ display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1fr); gap:8px; }
.field-pair select{ min-width:0; }
.field-pair input[type=range]{ margin-block:auto; } /* mittig neben der Auswahl */
/* Zwei Regler nebeneinander (Oscillator: Frequenz | FM-Tiefe) behalten den
   Rand des einzelnen Reglers, so bleibt der Oscillator genau so hoch wie
   mit Frequenz in eigener Zeile. Die Paare im Mixer sind ohne ihn gebaut. */
.field-pair--regler input[type=range]{ margin-block:2px; }
.field input[type=range]{ width:100%; accent-color:var(--audio); }
.module--mod input[type=range]{ accent-color:var(--mod); }
/* BPM-Regler eines Sequenzers, den SYNC taktet (UIManager._taktAnzeigen). */
.field input[type=range]:disabled{ opacity:.35; cursor:not-allowed; }
.field:has(input[type=range]:disabled) .readout--tippbar{ cursor:default; text-decoration:none; }
/* Modulation am Regler (js/modmarken.js): liegt über dem Regelweg, nimmt
   keinen Platz und keine Maus. Strich = Wert jetzt, Band darunter = Bereich;
   auf der Spur sähe das Band aus wie deren ungefüllter Teil. */
.field--marke{ position:relative; }
.mod-marke{ position:absolute; pointer-events:none; }
.mod-marke[hidden]{ display:none; }
.mod-marke__bereich{ position:absolute; bottom:-3px; height:3px; border-radius:2px; background:var(--mod); }
.mod-marke__strich{ position:absolute; left:0; top:15%; bottom:15%; width:2px; margin-left:-1px; border-radius:1px; background:var(--text-hi); box-shadow:0 0 0 1px var(--mod); }

.module__ports{
  display:flex; justify-content:space-between;
  padding:8px 10px 10px;
  border-top:1px solid var(--line);
  gap:6px;
}
.port{
  display:flex; align-items:center; gap:5px;
  /* 10,5 statt 9 px. Die Zeilenhöhe bleibt die von 9 px (Inter: 12,3 px),
     sonst würde jedes Modul 1–2 px höher und die Bibliothek setzt Module
     auf feste Abstände. In der Breite bleiben je Modul noch 70 px frei. */
  font-size:10.5px; line-height:1.17; letter-spacing:.3px; text-transform:uppercase;
  color:var(--text-lo);
  cursor:crosshair;
  padding:3px 2px;
  white-space:nowrap;
  /* Kabel von einem Port ziehen darf nicht als Scroll interpretiert werden. */
  touch-action:none;
}
.port__dot{
  width:10px; height:10px; border-radius:50%;
  border:2px solid var(--text-lo);
  background:var(--bg-2);
  transition:transform .1s ease, background .1s ease, border-color .1s ease;
}
.port--in .port__dot{ border-color:var(--audio); }
.port--out .port__dot{ border-color:var(--mod); }
.module--audio .port--out .port__dot{ border-color:var(--audio); }

/* Port-Farbe richtet sich nach dem Signaltyp (data-kind), nicht nach IN/OUT —
   das hält die Legende (Klang/Steuerung/Takt-Trigger) konsistent mit den
   tatsächlich sichtbaren Port-Punkten. Überschreibt die generischen Regeln
   oben gezielt für Trigger-/Note-Ports, die sonst fälschlich Türkis/Pink wären. */
.port[data-kind="trigger-in"] .port__dot,
.port[data-kind="gate-out"] .port__dot,
.port[data-kind="note-out"] .port__dot{
  border-color:var(--warn);
}
.port[data-kind="trigger-in"].port--active .port__dot,
.port[data-kind="gate-out"].port--active .port__dot,
.port[data-kind="note-out"].port--active .port__dot{
  background:var(--warn);
}

/* CV-IN-Ports sind Modulationsziele, nicht Audio — Rand daher Pink (MOD),
   nicht das generische Teal von .port--in. Höhere Spezifität überschreibt
   die Basisregel gezielt. */
.port[data-kind="cv-in"] .port__dot,
.port[data-kind="mod-in"] .port__dot{ border-color:var(--mod); }
.port[data-kind="cv-in"].port--active .port__dot,
.port[data-kind="mod-in"].port--active .port__dot{ background:var(--mod); }

/* Das Oszilloskop nimmt Klang und Steuerung — neutral statt Teal oder Pink. */
.port[data-kind="scope-in"] .port__dot{ border-color:var(--text-hi); }
.port[data-kind="scope-in"].port--active .port__dot{ background:var(--text-hi); }

.port:hover .port__dot{ transform:scale(1.25); }
.port:focus-visible{ outline:2px solid var(--audio); outline-offset:1px; border-radius:4px; }
.port--active .port__dot{ background:currentColor; }
/* Beim Kabelziehen (UIManager._zieleZeigen): passende Eingänge mit Ring
   und heller Schrift, alle übrigen Ports blass. */
.kabel-zieht .port:not(.port--passt):not(.port--active){ opacity:.3; }
.port--passt{ color:var(--text-hi); }
.port--passt .port__dot{ box-shadow:0 0 0 3px rgba(255,255,255,.2); animation:port-passt 1.2s ease-in-out infinite alternate; }
@keyframes port-passt{ to{ box-shadow:0 0 0 5px rgba(255,255,255,.08); } }
@media (prefers-reduced-motion:reduce){ .port--passt .port__dot{ animation:none; } }
.port--drop-target .port__dot{ background:#fff; box-shadow:0 0 0 4px rgba(255,255,255,.25); animation:none; }
.port--drop-invalid .port__dot{ background:#ff3b3b; box-shadow:0 0 0 4px rgba(255,59,59,.25); }
/* Ports waren mit 18px Höhe die kleinsten Tippziele der Fläche, beim
   Kabelziehen verdeckt der Finger den Punkt zudem selbst. Eine unsichtbare
   Fläche macht sie 40px hoch und 20px breiter, ohne dass sich Module oder
   Kabel verschieben. Gemessen in allen Modulen, de und en: darunter 11px bis
   zum Modulrand, darüber mindestens 21px bis zum nächsten Bedienelement,
   zwischen zwei Ports mindestens 22px (Sequenzer). Das Pseudo-Element zählt
   für pointerdown und elementFromPoint zum Port, Start und Ziel eines
   Kabels brauchen deshalb keinen eigenen Code. */
@media (pointer:coarse), (max-width:640px){
  .port{ position:relative; }
  .port::before{ content:""; position:absolute; inset:-11px -10px; }
}

/* Grund für ein abgelehntes Kabel, über dem Port (UIManager._kabelHinweis).
   Fest am Fenster statt auf der Fläche: so liegt sie über jedem Modul. */
.kabel-hinweis{
  position:fixed; z-index:60;
  max-width:240px;
  padding:6px 9px;
  background:var(--bg-1);
  border:1px solid #ff3b3b;
  border-radius:6px;
  box-shadow:0 6px 18px rgba(0,0,0,.4);
  font-size:12px; line-height:1.4; color:var(--text-hi);
  pointer-events:none;
}
.kabel-hinweis[hidden]{ display:none; }

/* Hinweis im richtigen Moment (UIManager._bindTipps): Aussehen wie die
   Sprechblase am Port, in der Farbe der Notizen und mit Pfeil zum Anker.
   z-index unter der Schublade (6), damit sie ihn verdeckt. */
.tipp{
  position:fixed; z-index:5;
  max-width:220px;
  padding:6px 9px;
  background:var(--bg-1);
  border:1px solid var(--notiz);
  border-radius:6px;
  box-shadow:0 6px 18px rgba(0,0,0,.4);
  font-size:12px; line-height:1.4; color:var(--text-hi);
  pointer-events:none;
}
.tipp[hidden]{ display:none; }
.tipp::before{
  content:""; position:absolute;
  left:calc(var(--pfeil, 50%) - 5px);
  width:8px; height:8px;
  background:var(--bg-1);
  border:1px solid var(--notiz);
  transform:rotate(45deg);
}
.tipp[data-lage="unter"]::before{ top:-5px; border-right:0; border-bottom:0; }
.tipp[data-lage="ueber"]::before{ bottom:-5px; border-left:0; border-top:0; }

/* =========================================================
   Ausgangsanzeige im Main Out und Modul Oszilloskop (js/scope.js)
   ========================================================= */
/* Ein Knopf, weil ein Klick den Modus wechselt — so ist er auch per
   Tastatur erreichbar. Das Canvas füllt ihn, Modus und Lampe liegen
   als Text darüber, damit sie mit der Sprache wechseln. */
.scope{
  position:relative;
  display:block;
  width:100%;
  height:64px;
  padding:0;
  background:var(--bg-0);
  border:1px solid var(--line);
  border-radius:6px;
  cursor:pointer;
  overflow:hidden;
}
/* Im Oszilloskop ist die Anzeige der ganze Inhalt, daher höher. */
.scope--modul{ height:120px; }
.scope:focus-visible{ outline:2px solid var(--audio); outline-offset:2px; }
.scope__bild{ display:block; width:100%; height:100%; }
.scope__modus,
.scope__clip{
  position:absolute; top:3px;
  font-size:9px; letter-spacing:.3px; text-transform:uppercase;
  line-height:1; padding:2px 4px; border-radius:3px;
  pointer-events:none;
}
.scope__modus{ left:4px; color:var(--text-lo); background:rgba(10,10,11,.7); }
.scope__clip{ right:4px; color:#fff; background:#ff3b3b; font-weight:600; }
.scope__clip[hidden]{ display:none; }

/* =========================================================
   Sequencer grid
   ========================================================= */
.seq-grid{
  display:grid;
  grid-template-columns:repeat(8, 1fr);
  gap:5px;
}
.seq-step{
  /* Höhe bewusst fest statt über aspect-ratio an die (variable) Breite
     gekoppelt: bei 16 Spalten (32/64 Schritte) zwang aspect-ratio+eine
     Mindesthöhe den Browser, auch eine Mindestbreite je Zelle zu
     erzwingen ("transferred size") — die Zellen ragten dadurch rechts
     über das Modul hinaus, selbst mit min-width:0 nicht zuverlässig
     behoben. Mit fester Höhe kann die Breite (1fr) immer frei auf die
     verfügbare Modulbreite schrumpfen, unabhängig von der Spaltenzahl. */
  height:22px;
  min-width:0;
  border-radius:5px;
  background:var(--bg-2);
  border:1px solid var(--line);
  cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  /* Vertikales Ziehen auf einem Schritt ändert die Tonhöhe (Touch-Ersatz
     fürs Mausrad) und darf nicht als Canvas-Scroll interpretiert werden. */
  touch-action:none;
}
.seq-step__note{
  font-size:8px; line-height:1;
  font-family:var(--font-mono);
  color:var(--text-lo);
  pointer-events:none; user-select:none;
}
.seq-step[data-on="true"] .seq-step__note{ color:#fff; }
.seq-hint{ font-size:10px; color:var(--text-lo); line-height:1.4; }
.seq-step:hover{ border-color:#5a5a62; }
.seq-step[data-on="true"]{
  background:var(--mod);
  border-color:var(--mod);
  box-shadow:0 0 6px rgba(255,0,85,.6);
}
.seq-step[data-playing="true"]{
  outline:2px solid var(--text-hi);
  outline-offset:1px;
}
.seq-step[data-beat="true"]{ border-color:#5a5a62; }
/* Nach dem Lauflicht: am gewählten Schritt gewinnt der Fokusrahmen. */
.seq-step:focus-visible{ outline:2px solid var(--audio); outline-offset:1px; }
/* Legato: Strich zum Vorgänger, der die 5px Lücke im Raster überbrückt.
   Gesetzt von UIManager._markTies, nie am Zeilenanfang. */
.seq-step{ position:relative; }
.seq-step[data-tied="true"]::before{
  content:''; position:absolute;
  left:-7px; top:50%; width:8px; height:4px; margin-top:-2px;
  background:var(--mod);
  pointer-events:none;
}
/* Ton außerhalb der gewählten Tonleiter: Punkt in der Ecke
   (UIManager._buildSequencerGrid, refreshNote). */
.seq-step[data-fremd="true"]::after{
  content:''; position:absolute;
  top:2px; right:2px; width:4px; height:4px; border-radius:50%;
  background:var(--warn);
  pointer-events:none;
}
/* Würfeln neben der Tonleiter, so hoch wie die Auswahl. */
.seq-wuerfeln{
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  padding:5px 8px; font:inherit; font-size:12px; color:var(--text-hi);
  cursor:pointer;
}
.seq-wuerfeln:hover{ border-color:#5a5a62; }

/* =========================================================
   Tastatur (UIManager._bindTastatur)
   ========================================================= */
/* Weiße Tasten als Flex-Reihe, schwarze darüber absolut gesetzt: ihre
   Mitte liegt auf der Fuge zweier weißer Tasten (left per JS in %).
   touch-action:none, damit Wischen über die Tasten ein Glissando ist und
   kein Scrollen der Arbeitsfläche. */
.tastatur{
  position:relative;
  display:flex;
  height:78px;
  touch-action:none;
  user-select:none;
}
.taste{
  display:flex; align-items:flex-end; justify-content:center;
  padding:0 0 4px;
  border:1px solid var(--line);
  font-family:var(--font-mono); font-size:9px; line-height:1;
  cursor:pointer;
  touch-action:none;
}
.taste--weiss{
  flex:1; min-width:0;
  background:var(--text-hi); color:#55555d;
  border-radius:0 0 4px 4px;
}
.taste--schwarz{
  position:absolute; top:0; z-index:1;
  width:9%; height:58%;
  transform:translateX(-50%);
  background:var(--bg-3); color:var(--text-lo);
  border-color:var(--bg-0);
  border-radius:0 0 3px 3px;
}
/* Halten: Knopf in Höhe der Auswahllisten, eingerastet in der Farbe der
   gedrückten Tasten. */
.tastatur__halten{
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  padding:5px 6px; font:inherit; font-size:12px; color:var(--text-lo);
  cursor:pointer;
}
.tastatur__halten[aria-pressed="true"]{
  border-color:var(--warn); color:var(--warn);
  background:rgba(255,176,32,.12);
}
/* Glide, Halten und Streifen in einer Reihe: die Knöpfe so breit wie ihre
   Aufschrift, der Rest für den Regler. */
.field-pair--tastatur{ grid-template-columns:minmax(0, 1fr) auto auto; }

/* Modulationsstreifen (UIManager._tastaturZeigen). Die Füllung reicht
   bei 0 … 1 vom linken Rand, bei −1 … +1 von der Mitte bis zum Wert. */
.streifen-feld{ display:flex; flex-direction:column; gap:10px; }
.streifen-feld[hidden]{ display:none; }
.streifen{
  position:relative;
  height:24px;
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  overflow:hidden;
  cursor:ew-resize;
  touch-action:none;
  user-select:none;
}
.streifen__fuellung{
  position:absolute; top:0; bottom:0;
  background:rgba(255,0,85,.35);
  border-inline:2px solid var(--mod);
  pointer-events:none;
}
/* Zurückfedern gleitet sichtbar, beim Ziehen folgt die Füllung sofort. */
.streifen--federt .streifen__fuellung{ transition:left .08s ease-out, width .08s ease-out; }
.streifen__wert{
  position:absolute; right:6px; top:50%; transform:translateY(-50%);
  font-family:var(--font-mono); font-size:10px; color:var(--text-hi);
  pointer-events:none;
}
/* Ohne Streifen ist MOD unsichtbar, außer es steckt noch ein Kabel: Dann
   bleibt der Port blass stehen und das Kabel wirkt wieder, sobald der
   Streifen an ist (UIManager._streifenKabel). */
.module--kbd:not([data-streifen="an"]) .port[data-port="mod"]{ opacity:.4; }
.module--kbd:not([data-streifen="an"]):not([data-mod-kabel]) .port[data-port="mod"]{ visibility:hidden; }

/* Außerhalb des Fensters der Rechnertastatur (UIManager._fensterLage):
   mit Maus und Finger spielbar, nur ohne Buchstaben und etwas blasser. */
.taste--weiss[data-fenster="aus"]{ background:#b9bbc1; }
.taste--schwarz[data-fenster="aus"]{ background:#1f1f24; }

.taste[data-an="true"]{
  background:var(--warn); color:var(--bg-0);
  box-shadow:0 0 6px rgba(255,176,32,.6);
}

/* =========================================================
   Dialog-Overlay (Bestätigen/Eingabe/Hinweis) — DOM statt
   window.confirm()/prompt()/alert(), siehe UIManager._showDialog.
   ========================================================= */
.confirm-overlay{
  position:fixed; inset:0;
  background:rgba(0,0,0,.6);
  display:flex; align-items:center; justify-content:center;
  z-index:100;
}
.confirm-box{
  width:280px;
  background:var(--bg-1);
  border:1px solid var(--line);
  border-radius:var(--radius);
  padding:20px;
  box-shadow:0 16px 40px rgba(0,0,0,.5);
}
.confirm-message{ margin:0 0 16px; font-size:13px; color:var(--text-hi); line-height:1.45; }
.confirm-input{
  width:100%; box-sizing:border-box;
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  padding:8px; margin:0 0 16px; font-size:13px; color:var(--text-hi); font-family:inherit;
}
.confirm-input:focus{ outline:none; border-color:var(--audio); }
.confirm-actions{ display:flex; justify-content:flex-end; gap:8px; }
.confirm-actions .btn{ padding:7px 14px; font-size:12px; }
.confirm-ok{ background:var(--mod-dim); border-color:var(--mod); color:var(--mod); }
.confirm-ok:focus-visible, .confirm-cancel:focus-visible{ outline:2px solid var(--audio); outline-offset:2px; }

/* Breiter Dialog mit eigenem Inhalt (Hilfe); der Inhalt scrollt,
   Überschrift und Knopf bleiben stehen. */
.confirm-box--breit{
  width:min(720px, calc(100vw - 32px));
  max-height:calc(100vh - 32px); max-height:calc(100dvh - 32px);
  box-sizing:border-box;
  display:flex; flex-direction:column;
}
.confirm-box--breit .confirm-message{ font-size:15px; font-weight:600; }
/* Hilfe (UIManager._hilfeZeigen): Reiter oben, darunter scrollt die
   Seite. Feste Höhe, damit der Kasten beim Reiterwechsel nicht springt;
   wo der Platz fehlt, gibt sie nach. */
.hilfe{ flex:0 1 640px; min-height:0; display:flex; flex-direction:column; margin:0 0 16px; }
.hilfe__reiter[hidden]{ display:none; }
.hilfe__reiter{ display:flex; gap:4px; border-bottom:1px solid var(--line); margin:0 0 12px; }
.hilfe__reiter [role="tab"]{
  background:none; border:0; border-bottom:2px solid transparent; margin-bottom:-1px;
  padding:6px 12px; font:inherit; font-size:12px; color:var(--text-lo); cursor:pointer;
}
.hilfe__reiter [role="tab"]:hover{ color:var(--text-hi); }
.hilfe__reiter [aria-selected="true"]{ color:var(--text-hi); border-bottom-color:var(--audio); }
.hilfe__reiter [role="tab"]:focus-visible{ outline:2px solid var(--audio); outline-offset:-2px; }
.hilfe__seite{ flex:1 1 auto; min-height:0; overflow:auto; margin:0 -4px; padding:0 4px; }
.hilfe__seite:focus-visible{ outline:2px solid var(--audio); outline-offset:2px; }
.hilfe__einleitung{ margin:0 0 10px; font-size:12px; line-height:1.45; color:var(--text-lo); }
.hilfe__modul{ border-top:1px solid var(--line); }
.hilfe__modul:last-child{ border-bottom:1px solid var(--line); }
.hilfe__modul > summary{ display:flex; align-items:center; gap:10px; padding:7px 2px; cursor:pointer; list-style:none; }
.hilfe__modul > summary::-webkit-details-marker{ display:none; }
.hilfe__modul > summary::after{ content:'▸'; margin-left:auto; color:var(--text-lo); }
.hilfe__modul[open] > summary::after{ content:'▾'; }
.hilfe__modul > summary:focus-visible{ outline:2px solid var(--audio); outline-offset:-2px; }
.hilfe__icon--out{ background:var(--bg-0); color:var(--text-hi); }
.hilfe__name{ display:flex; flex-direction:column; line-height:1.3; }
.hilfe__name b{ font-size:13px; font-weight:600; color:var(--text-hi); }
.hilfe__name small{ font-size:11px; color:var(--text-lo); }
.hilfe__inhalt{ padding:2px 2px 14px 40px; font-size:12px; line-height:1.45; color:var(--text-hi); }
.hilfe__inhalt > p{ margin:0 0 8px; }
.hilfe__inhalt h4{
  margin:14px 0 6px; font-size:11px; font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--text-lo);
}
.hilfe__liste{ margin:0; display:grid; grid-template-columns:fit-content(10em) 1fr; gap:8px 14px; }
.hilfe__liste dt{ font-weight:600; }
.hilfe__liste dt small{ display:block; font-weight:400; font-size:11px; color:var(--text-lo); }
.hilfe__liste dd{ margin:0; }
.hilfe__liste ul{ margin:4px 0 0; padding-left:16px; }
.hilfe__liste li{ margin:2px 0; }
.hilfe__tipp{ margin:6px 0 0; padding-left:8px; border-left:2px solid var(--mod); color:var(--text-lo); }
.hilfe__tipp b{ color:var(--text-hi); font-weight:600; }
.hilfe__port{ font-family:var(--font-mono); font-size:11px; }
.hilfe__presets{ display:flex; flex-wrap:wrap; gap:6px; margin:0; }
.hilfe__preset{ padding:6px 12px; font-size:12px; }
@media (max-width:520px){
  .hilfe__inhalt{ padding-left:2px; }
  .hilfe__liste{ grid-template-columns:1fr; gap:2px; }
  .hilfe__liste dd{ margin-bottom:8px; }
}
.kuerzel{ columns:2 260px; column-gap:24px; }
/* Niedrige Fenster (Handy quer): weniger Rand, mehr Platz zum Lesen. */
@media (max-height:420px){
  .confirm-box--breit{ padding:12px 16px; }
  .confirm-box--breit .confirm-message{ margin-bottom:8px; }
  .hilfe{ margin-bottom:10px; }
  .hilfe__reiter{ margin-bottom:6px; }
}
.kuerzel__gruppe{ break-inside:avoid; margin:0 0 14px; }
.kuerzel h3{
  margin:0 0 6px; font-size:11px; font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--text-lo);
}
.kuerzel dl{ margin:0; display:grid; grid-template-columns:fit-content(11em) 1fr; gap:6px 12px; align-items:baseline; }
.kuerzel dt{ font-size:11px; line-height:1.9; color:var(--text-lo); }
.kuerzel dt > span{ white-space:nowrap; }
.kuerzel dd{ margin:0; font-size:12px; line-height:1.4; color:var(--text-hi); }
.kuerzel kbd{
  display:inline-block; min-width:1.4em; padding:1px 5px; box-sizing:border-box;
  background:var(--bg-2); border:1px solid var(--line); border-bottom-width:2px; border-radius:4px;
  font:inherit; font-size:11px; text-align:center; color:var(--text-hi);
}

/* =========================================================
   Kachelraster — gemeinsam für "Werkzeuge" und "Modulo"
   Steht hinter der Dokument-Typografie, damit .tile-Regeln bei
   gleicher Spezifität gewinnen.
   ========================================================= */
/* Am Panel beider Kachel-Tabs, nicht nur an den Werkzeugen — daher
   --tiles und nicht --tools. */
.tabpanel--tiles{ max-width:1100px; }

/* Sprungleiste über den Kacheln */
.tools-nav{ display:flex; flex-wrap:wrap; gap:6px; margin:0 0 18px; }
.tools-nav button{
  padding:6px 14px; border-radius:999px;
  background:var(--bg-2); border:1px solid var(--line);
  font-size:12.5px; color:var(--text-lo); cursor:pointer;
}
.tools-nav button:hover{ color:var(--text-hi); border-color:#4a4a52; }
@media (pointer:coarse), (max-width:640px){
  .tools-nav button{ min-height:40px; font-size:14px; }
}
.tiles{
  display:grid;
  /* min(): unter 300px Panelbreite (kleine Handys) nicht überlaufen */
  grid-template-columns:repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap:16px;
}
/* Kacheln einer Reihe gleich hoch (Grid streckt), der Hinweis sitzt unten:
   die Rahmen stehen bündig statt Lücken unter kurzen Kacheln. */
.tile{
  display:flex;
  flex-direction:column;
  /* Sprungziel: nicht unter der klebenden Tab-Leiste verschwinden */
  scroll-margin-top:84px;
  background:var(--bg-1);
  border:1px solid var(--line);
  border-top:2px solid var(--audio);
  border-radius:var(--radius);
  box-shadow:0 8px 24px rgba(0,0,0,.4);
}
.tile--seq{ border-top-color:var(--warn); }
.tile--mod{ border-top-color:var(--mod); }
.tile--out{ border-top-color:var(--text-hi); }
.tile__head{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:9px 12px;
  border-bottom:1px solid var(--line);
}
.tile .tile__title{ margin:0; font-size:12.5px; font-weight:600; letter-spacing:.2px; }
.tile__action{ padding:5px 12px; font-size:12px; }
.tile__body{ flex:1 1 auto; padding:12px; display:flex; flex-direction:column; gap:12px; }
.tile__inputs{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(80px, 1fr));
  gap:8px;
}
/* Vier Felder: 2 × 2 statt 3 + 1 mit einem verwaisten Feld */
.tile__inputs--2x2{ grid-template-columns:repeat(2, minmax(0, 1fr)); }
/* Untereinander: für lange Eingaben wie eine IBAN, die nebeneinander
   nur ein paar Zeichen breit wäre */
.tile__inputs--stapel{ grid-template-columns:1fr; }
/* Einzelnes Feld über die volle Zeile, neben kurzen Nachbarn */
.tile__inputs .field--weit{ grid-column:1 / -1; }
.tile .field input,
.tile .field textarea{
  width:100%; min-width:0;
  background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  padding:5px 6px; font-size:12px; font-family:var(--font-mono); color:var(--text-hi);
}
/* Nur in der Höhe ziehbar: seitlich würde das Feld aus der Kachel laufen. */
.tile .field textarea{ display:block; resize:vertical; line-height:1.5; }
.tile .field input:focus,
.tile .field textarea:focus{ outline:none; border-color:var(--audio); }
.tile .field input:invalid{ border-color:var(--mod); }

/* Touch: iOS Safari zoomt beim Antippen in Felder unter 16px Schrift
   hinein; Tippziele auf etwa 40px, damit sie mit dem Finger treffbar sind. */
@media (pointer:coarse), (max-width:640px){
  .tile .field input,
  .tile .field textarea,
  .tile .field select{ font-size:16px; min-height:40px; }
  .tile__action{ min-height:40px; padding:8px 16px; font-size:13px; }
  .tile .tile__hint summary{ padding:10px 0; font-size:13px; }
}
.tile__result{
  display:block;
  font-family:var(--font-mono); font-size:12px; line-height:1.6;
  color:var(--text-lo);
}
.tile__result strong{ display:block; font-size:15px; color:var(--text-hi); font-weight:600; }
.tile__result .tile__error{ color:var(--mod); }
.tile .tile__intro{ margin:0; font-size:12px; line-height:1.5; }
/* Brücke zum Generator: Modulnamen in den Farben der Palette-Legende */
.tile .tile__use{
  margin:0; padding:7px 9px;
  font-size:11.5px; line-height:1.5; color:var(--text-lo);
  background:var(--bg-0); border-left:2px solid var(--line); border-radius:4px;
}
.tile__use-label{
  display:block; margin-bottom:2px;
  font-size:9.5px; letter-spacing:1px; text-transform:uppercase;
}
/* Ein Klick zeigt das Modul im Generator (UIManager._bindModulZeigen) */
.tile__modul{
  padding:0; border:0; background:none; font:inherit; font-weight:600; color:var(--text-hi);
  text-decoration:underline dotted 1px; text-underline-offset:2px; cursor:pointer;
}
.tile__modul:hover{ text-decoration-style:solid; }
.tile__modul.klang{ color:var(--audio); }
.tile__modul.steuerung{ color:var(--mod); }
.tile__modul.takt{ color:var(--warn); }
/* Erklärung und Formel eingeklappt: wer nur rechnen will, scrollt weniger */
.tile__hint{ margin-top:auto; padding-top:8px; border-top:1px solid var(--line); }
.tile__hint summary{ cursor:pointer; font-size:11px; color:var(--text-lo); }
.tile__hint summary:hover,
.tile__hint[open] summary{ color:var(--text-hi); }
.tile .tile__hint p{ margin:6px 0 0; font-size:10.5px; line-height:1.45; }

/* Euklidischer Rhythmus: Ring (Schritt 0 oben, im Uhrzeigersinn) und
   dieselbe Folge als Zeile, im Duktus der Sequencer-Schritte. */
.euclid-ring{ width:100%; max-width:200px; margin:0 auto; display:block; }
.euclid-ring circle.bahn{ fill:none; stroke:var(--line); stroke-width:.6; }
.euclid-ring polygon{ fill:rgba(255,176,32,.12); stroke:var(--warn); stroke-width:.8; }
.euclid-ring circle.schritt{ fill:var(--bg-2); stroke:var(--text-lo); stroke-width:.6; }
.euclid-ring circle.schritt--an{ fill:var(--warn); stroke:var(--warn); }
.step-row{ display:grid; gap:4px; }
.step-row span{ height:16px; border-radius:4px; background:var(--bg-2); border:1px solid var(--line); }
.step-row span.an{ background:var(--warn); border-color:var(--warn); }

.tile__table{ width:100%; border-collapse:collapse; font-family:var(--font-mono); font-size:11.5px; }
.tile__table th,
.tile__table td{ padding:4px 6px; text-align:right; border-bottom:1px solid var(--line); }
.tile__table th{ color:var(--text-lo); font-weight:500; font-family:var(--font-ui); font-size:11px; }
.tile__table td{ color:var(--text-hi); }
.tile__table th:first-child,
.tile__table td:first-child{ text-align:left; color:var(--text-lo); }
/* Sechzehntel = ein Sequenzer-Schritt; "aus" = im Generator nicht einstellbar */
.tile__table tr.tile__table-mark td{ background:var(--bg-2); }
.tile__table td.aus{ opacity:.4; }
/* Texttabelle (Akkord): linksbündig, im Ergebnisblock mit etwas Luft */
.tile__table--text th,
.tile__table--text td{ text-align:left; }
.tile__result .tile__table{ margin:4px 0 6px; }

/* Skala: zwölf Tasten ab C, schwarze Tasten dunkler, Grundton in Amber. */
.step-row--keys span[data-schwarz]{ background:var(--bg-0); }
.step-row--keys span.an{ background:var(--audio); border-color:var(--audio); }
.step-row--keys span.grund{ background:var(--warn); border-color:var(--warn); }
.tile .field select{ width:100%; min-width:0; }

/* =========================================================
   Modulo — Darstellungen der Kacheln
   ========================================================= */
/* Modulo-Uhr: Stellen 0 … m−1 auf dem Kreis, 0 oben, Zeiger auf dem Rest. */
.mod-dial{ width:100%; max-width:190px; margin:0 auto; display:block; overflow:visible; }
.mod-dial circle.bahn{ fill:none; stroke:var(--line); stroke-width:.6; }
.mod-dial circle.stelle{ fill:var(--bg-2); stroke:var(--text-lo); stroke-width:.6; }
.mod-dial circle.stelle--null{ fill:var(--bg-2); stroke:var(--text-hi); stroke-width:1; }
.mod-dial circle.stelle--rest{ fill:var(--warn); stroke:var(--warn); }
.mod-dial line.zeiger{ stroke:var(--warn); stroke-width:.8; }
.mod-dial text{
  font-family:var(--font-mono); font-size:6px; fill:var(--text-lo);
  text-anchor:middle; dominant-baseline:central;
}
.mod-dial text.rest{ fill:var(--warn); }

/* Prüfziffern: Ziffern und Gewichte stehen untereinander, deshalb kein
   Umbruch — lieber waagerecht schieben als die Spalten verlieren. */
.pruef-rechnung{
  margin:2px 0; padding:5px 7px;
  background:var(--bg-0); border-radius:4px;
  font-size:11px; line-height:1.5; color:var(--text-hi);
  white-space:pre; overflow-x:auto;
}
/* Die IBAN wird zu einer Zahl mit über zwanzig Stellen: hier ist der
   Umbruch wichtiger als die Ausrichtung. */
.pruef-rechnung--umbruch{ white-space:pre-wrap; word-break:break-all; }
.tile__result.pruef-ok strong{ color:var(--audio); }
.tile__result.pruef-schlecht strong{ color:var(--mod); }

/* Caesar: zwei Zeilen genau untereinander — Alphabet über Verschiebung
   bzw. Text über Schlüssel. Der Sinn geht verloren, sobald umbrochen wird. */
.caesar-band{
  padding:6px 7px;
  background:var(--bg-0); border-radius:4px;
  font-family:var(--font-mono); font-size:11px; line-height:1.6;
  color:var(--text-lo);
  white-space:pre; overflow-x:auto;
}
/* Modul-Kreis: alle Sehnen in einem Pfad, halbdurchsichtig — wo viele
   übereinanderliegen, zeichnet sich die Hüllkurve von selbst heller ab. */
.mod-circle{ width:100%; max-width:230px; margin:0 auto; display:block; }
.mod-circle circle.bahn{ fill:none; stroke:var(--line); stroke-width:.4; }
.mod-circle path.faden{
  fill:none; stroke:var(--audio); stroke-width:.25; stroke-opacity:.55;
}
.mod-circle circle.punkt{ fill:var(--text-lo); }
.mod-circle circle.punkt--null{ fill:var(--warn); }

/* Pascal mod m: ein Pfad je Rest. Die Null bleibt dunkel, sonst ginge
   das Muster in der Fläche unter. */
.mod-pascal{ width:100%; max-width:240px; margin:0 auto; display:block; }
.mod-pascal path.voll{ fill:var(--audio); }
.mod-pascal path.leer{ fill:var(--bg-2); }

/* Polyrhythmus: je Schritt eine Zelle, oben Stimme A, unten Stimme B. */
.poly-row{ display:grid; gap:3px; }
.poly-row span{ display:flex; flex-direction:column; gap:2px; border-radius:3px; }
.poly-row i{ height:8px; border-radius:2px; background:var(--bg-2); border:1px solid var(--line); }
.poly-row i.a{ background:var(--warn); border-color:var(--warn); }
.poly-row i.b{ background:var(--audio); border-color:var(--audio); }
.poly-row span.beide{ outline:1px solid var(--text-hi); outline-offset:1px; }

/* Das Ergebnis ist das, was man mitnimmt: größer und markierbar. */
.caesar-aus{
  margin:2px 0 4px; padding:7px 8px;
  background:var(--bg-0); border-left:2px solid var(--audio); border-radius:4px;
  font-size:12.5px; line-height:1.5; color:var(--text-hi);
  word-break:break-word;
}

/* Pegelanzeige: Balken über −60 … 0 dBFS, darüber in Pink. */
.pegel{ height:10px; background:var(--bg-2); border:1px solid var(--line); border-radius:5px; overflow:hidden; }
.pegel__balken{ height:100%; background:var(--audio); transition:width .15s ease; }
.pegel__balken.ueber{ background:var(--mod); }
