*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}

/* Farben und Schriften stehen in themes.css — dort ist auch beschrieben,
   warum sie nach ihrer Aufgabe benannt sind und nicht nach ihrem Farbton. */

html{scroll-behavior:smooth}

/* font-weight:300 war hier der Hauptgrund fuer die schlechte Lesbarkeit:
   Inter Light auf dunklem Grund verliert bei kleinen Graden sichtbar an
   Kontrast. Regular ist die richtige Wahl fuer Fliesstext. */
body{background:var(--bg);color:var(--text);font-family:var(--sans);font-size:15px;line-height:1.6;font-weight:400;overflow:hidden;-webkit-font-smoothing:antialiased}

/* ═══ FOKUS & TASTATURBEDIENUNG ═══
   Ein sichtbarer Fokusring auf allem, was per Tab erreichbar ist —
   :focus-visible zeigt ihn nur bei Tastaturbedienung, nicht bei Mausklick. */
:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:2px}
:focus:not(:focus-visible){outline:none}
/* Browser ohne :focus-visible verwerfen beide Regeln oben und zeigen ihren
   eigenen Fokusrahmen. Hier wird daraus wieder unserer. */
@supports not selector(:focus-visible){
  a:focus,button:focus,input:focus,[tabindex]:focus{outline:2px solid var(--blue);outline-offset:2px}
}

.skip-link{position:absolute;top:0;left:0;z-index:10000;transform:translateY(-120%);
  background:var(--blue);color:#0D1117;font-family:var(--mono);font-size:.78rem;font-weight:700;
  padding:.7rem 1.2rem;text-decoration:none;transition:transform .15s ease}
.skip-link:focus{transform:none}

/* Buttons, die wie Text/Zeilen aussehen sollen, auf Null setzen */
.tree-folder,.tree-file,.tab-x,.login-cancel,.lh-close,.splash-skip,.cta{
  font:inherit;color:inherit;background:none;border:none;cursor:pointer}

/* ═══ LOADING SCREEN ═══ */
#splash{position:fixed;inset:0;z-index:9999;background:#0D1117;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;transition:opacity .5s ease}
#splash.fade{opacity:0;pointer-events:none}
.splash-logo{display:flex;align-items:center;gap:1rem}
.splash-icon{width:48px;height:48px;background:var(--blue);display:flex;align-items:center;justify-content:center;font-size:1.4rem;animation:splashPulse 1s ease-in-out infinite alternate}
@keyframes splashPulse{from{box-shadow:0 0 0 0 rgba(88,166,255,.4)}to{box-shadow:0 0 0 12px rgba(88,166,255,0)}}
.splash-name{font-family:var(--mono);font-size:1.1rem;color:var(--text)}
.splash-name span{color:var(--blue)}
.splash-files{font-family:var(--mono);font-size:.72rem;color:var(--dim);text-align:left;width:min(320px,84vw);height:5rem;overflow:hidden}
.splash-file{padding:.1rem 0;transition:color .3s}
.splash-file.done{color:var(--green)}
.splash-file.active{color:var(--orange)}
.splash-bar-wrap{width:min(320px,84vw)}
.splash-bar-label{font-family:var(--mono);font-size:.68rem;color:var(--muted);margin-bottom:.5rem;display:flex;justify-content:space-between}
.splash-bar-track{height:2px;background:var(--border);width:100%}
.splash-bar-fill{height:100%;background:var(--blue);width:0%;transition:width .4s ease}
.splash-version{font-family:var(--mono);font-size:.65rem;color:var(--dim)}
.splash-skip{font-family:var(--mono);font-size:.68rem;color:var(--dim);border:1px solid var(--border2);
  padding:.35rem .9rem;transition:color .15s,border-color .15s}
.splash-skip:hover{color:var(--muted);border-color:var(--border)}

/* ═══ IDE SHELL ═══ */
/* dvh statt vh: auf dem Handy zieht sich die Adressleiste beim Scrollen
   ein, 100vh rechnet mit der ausgefahrenen Höhe und erzeugt sonst einen
   abgeschnittenen Rand. vh bleibt als Rückfall für ältere Browser. */
.ide{display:grid;grid-template-rows:38px 36px 1fr;height:100vh;height:100dvh;overflow:hidden;opacity:0;transition:opacity .4s ease}
.ide.visible{opacity:1}

/* nur für Screenreader */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* TITLEBAR */
.titlebar{background:var(--tb);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 1rem;gap:1rem;user-select:none;flex-shrink:0;min-width:0}
/* Die drei runden Ampelpunkte von macOS sind entfallen: VS Code hat sie
   nicht. Auf Windows und Linux zeichnet es seine eigene Titelleiste mit
   den Fensterknoepfen rechts, auf macOS liefert das Betriebssystem sie.
   Ein nachgebauter macOS-Rahmen um eine VS-Code-Oberflaeche war also
   gerade der Teil, der nicht stimmte. Stattdessen steht dort jetzt das
   Menue und das Suchfeld — beides in vscode.css. Auch .tb-title ist damit
   entfallen: Die Mitte der Titelleiste gehoert dem Suchfeld, genau wie im
   Original. */

/* Login-Status — sass früher in der blauen Leiste unten */
.tb-auth{font-family:var(--mono);font-size:.64rem;color:var(--dim);white-space:nowrap;
  border:1px solid var(--border2);padding:.2rem .55rem;border-radius:2px;flex-shrink:0}
.tb-auth.authed{color:var(--green);border-color:rgba(63,185,80,.4);background:rgba(63,185,80,.1)}

/* DE/EN-Umschalter — gleiche Bauart wie der Login-Status daneben */
.tb-lang{font-family:var(--mono);font-size:.64rem;font-weight:600;letter-spacing:.03em;
  color:var(--muted);white-space:nowrap;border:1px solid var(--border2);
  padding:.2rem .5rem;border-radius:2px;flex-shrink:0;cursor:pointer;
  background:transparent;transition:background .15s,color .15s}
.tb-lang:hover{background:rgba(255,255,255,.07);color:var(--text)}

/* Explorer-Schalter — nur auf schmalen Bildschirmen sichtbar */
.sb-toggle{display:none;font-size:1rem;line-height:1;color:var(--muted);padding:.3rem .5rem;
  border-radius:3px;flex-shrink:0;transition:background .15s,color .15s}
.sb-toggle:hover{background:rgba(255,255,255,.07);color:var(--text)}
.sb-backdrop{display:none}

/* TABBAR */
.tabbar{background:var(--tb);border-bottom:1px solid var(--border);display:flex;align-items:stretch;overflow-x:auto;scrollbar-width:none}
.tabbar::-webkit-scrollbar{display:none}
.tab{display:flex;align-items:center;gap:.4rem;padding:0 1rem;height:36px;font-family:var(--mono);font-size:.72rem;color:var(--muted);cursor:pointer;border-right:1px solid var(--border);border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s,background .15s;flex-shrink:0;position:relative}
.tab:hover{background:rgba(255,255,255,.04);color:var(--text)}
.tab.active{color:var(--text);border-bottom-color:var(--blue);background:var(--bg)}
.tab .tab-x{opacity:0;margin-left:.3rem;font-size:.8rem;line-height:1;color:var(--dim);padding:.15rem .25rem;
  border-radius:2px;transition:opacity .15s,background .15s,color .15s}
.tab:hover .tab-x,.tab .tab-x:focus-visible{opacity:1}
.tab .tab-x:hover{background:rgba(255,255,255,.1);color:var(--text)}
.tab.closed{display:none}
.tab .lock{font-size:.58rem;opacity:.55;margin-left:.25rem}
.tab.locked-tab{color:var(--dim)}
.tab.locked-tab:hover{color:var(--muted)}

/* PANE */
.pane{display:grid;grid-template-columns:240px 1fr;overflow:hidden;min-height:0}

/* SIDEBAR */
.sidebar{background:var(--sb);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--border) transparent}
.sb-title{font-family:var(--mono);font-size:.62rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);padding:.85rem 1rem .45rem;border-bottom:1px solid var(--border);flex-shrink:0;display:flex;justify-content:space-between;align-items:center}
.sb-title-icons{display:flex;gap:.6rem;font-size:.75rem;color:var(--dim)}
.sb-title-icons span{cursor:pointer;transition:color .15s}
.sb-title-icons span:hover{color:var(--muted)}
.tree{padding:.3rem 0}

/* Folder rows */
.tree-folder{width:100%;text-align:left;font-family:var(--mono);font-size:.73rem;color:var(--muted);padding:.3rem .9rem;display:flex;align-items:center;gap:.45rem;cursor:pointer;user-select:none;transition:color .15s,background .15s}
.tree-folder:hover{background:rgba(255,255,255,.04);color:var(--text)}
.tree-folder .arrow{font-size:.55rem;transition:transform .2s;display:inline-block;width:.7rem}
.tree-folder.open .arrow{transform:rotate(90deg)}
.folder-icon{font-size:.78rem}

/* File rows */
/* visibility mitschalten, damit eingeklappte Dateien nicht per Tab
   erreichbar bleiben (unsichtbar, aber fokussierbar wäre ein Fehler) */
.tree-children{overflow:hidden;max-height:0;visibility:hidden;transition:max-height .25s ease,visibility 0s .25s}
.tree-folder.open + .tree-children{max-height:600px;visibility:visible;transition:max-height .25s ease,visibility 0s}
.tree-file{width:100%;text-align:left;font-family:var(--mono);font-size:.71rem;color:var(--muted);padding:.26rem .9rem .26rem 2.4rem;display:flex;align-items:center;gap:.45rem;cursor:pointer;transition:background .12s,color .12s}
.tree-file:hover{background:rgba(255,255,255,.05);color:var(--text)}
.tree-file.active{background:rgba(88,166,255,.1);color:var(--blue)}
.tree-file.locked-file{color:var(--dim)}
.tree-file.locked-file:hover{color:var(--muted);background:rgba(255,255,255,.03)}

/* file type colors */
.ic-js{color:#E3B341}
.ic-sql{color:#CC2936}.ic-md{color:#8B949E}.ic-json{color:#CBCB41}
.ic-lock{color:var(--dim)}

/* file-icon (devicon / svg) sizing for tabs & sidebar */
.file-ic{font-size:.95rem;line-height:1;width:1em;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}
.file-ic svg{width:1em;height:1em}
/* Markdown-Glyph aufhellen — nur am inneren <i>, sonst zeichnet die
   Devicon-Schrift das Icon zweimal (einmal pro Element mit der Klasse). */
.ic-md-wrap i{filter:invert(1) brightness(1.6)}
/* "{ }" ist drei Zeichen breit und würde in der 1em-Box umbrechen */
.file-ic.ic-json{width:auto;white-space:nowrap;font-size:.8rem;letter-spacing:-.04em}

.sb-bottom{margin-top:auto;border-top:1px solid var(--border);padding:.6rem 1rem;display:flex;align-items:center;gap:.5rem}
.git-dot{width:7px;height:7px;border-radius:50%;background:var(--green);flex-shrink:0}
.git-txt{font-family:var(--mono);font-size:.66rem;color:var(--green)}

/* EDITOR */
/* min-width:0 ist hier Pflicht: Grid-Spalten schrumpfen sonst nicht unter
   ihre min-content-Breite, und eine einzige lange Code-Zeile würde die
   ganze Seite auf dem Handy seitlich aufziehen. */
.editor{overflow:hidden;display:flex;flex-direction:column;min-height:0;min-width:0}
.editor-scroll{overflow-y:auto;flex:1;scrollbar-width:thin;scrollbar-color:var(--border) transparent}
.editor-panel{display:none}
.editor-panel.active{display:block}

/* Leerer Editor (alle Tabs geschlossen) */
.editor-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1rem;min-height:60vh;padding:2rem;text-align:center}
.editor-empty[hidden]{display:none}
.ee-key{font-family:var(--mono);font-size:2rem;color:var(--border);letter-spacing:.15em}
.ee-txt{font-family:var(--mono);font-size:.82rem;color:var(--dim)}

/* Zweispaltiger Lebenslauf */
.cv-cols{display:grid;grid-template-columns:1fr 1fr;gap:3rem}
@media(max-width:820px){.cv-cols{grid-template-columns:1fr;gap:1rem}}

/* BREADCRUMB */
.breadcrumb{background:var(--tb);border-bottom:1px solid var(--border);padding:.36rem 1.5rem;font-family:var(--mono);font-size:.7rem;color:var(--dim);display:flex;align-items:center;gap:.4rem;flex-shrink:0;user-select:none}
.breadcrumb .bc-part{color:var(--text)}
.breadcrumb .sep{color:var(--dim)}
.bc-logout{margin-left:auto;background:none;border:1px solid var(--border);color:var(--muted);font-family:var(--mono);font-size:.65rem;padding:.22rem .65rem;cursor:pointer;transition:border-color .2s,color .2s}
.bc-logout:hover{border-color:var(--red);color:var(--red)}

/* ═══ GLIEDERUNG & LESEFORTSCHRITT ═══
   Lange Abschnitte werden sonst zur Scroll-Strecke. Die Gliederung wird
   aus den Zwischentiteln erzeugt und bleibt beim Scrollen oben stehen. */
/* z-index bewusst hoch: ein Bild, ueber dem die Maus steht, bekommt durch
   die Hervorhebung eine eigene Zeichenebene und schob sich sonst vor die
   Leiste, sobald es beim Scrollen darunter durchlief. */
/* Eine Zeile, die bei Bedarf seitwaerts laeuft. Mit Umbruch wurde die Leiste
   auf schmaleren Bildschirmen drei Zeilen hoch und nahm dem Text oben
   dauerhaft ueber hundert Pixel weg. */
.outline{position:sticky;top:0;z-index:40;background:var(--bg);
  border-bottom:1px solid var(--border2);padding:.55rem 2.5rem;
  display:flex;flex-wrap:nowrap;align-items:center;gap:.3rem .5rem;
  overflow-x:auto;scrollbar-width:none}
.outline::-webkit-scrollbar{display:none}
.outline a{font-family:var(--mono);font-size:.7rem;color:var(--muted);
  text-decoration:none;padding:.2rem .55rem;border:1px solid transparent;
  border-radius:2px;transition:color .15s,background .15s,border-color .15s;
  flex-shrink:0;white-space:nowrap}
.outline a:hover{color:var(--text);background:rgba(255,255,255,.05)}
.outline a.current{color:var(--blue);border-color:var(--border);background:#161B22}

.read-bar{position:sticky;top:0;z-index:41;height:2px;background:transparent}
.read-bar span{display:block;height:100%;width:0;background:var(--blue);
  transition:width .1s linear}

/* Zieltitel nicht unter der Gliederung verstecken */
.code-content h3.ed-h2{scroll-margin-top:4.2rem}
/* Freiraum am Ende, damit auch der letzte Abschnitt nach oben gescrollt
   werden kann. Ohne ihn bleibt der Sprung dorthin wirkungslos. */
.code-content.has-outline{padding-bottom:42vh}

/* ═══ FALTBARE DETAILS (Code-Folding-Metapher) ═══ */
.fold{margin:0 0 .9rem}
.fold-btn{display:inline-flex;align-items:center;gap:.4rem;font-family:var(--mono);
  font-size:.7rem;color:var(--muted);background:none;border:none;cursor:pointer;
  padding:.25rem 0;transition:color .15s}
.fold-btn:hover{color:var(--blue)}
.fold-btn .fold-arrow{display:inline-block;font-size:.6rem;transition:transform .18s}
.fold-btn[aria-expanded="true"] .fold-arrow{transform:rotate(90deg)}
.fold-body[hidden]{display:none}
.fold-body{padding-top:.5rem}

/* In den Projektkarten ist der Schalter kein Nebensatz, sondern das
   einzige Bedienelement der Karte — er bekommt einen Rahmen, damit man
   ihn als Knopf erkennt und nicht für eine Überschrift hält. */
.proj-card>.fold{margin:.2rem 0 .9rem}
.proj-card .fold-btn{border:1px solid var(--border);border-radius:3px;
  padding:.3rem .7rem;font-size:.74rem}
.proj-card .fold-btn:hover{border-color:var(--blue);background:var(--sb)}
/* Im geöffneten Zustand hängt der Inhalt sichtbar am Knopf. */
.proj-card .fold-body{border-left:1px solid var(--border);padding-left:.9rem;
  margin-top:.6rem}

/* CODE VIEW
   Die Spalte mit den geschaetzten Zeilennummern ist entfallen — echte,
   fortlaufende Nummern stehen jetzt in den Codebloecken selbst, wo sie
   stimmen. Damit brauchte es hier auch kein Raster mehr. */
.code-view{min-height:100%}
.code-content{padding:2rem 2.5rem;min-width:0;overflow-wrap:break-word}

/* SYNTAX */
.cmt{color:var(--dim);font-family:var(--mono);font-size:.79rem;font-style:italic}
.kw{color:var(--purple);font-family:var(--mono)}
.kw-b{color:var(--blue);font-family:var(--mono)}
.kw-g{color:var(--green);font-family:var(--mono)}
.str{color:var(--orange);font-family:var(--mono)}
.fn{color:#79C0FF;font-family:var(--mono)}
.punct{color:var(--muted);font-family:var(--mono)}
.num{color:#79C0FF;font-family:var(--mono)}
.type{color:var(--teal);font-family:var(--mono)}

/* HERO */
.hero-name{font-family:var(--mono);font-size:clamp(2.4rem,4.5vw,4rem);font-weight:700;line-height:.95;color:var(--text);margin:1.2rem 0 .6rem;letter-spacing:-.02em}
.hero-name .acc{color:var(--blue)}
.hero-role{font-family:var(--mono);font-size:.92rem;color:var(--muted);margin-bottom:1.8rem}
.blink{display:inline-block;width:2px;height:.85em;background:var(--blue);margin-left:2px;vertical-align:text-bottom;animation:blink 1.1s step-end infinite}
@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}

/* PROSE */
.prose{font-family:var(--sans);font-size:.93rem;color:var(--muted);line-height:1.85;max-width:72ch}
.prose p{margin-bottom:1rem}
.prose strong{color:var(--text);font-weight:500}

/* Inline-Code im Fliesstext und in Tabellen */
code{font-family:var(--mono);font-size:.86em;background:var(--border2);color:var(--teal);
  padding:.1em .38em;border-radius:2px;border:1px solid var(--border);white-space:nowrap}

/* CALL TO ACTION */
.cta-row{display:flex;flex-wrap:wrap;gap:.7rem;margin:1.6rem 0 .4rem}
.cta{display:inline-flex;align-items:center;gap:.5rem;font-family:var(--mono);font-size:.78rem;
  padding:.62rem 1.1rem;border:1px solid var(--border);background:#161B22;color:var(--text);
  text-decoration:none;transition:border-color .2s,background .2s,transform .15s}
.cta:hover{border-color:var(--blue);background:#1C2333;transform:translateY(-2px)}
/* Das GitHub-Zeichen kommt aus derselben Schrift wie die Technologie-Logos.
   Ohne "colored" nimmt es die Farbe der Schaltflaeche an — die Originalfarbe
   waere fast schwarz und auf dunklem Grund nicht zu sehen. */
.cta-ic{font-size:1.05em;line-height:1}
.cta-primary{background:var(--blue);border-color:var(--blue);color:#0D1117;font-weight:700}
.cta-primary:hover{background:#79C0FF;border-color:#79C0FF;color:#0D1117}

/* KURZ-FAKTEN */
.fact-row{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--border);
  margin:1.8rem 0 .5rem;max-width:760px;border:1px solid var(--border)}
.fact{background:var(--bg);padding:.9rem 1rem}
.fact dt{font-family:var(--mono);font-size:.6rem;letter-spacing:.09em;text-transform:uppercase;color:var(--dim);margin-bottom:.3rem}
.fact dd{font-family:var(--mono);font-size:.78rem;color:var(--text)}

/* CODE BLOCK */
/* Lange Code-Zeilen scrollen im Block, statt die Seite breiter zu machen */
.cb{background:#161B22;border:1px solid var(--border);padding:1.1rem 1.4rem;margin:1.5rem 0;font-family:var(--mono);font-size:.77rem;line-height:2;position:relative;border-radius:3px;overflow-x:auto;overflow-y:hidden}
.cb::before{content:"";position:absolute;inset:0;background:linear-gradient(135deg,rgba(88,166,255,.03),transparent);pointer-events:none}
.cb-lang{position:absolute;top:.45rem;right:.75rem;font-size:.62rem;color:var(--dim);letter-spacing:.07em}
.cl{display:flex;gap:.5rem;align-items:baseline}
.i1{padding-left:1.4rem}.i2{padding-left:2.8rem}

/* SECTION HEADERS */
.ed-h1{font-family:var(--mono);font-size:1.4rem;font-weight:700;color:var(--text);margin:1.5rem 0 1.2rem;padding-bottom:.55rem;border-bottom:1px solid var(--border)}
.ed-h2{font-family:var(--mono);font-size:.88rem;font-weight:500;color:var(--blue);margin:2rem 0 .8rem;display:flex;align-items:center;gap:.5rem}
.ed-h2::before{content:"##";color:var(--dim);font-size:.78rem}

/* TABLE */
.ed-table{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:.77rem;margin:.5rem 0 1.8rem}
.ed-table th{text-align:left;color:var(--muted);font-weight:400;padding:.45rem .75rem;border-bottom:1px solid var(--border)}
.ed-table td{padding:.45rem .75rem;border-bottom:1px solid var(--border2);color:var(--text);transition:background .15s}
.ed-table tr:hover td{background:rgba(255,255,255,.02)}
.badge{font-size:.63rem;letter-spacing:.05em;padding:.15rem .5rem;border-radius:2px;display:inline-block;font-family:var(--mono)}
.bg{background:rgba(63,185,80,.15);color:var(--green)}
.bb{background:rgba(88,166,255,.15);color:var(--blue)}
.bo{background:rgba(227,179,65,.15);color:var(--orange)}
.bt{background:rgba(57,208,184,.15);color:var(--teal)}

/* LEGENDE ZUR SKILL-TABELLE */
.legend{list-style:none;margin:0 0 1.6rem;padding:0;display:flex;flex-wrap:wrap;gap:.5rem 1.4rem}
.legend li{display:flex;align-items:center;gap:.5rem;font-family:var(--mono);
  font-size:.7rem;color:var(--dim)}

/* TECH STACK GRID */
.tech-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:.6rem;margin:1rem 0 2rem}
.tech-card{background:var(--bg);border:1px solid var(--border);border-radius:3px;padding:1.3rem;
  transition:background .2s;position:relative;overflow:hidden;cursor:default}
.tech-card:hover{background:#161B22}
.tech-card::after{content:"";position:absolute;bottom:0;left:0;right:0;height:1px;background:var(--blue);transform:scaleX(0);transform-origin:left;transition:transform .3s}
.tech-card:hover::after{transform:scaleX(1)}
.tech-icon{font-size:1.5rem;display:block;margin-bottom:.6rem}
.tech-name{font-family:var(--mono);font-size:.78rem;color:var(--text);margin-bottom:.3rem}
.tech-cat{font-family:var(--mono);font-size:.62rem;color:var(--dim)}

/* PROJECTS
   Echte Abstände statt des 1px-Hintergrund-Tricks: bei ungerader
   Kartenzahl blieb dort sonst eine leere Rasterzelle als grauer Block
   stehen, weil der Container-Hintergrund durchscheint. */
/* align-items:stretch (statt start): zwei Karten nebeneinander bekommen
   dieselbe Hoehe wie die groessere der beiden. proj-card ist dafuer eine
   Flex-Spalte — proj-media waechst in die zusaetzliche Hoehe hinein (das
   Bild wird groesser statt dass unten Luft bleibt), und bei Karten ohne
   Bild schiebt margin-top:auto stattdessen proj-links ganz nach unten. */
.proj-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;margin:1rem 0 2.4rem}
.proj-card{background:var(--bg);border:1px solid var(--border);border-radius:3px;padding:1.5rem;
  transition:background .2s,border-color .2s;position:relative;overflow:hidden;
  display:flex;flex-direction:column}
.proj-card:hover{background:#161B22;border-color:var(--border2)}
.proj-card::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,var(--blue),var(--teal));transform:scaleX(0);transform-origin:left;transition:transform .35s}
.proj-card:hover::before{transform:scaleX(1)}
.proj-type{font-family:var(--mono);font-size:.63rem;color:var(--dim);margin-bottom:.55rem}
.proj-t{font-family:var(--mono);font-size:1rem;font-weight:700;color:var(--text);margin-bottom:.6rem}

/* Abstract — die Kurzfassung, optisch vom Fliesstext abgesetzt */
.proj-abstract{font-family:var(--sans);font-size:.87rem;color:var(--text);line-height:1.7;
  margin-bottom:.9rem;padding-left:.9rem;border-left:2px solid var(--blue)}
.proj-abstract strong{font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--blue);font-weight:500}

.proj-body{font-family:var(--sans);font-size:.83rem;color:var(--muted);line-height:1.7;margin-bottom:.9rem}

/* Rolle, Team, Gelerntes — Beschriftung oben, Inhalt darunter. Bei
   laengeren Texten (z.B. "Gelernt" mit einer ganzen Liste) lief der Inhalt
   in der alten Zweispalten-Anordnung neben der Beschriftung her statt
   darunter; als Block untereinander liest sich beides sauberer. Eine
   Haarlinie darueber trennt die Details vom Fliesstext. */
.proj-meta{margin:0 0 .9rem;padding-top:.85rem;border-top:1px solid var(--border);
  display:flex;flex-direction:column;gap:.55rem}
.proj-meta>div{display:flex;flex-direction:column;gap:.15rem}
.proj-meta dt{font-family:var(--mono);font-size:.66rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--dim)}
.proj-meta dd{font-family:var(--sans);font-size:.82rem;color:var(--muted);line-height:1.6}
.proj-ul{list-style:none;margin:.35rem 0 0;padding:0;display:flex;flex-direction:column;gap:.3rem}
.proj-ul li{position:relative;padding-left:1rem}
.proj-ul li::before{content:"›";position:absolute;left:0;color:var(--blue)}

.proj-links{display:flex;flex-wrap:wrap;gap:.9rem;margin-bottom:.7rem;margin-top:auto}
.proj-link{display:inline-flex;align-items:center;gap:.35rem;font-family:var(--mono);font-size:.78rem;color:var(--blue);text-decoration:none}
.proj-link:hover{text-decoration:underline}
.chips{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}
.chip{font-family:var(--mono);font-size:.62rem;background:var(--border2);color:var(--muted);padding:.18rem .5rem;border:1px solid var(--border)}

/* ═══ MEDIEN: VIDEO, SCREENSHOTS, DOWNLOADS ═══ */
.proj-media:empty{display:none}
.proj-media{display:flex;flex-direction:column;justify-content:center;flex:1}

.media-figure{margin:1rem 0 .8rem}
.proj-video{width:100%;height:auto;display:block;background:#000;
  border:1px solid var(--border);border-radius:3px}
/* Aufnahme vom Handy: hochkant und schmal, sonst füllt sie die halbe Karte.
   Über die Höhe begrenzt statt über die Breite — sonst war die Karte mit
   dem Video doppelt so hoch wie ihre Nachbarn und das Raster wirkte
   ausgefranst. */
.proj-video.hoch{width:auto;height:340px;max-width:100%}
/* Zuschnitt-Rahmen: blendet den schwarzen Rand einer Emulator-Aufnahme aus,
   indem er das Video ueberlaufen laesst. Die Masse setzt app.js aus dem
   MEDIA-Eintrag, hier steht nur, was unabhaengig vom einzelnen Video gilt.
   Der Abspielknopf liegt darueber und verschwindet, sobald es laeuft. */
.video-player{display:inline-block;max-width:100%}
.video-crop{position:relative;overflow:hidden;background:#000;
  border:1px solid var(--border);border-radius:3px 3px 0 0;max-width:100%}
.video-crop .proj-video{border:0;border-radius:0;display:block}
.video-play{position:absolute;inset:0;width:100%;height:100%;padding:0;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  background:rgba(0,0,0,.35);border:0;color:#fff;font-size:1.6rem;
  transition:opacity .18s}
.video-play:hover{background:rgba(0,0,0,.2)}
.video-crop.laeuft .video-play{opacity:0;background:transparent}
.video-crop.laeuft .video-play:hover,
.video-play:focus-visible{opacity:1;background:rgba(0,0,0,.35)}
@media (prefers-reduced-motion:reduce){.video-play{transition:none}}

/* Eigene Steuerleiste, buendig unter dem Ausschnitt. Sie nimmt dessen
   Breite an, statt darueber hinauszustehen — ein Kasten, der breiter ist
   als das Bild darueber, sieht nach Versehen aus. Damit die vier Teile in
   die schmale Zeile passen, ist alles knapp gehalten: die Spurleiste darf
   schrumpfen, Knoepfe und Zeit bleiben. */
.video-bar{display:flex;align-items:center;gap:.3rem;
  padding:.3rem .35rem;background:var(--sb);
  border:1px solid var(--border);border-top:0;border-radius:0 0 3px 3px}
.vb-btn{flex:none;padding:.1rem .25rem;background:none;border:0;cursor:pointer;
  color:var(--text);font-size:.72rem;line-height:1;border-radius:2px}
.vb-btn:hover{color:var(--blue);background:var(--list-hover)}
.vb-time{flex:none;font-family:var(--mono);font-size:.6rem;color:var(--muted);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.vb-seek{flex:1;min-width:34px;height:3px;-webkit-appearance:none;appearance:none;
  background:var(--border);border-radius:2px;cursor:pointer}
.vb-seek::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:11px;height:11px;border-radius:50%;background:var(--blue);border:0;cursor:pointer}
.vb-seek::-moz-range-thumb{width:11px;height:11px;border-radius:50%;
  background:var(--blue);border:0;cursor:pointer}
.vb-seek:focus-visible{outline:2px solid var(--blue);outline-offset:3px}

/* Grossansicht: der Zuschnitt wird in app.js aufgehoben, hier bekommt das
   Video den ganzen Platz ueber der Leiste. Gilt fuer echtes Vollbild und
   fuer die Rueckfallebene, die sich nur ueber die Seite legt. */
.video-player.voll{display:flex;flex-direction:column;width:100%;height:100%;
  background:#000}
/* margin:auto zentriert den Ausschnitt im Platz, der ueber der Leiste
   uebrig bleibt — waagrecht und senkrecht zugleich. */
.video-player.voll .video-crop{margin:auto;border:0;border-radius:0}
.video-player.voll .video-bar{flex:none;border:0;border-radius:0}
/* Ueber allem der Seite, aber unter Login-Dialog (900) und Vollansicht der
   Screenshots (950) — die duerfen die Grossansicht ueberdecken. */
.video-player.fix{position:fixed;inset:0;z-index:890;max-width:none}
/* Solange die Grossansicht liegt, soll die Seite darunter nicht mitrollen. */
body.voll-offen{overflow:hidden}
.media-cap{font-family:var(--mono);font-size:.68rem;color:var(--dim);margin-top:.45rem;
  display:flex;gap:.6rem;align-items:baseline}
.media-dur{color:var(--blue)}

/* Screenshot-Streifen — anklickbar für die Vollansicht.
   auto-fit statt auto-fill: eine einzelne Kachel bekommt sonst nur ihre
   Mindestbreite und daneben bleibt eine leere Spalte reserviert — genau
   die Lücke, die bei Karten mit nur einem Bild am unruhigsten aussah.
   Mit auto-fit teilt sich die vorhandene Breite auf das auf, was wirklich
   da ist. */
.shot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));
  gap:.75rem;margin:.9rem 0 .8rem}
.shot{padding:0;border:1px solid var(--border);background:var(--sb);overflow:hidden;
  border-radius:3px;aspect-ratio:9/16;transition:border-color .18s,transform .18s}
.shot:hover{border-color:var(--blue);transform:translateY(-2px)}
.shot img{width:100%;height:100%;object-fit:cover;display:block}

/* Aufnahmen vom Rechner statt vom Handy: wenige, dafür breite Kacheln.
   In 110 Pixeln ist von einem Desktop nichts mehr zu erkennen. */
.shot-grid.quer{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.shot-grid.quer .shot{aspect-ratio:16/10}
.shot-grid.quer .shot img{object-fit:contain;background:#000}

/* Bilder mit sehr unterschiedlichen Seitenverhältnissen — etwa ein breiter
   Streifen neben einem fast quadratischen Fenster. Hier bekommt jede
   Kachel die Proportion ihres Bildes, statt beides in dieselbe Form zu
   schneiden oder mit schwarzen Balken aufzufüllen. */
.shot-grid.frei{grid-template-columns:repeat(auto-fit,minmax(230px,1fr));align-items:start}
.shot-grid.frei .shot{aspect-ratio:auto}
.shot-grid.frei .shot img{height:auto;object-fit:fill}

/* Karussell-Vorschau: ein Bild ist zu sehen, Pfeile und Punkte blaettern
   durch den Rest. Dieselben Format-Varianten wie beim Raster (quer/frei),
   aber ohne dessen Grundproblem — zwei unterschiedlich geformte Bilder
   nebeneinander gab es hier gar nicht erst, weil immer nur eins zu sehen
   ist. */
.shot-carousel{position:relative;margin:.9rem 0 .8rem}
.shot-carousel .shot{width:100%}
.shot-carousel.quer .shot{aspect-ratio:16/10}
.shot-carousel.quer .shot img{object-fit:contain;background:#000}
.shot-carousel.frei .shot{aspect-ratio:auto}
.shot-carousel.frei .shot img{height:auto;object-fit:fill}

.sc-nav{position:absolute;top:50%;transform:translateY(-50%);width:34px;height:34px;
  display:flex;align-items:center;justify-content:center;border-radius:50%;
  background:rgba(13,17,23,.72);border:1px solid var(--border);color:var(--text);
  font-size:1.15rem;line-height:1;cursor:pointer;transition:border-color .18s,background .18s;z-index:2}
.sc-nav:hover{border-color:var(--blue);background:#1C2333}
.sc-prev{left:.6rem}
.sc-next{right:.6rem}

.sc-dots{position:absolute;left:50%;bottom:.6rem;transform:translateX(-50%);
  display:flex;gap:.4rem;padding:.3rem .55rem;border-radius:12px;
  background:rgba(13,17,23,.55);z-index:2}
.sc-dot{width:6px;height:6px;padding:0;border:0;border-radius:50%;
  background:rgba(255,255,255,.45);cursor:pointer;transition:background .18s,transform .18s}
.sc-dot:hover{background:rgba(255,255,255,.75)}
.sc-dot.active{background:var(--blue);transform:scale(1.3)}

/* Leerer Rahmen, solange für ein Projekt noch kein Bild vorliegt. Er hält
   den Platz frei, damit die Karten gleich aufgebaut bleiben, und ist
   gestrichelt, damit niemand ihn für ein geladenes Bild hält. */
/* Eingebettete Seite als Vorschau — sieht aus wie ein Bildschirmfoto,
   ist aber die Seite selbst. Weiss hinterlegt, weil die alte Seite von
   einem weissen Hintergrund ausgeht und im dunklen Design sonst
   durchscheinen würde. */
.embed-frame{position:relative;aspect-ratio:16/10;overflow:hidden;background:#fff;
  border:1px solid var(--border);border-radius:3px;max-width:520px}
.embed-frame iframe{position:absolute;top:0;left:0;width:1280px;height:800px;border:0;
  transform-origin:top left;pointer-events:none}
.embed-open{display:inline-block;margin-top:.45rem;font-family:var(--mono);font-size:.7rem;
  color:var(--blue);text-decoration:none}
.embed-open:hover{text-decoration:underline}

/* Leere Rahmen bleiben kleiner als echte Bilder: ein Platzhalter in voller
   Kartenbreite zieht mehr Aufmerksamkeit auf sich als der Text daneben. */
.shot-grid.ph-grid.quer{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.shot.ph,.video-ph{display:flex;align-items:center;justify-content:center;
  border:1px dashed var(--border2);background:var(--sb);border-radius:3px;cursor:default}
.shot.ph:hover{transform:none;border-color:var(--border2)}
.shot.ph span,.video-ph span{font-family:var(--mono);font-size:.66rem;color:var(--dim)}
/* Hochkant, weil dort eine Bildschirmaufnahme vom Handy hinkommt. Über
   die ganze Kartenbreite wäre sie unangenehm hoch. */
.video-ph{aspect-ratio:9/16;max-width:230px;margin:.9rem 0 .8rem}

.dl-list{display:flex;flex-direction:column;gap:.4rem;margin:.9rem 0 .5rem}
.dl-item{display:flex;align-items:center;gap:.6rem;padding:.55rem .8rem;
  background:var(--sb);border:1px solid var(--border);border-radius:3px;
  font-family:var(--mono);font-size:.74rem;color:var(--text);text-decoration:none;
  transition:border-color .18s,background .18s}
.dl-item:hover{border-color:var(--blue);background:#1C2333}
.dl-ic{color:var(--blue);font-size:.9rem}
.dl-meta{margin-left:auto;color:var(--dim);font-size:.66rem;white-space:nowrap}

/* Vollansicht eines Screenshots */
.lightbox{z-index:950;background:rgba(13,17,23,.96);flex-direction:column;gap:1rem}
.lightbox.show{opacity:1;pointer-events:all}
/* Die Vollansicht war auf 900px gedeckelt. Auf einem breiten Monitor stand
   damit jedes Bild als Briefmarke zwischen zwei schwarzen Flächen — gerade
   bei den Aufnahmen vom URL-Shortener, die 2400 Pixel breit sind. Jetzt
   darf sie den Platz nehmen, den es gibt. */
.lb-figure{display:flex;flex-direction:column;align-items:center;gap:.8rem;
  max-width:94vw;max-height:90vh;margin:0}
.lb-img{max-width:100%;max-height:80vh;object-fit:contain;border:1px solid var(--border);border-radius:3px}
.lb-cap{font-family:var(--mono);font-size:.74rem;color:var(--muted);text-align:center;max-width:60ch}
.lb-nav,.lb-close{position:absolute;background:rgba(22,27,34,.9);border:1px solid var(--border);
  color:var(--text);cursor:pointer;font-family:var(--mono);border-radius:3px;
  transition:border-color .18s,background .18s}
.lb-nav{top:50%;transform:translateY(-50%);width:44px;height:64px;font-size:1.6rem;line-height:1}
.lb-prev{left:1.2rem}.lb-next{right:1.2rem}
.lb-close{top:1.2rem;right:1.2rem;width:40px;height:40px;font-size:1.3rem;line-height:1}
.lb-nav:hover,.lb-close:hover{border-color:var(--blue);background:#1C2333}
.lb-nav[hidden]{display:none}
.lb-count{position:absolute;bottom:1.4rem;font-family:var(--mono);font-size:.7rem;color:var(--dim)}

@media(max-width:820px){
  .lb-nav{width:38px;height:52px;font-size:1.3rem}
  .lb-prev{left:.5rem}.lb-next{right:.5rem}
  .shot-grid{grid-template-columns:repeat(auto-fit,minmax(88px,1fr))}
  /* Breite Aufnahmen auf dem Handy lieber untereinander als zwei
     unleserliche Spalten nebeneinander. */
  .shot-grid.quer,.shot-grid.frei{grid-template-columns:1fr}
}

/* TIMELINE */
.tl{position:relative;padding-left:1.6rem;margin:.5rem 0}
.tl::before{content:"";position:absolute;left:0;top:0;bottom:0;width:1px;background:var(--border)}
.tl-e{position:relative;margin-bottom:2rem;opacity:0;transform:translateX(-8px);transition:opacity .4s ease,transform .4s ease}
.tl-e.in{opacity:1;transform:none}
.tl-e::before{content:"";position:absolute;left:-1.65rem;top:.5rem;width:7px;height:7px;border-radius:50%;background:var(--bg);border:1.5px solid var(--blue);transition:background .2s,box-shadow .2s}
.tl-e:hover::before{background:var(--blue);box-shadow:0 0 8px rgba(88,166,255,.5)}
.tl-date{font-family:var(--mono);font-size:.7rem;color:var(--blue);margin-bottom:.2rem}
.tl-title{font-family:var(--mono);font-size:.88rem;color:var(--text);margin-bottom:.15rem}
.tl-sub{font-family:var(--sans);font-size:.8rem;color:var(--muted)}

/* IMPRESSUM */
.impressum{margin-top:2.5rem;padding:1.4rem 1.6rem;background:var(--sb);border:1px solid var(--border);
  border-radius:3px;max-width:640px}
.imp-list{display:flex;flex-direction:column;gap:.85rem}
.imp-list dt{font-family:var(--mono);font-size:.6rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--dim);margin-bottom:.2rem}
.imp-list dd{font-family:var(--mono);font-size:.76rem;color:var(--muted);line-height:1.65}
.imp-list a{color:var(--blue);text-decoration:none}
.imp-list a:hover{text-decoration:underline}

/* ═══ LOGIN MODAL ═══ */
.overlay{position:fixed;inset:0;z-index:900;background:rgba(13,17,23,.92);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .25s;padding:1rem}
.overlay.show{opacity:1;pointer-events:all}
.login-box{background:var(--sb);border:1px solid var(--border);width:100%;max-width:420px;animation:slideUp .25s ease}
@keyframes slideUp{from{transform:translateY(12px);opacity:0}to{transform:none;opacity:1}}
.login-head{background:var(--tb);border-bottom:1px solid var(--border);padding:.8rem 1.2rem;display:flex;align-items:center;gap:.5rem}
.lh-dots{display:flex;gap:5px;align-items:center}
.lh-dot{width:10px;height:10px;border-radius:50%;display:block;padding:0}
.lh-close{cursor:pointer;transition:filter .15s}
.lh-close:hover{filter:brightness(1.25)}
.login-title{font-family:var(--mono);font-size:.72rem;color:var(--muted);margin-left:.4rem}
.login-body{padding:1.8rem}
.login-cmt{font-family:var(--mono);font-size:.73rem;color:var(--dim);font-style:italic;margin-bottom:1.4rem;line-height:1.7}
.login-label{font-family:var(--mono);font-size:.72rem;color:var(--muted);display:block;margin-bottom:.4rem}
.login-iw{position:relative;margin-bottom:1.2rem}
.login-in{width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);font-family:var(--mono);font-size:.8rem;padding:.72rem 2.5rem .72rem .9rem;outline:none;transition:border-color .2s}
.login-in:focus{border-color:var(--blue)}
.login-tog{position:absolute;right:.75rem;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;color:var(--dim);font-size:.82rem;transition:color .15s}
.login-tog:hover{color:var(--muted)}
.login-btn{width:100%;background:var(--blue);color:#0D1117;border:none;font-family:var(--mono);font-size:.79rem;font-weight:700;padding:.75rem;cursor:pointer;transition:background .2s;letter-spacing:.06em}
.login-btn:hover:not(:disabled){background:#79C0FF}
.login-btn:disabled{background:var(--border);color:var(--dim);cursor:not-allowed}
.login-err{font-family:var(--mono);font-size:.7rem;color:var(--red);margin-top:.75rem;min-height:1rem;display:none;align-items:center;gap:.4rem}
.login-cancel{display:block;width:100%;margin-top:.85rem;text-align:center;font-family:var(--mono);font-size:.67rem;color:var(--dim);cursor:pointer;transition:color .15s}
.login-cancel:hover{color:var(--muted)}

/* NOTEN */
.noten-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.6rem;margin:1rem 0}
.note-card{background:var(--bg);border:1px solid var(--border);border-radius:3px;padding:1.3rem;transition:background .2s}
.note-card:hover{background:#161B22}
.note-fach{font-family:var(--mono);font-size:.68rem;color:var(--muted);margin-bottom:.4rem}
.note-val{font-family:var(--mono);font-size:2rem;font-weight:700;line-height:1}
.note-sem{font-family:var(--mono);font-size:.62rem;color:var(--dim);margin-top:.3rem}
.note-5{color:var(--green)}.note-4{color:var(--blue)}.note-3{color:var(--orange)}.note-2{color:var(--red)}

/* Kompetenznachweis je Karte: Vorschau und Download */
.note-akt{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.9rem;
  padding-top:.8rem;border-top:1px solid var(--border)}
.note-btn{font-family:var(--mono);font-size:.62rem;color:var(--muted);
  background:var(--sb);border:1px solid var(--border);border-radius:2px;
  padding:.32rem .6rem;cursor:pointer;display:inline-flex;align-items:center;
  gap:.35rem;transition:color .15s,background .15s,border-color .15s}
.note-btn:hover{color:var(--text);background:#161B22;border-color:var(--blue)}
.note-btn:disabled{opacity:.5;cursor:default}

/* Die Vorschau steht unter dem Raster und nicht in der Karte: in einem
   Drittel der Breite ist ein A4-Blatt nicht zu lesen. */
.zeugnis-vorschau{margin:1.2rem 0;border:1px solid var(--border);
  border-radius:3px;background:#161B22;overflow:hidden}
.zeugnis-kopf{display:flex;align-items:center;gap:.6rem;padding:.5rem .8rem;
  border-bottom:1px solid var(--border);background:var(--sb)}
.zeugnis-titel{font-family:var(--mono);font-size:.66rem;color:var(--muted)}
.zeugnis-link{margin-left:auto;font-family:var(--mono);font-size:.62rem;
  color:var(--muted);text-decoration:none;background:none;border:1px solid var(--border);
  border-radius:2px;padding:.24rem .55rem;cursor:pointer}
.zeugnis-link:hover{color:var(--text);border-color:var(--blue)}
.zeugnis-link+.zeugnis-link{margin-left:0}
.zeugnis-vorschau iframe{width:100%;height:min(78vh,760px);border:0;display:block;
  background:#fff}
.zeugnis-fehler{font-family:var(--mono);font-size:.72rem;color:var(--orange);
  padding:1.2rem;margin:0}
.noten-avg{font-family:var(--mono);font-size:.8rem;color:var(--muted);margin-top:1.2rem;padding:1rem 1.2rem;background:#161B22;border:1px solid var(--border);display:inline-flex;align-items:center;gap:1rem}
.noten-avg strong{color:var(--blue);font-size:1.1rem}

/* ═══ QUELLEN (Fuss der Startseite) ═══
   Bewusst ruhig: das ist eine Pflichtangabe, kein Inhalt, mit dem die
   Startseite endet. Zwei Spalten, damit die Liste nicht zur Kolonne wird. */
.quellen{margin-top:3rem;padding-top:1.4rem;border-top:1px solid var(--border)}
.quellen-h{font-family:var(--mono);font-size:.7rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--dim);font-weight:400;margin:0 0 .8rem}
.quellen-intro{font-family:var(--sans);font-size:.76rem;color:var(--muted);margin:0 0 .9rem}
.quellen-liste{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:.7rem 2rem;margin:0}
.q-e dt{font-family:var(--mono);font-size:.72rem;color:var(--text);margin-bottom:.15rem}
/* --muted statt --dim: bei .72rem kommt --dim nur auf einen Kontrast von
   3.5:1, das ist unter dem, was fuer Fliesstext verlangt wird. --muted
   liegt bei 6.2:1 — in hellen wie dunklen Designs. */
.q-e dd{font-family:var(--sans);font-size:.72rem;color:var(--muted);margin:0;line-height:1.55}
.q-e a{color:var(--blue);text-decoration:none}
.q-e a:hover{text-decoration:underline}
.quellen-fuss{font-family:var(--sans);font-size:.72rem;color:var(--muted);
  margin:1.2rem 0 0;line-height:1.6}
.quellen-fuss a{color:var(--blue);text-decoration:none}
.quellen-fuss a:hover{text-decoration:underline}

/* SCROLLBAR */
::-webkit-scrollbar{width:8px;height:8px}
::-webkit-scrollbar-track{background:var(--bg)}
::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
::-webkit-scrollbar-thumb:hover{background:var(--dim)}

/* REVEAL */
.reveal{opacity:0;transform:translateY(10px);transition:opacity .5s ease,transform .5s ease}
.reveal.in{opacity:1;transform:none}

/* ═══════════════════════════════════════════════════════════════
   RESPONSIVE

   Drei Stufen: Tablet (<=1024px) schmalerer Explorer, Handy (<=820px)
   Explorer als ausfahrbare Schublade, sehr schmal (<=480px) engere
   Abstände. Der Explorer verschwindet auf dem Handy bewusst nicht
   mehr ersatzlos — er ist die einzige Stelle, an der die gesperrten
   Dateien und die Ordnerstruktur sichtbar sind.
═══════════════════════════════════════════════════════════════ */

@media(max-width:1024px){
  .pane{grid-template-columns:200px 1fr}
  .tech-grid{grid-template-columns:repeat(3,1fr)}
  .code-content{padding:1.8rem 1.6rem}
}

@media(max-width:820px){
  .pane{grid-template-columns:1fr;position:relative}

  /* Explorer als Schublade über dem Inhalt */
  .sb-toggle{display:inline-flex;align-items:center}
  .sidebar{
    position:fixed;top:38px;bottom:0;left:0;width:min(280px,82vw);z-index:500;
    transform:translateX(-100%);visibility:hidden;
    box-shadow:3px 0 20px rgba(0,0,0,.55);
    transition:transform .22s ease,visibility 0s .22s;
  }
  .sidebar.open{transform:none;visibility:visible;transition:transform .22s ease,visibility 0s}
  .sb-backdrop{display:block;position:fixed;inset:38px 0 0 0;z-index:499;
    background:rgba(13,17,23,.65);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
  .sb-backdrop[hidden]{display:none}

  /* Grössere Trefferflächen für Finger */
  .tab{height:44px;padding:0 .9rem;font-size:.75rem}
  .tabbar{align-items:stretch}
  .ide{grid-template-rows:38px 44px 1fr}
  .tree-file{padding-top:.55rem;padding-bottom:.55rem}
  .tree-folder{padding-top:.55rem;padding-bottom:.55rem}
  .tab-x{opacity:1;padding:.35rem}

  .proj-grid,.noten-grid,.tech-grid,.quellen-liste{grid-template-columns:1fr}
  .fact-row{grid-template-columns:1fr 1fr}
  .cta{flex:1 1 auto;justify-content:center}
  .code-view{grid-template-columns:32px 1fr}
  .code-content{padding:1.5rem 1.2rem}
  /* Breite Tabellen bekommen ihren eigenen Scrollbereich, damit nicht
     die ganze Seite quer läuft */
  .ed-table{display:block;overflow-x:auto;max-width:100%}
  .hero-role{overflow-wrap:anywhere}
  body{overflow:auto}
  /* Auf dem Handy darf die Seite mitwachsen, statt alles in 100vh zu quetschen */
  .ide{overflow:auto;height:auto;min-height:100vh;min-height:100dvh}
}

@media(max-width:560px){
  /* Zeilennummern kosten hier nur Platz */
  .code-view{display:block}
  .tb-auth{font-size:.6rem;padding:.15rem .4rem}
  .tb-lang{font-size:.6rem;padding:.15rem .4rem}
  .cta-row{flex-direction:column}
  .cta{width:100%}
  .fact-row{grid-template-columns:1fr}
}

@media(max-width:480px){
  .login-body{padding:1.3rem}
  .code-content{padding:1.2rem .9rem}
  .breadcrumb{padding:.36rem 1rem}
  .hero-name{font-size:clamp(2rem,11vw,2.8rem)}
  .noten-grid{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  *{transition:none!important;animation:none!important}
  html{scroll-behavior:auto}
  /* Ohne Animation dürfen die Reveal-Elemente nicht unsichtbar bleiben */
  .reveal,.tl-e{opacity:1;transform:none}
}

/* ═══ DRUCK / "ALS PDF SPEICHERN" ═══
   Ein Ansteller druckt die Seite oder legt sie als PDF ab. Dann ist die
   Editor-Kulisse nur Ballast: Fensterrahmen, Tabs, Explorer und Statusleiste
   fliegen raus, alle Panels werden untereinander auf Weiss ausgegeben. */
@media print{
  @page{margin:1.5cm}
  #splash,.skip-link,.titlebar,.tabbar,.sidebar,.sb-backdrop,.breadcrumb,
  .overlay,.cta-row,.sb-bottom,.editor-empty{display:none!important}

  html,body,.ide,.pane,.editor,.editor-scroll{
    height:auto!important;overflow:visible!important;display:block!important;background:#fff!important}
  body{color:#111;font-size:11pt}
  .ide{opacity:1!important}

  /* Auf Papier gibt es nichts zum Aufklappen: das Eingeklappte wird
     ausgedruckt, der Schalter dazu verschwindet. */
  .fold-body[hidden]{display:block!important}
  .fold-btn{display:none!important}

  /* Alle Tabs untereinander drucken, nicht nur den aktiven */
  .editor-panel{display:block!important;page-break-after:always;padding-top:.4cm}
  .editor-panel:last-child{page-break-after:auto}
  .code-view{display:block!important}
  .code-content{padding:0!important;line-height:1.5}
  .reveal,.tl-e{opacity:1!important;transform:none!important}

  /* Farben zuerst flächendeckend auf dunkel ziehen. Einzelne Regeln wie
     .fact dd sind spezifischer als jeder Container-Override und würden
     sonst hell auf weiss drucken — also erst alles, dann die Akzente. */
  .editor-panel,.editor-panel *:not([class*="devicon"]){color:#222!important}
  .ed-h1,.hero-name,.hero-name .acc,.proj-t,.tech-name,.tl-title,
  .fact dd,.note-val,.noten-avg strong,.kk-t{color:#000!important}
  .cmt,.proj-type,.tech-cat,.fact dt,.note-sem,.note-fach,
  .cb-lang,.tl-date,.tl-sub,.prose,.proj-body,.hero-role{color:#555!important}

  .ed-h1{border-bottom:1px solid #999;font-size:15pt}
  .ed-h2{font-size:12pt}
  .ed-h2::before{content:""}
  .hero-name{font-size:26pt}
  .blink{display:none}

  .cb,.tech-card,.proj-card,.note-card,.kk,.fact,.noten-avg{
    background:#fff!important;border:1px solid #bbb!important}
  .cb::before,.proj-card::before,.tech-card::after{display:none}
  .proj-grid,.tech-grid,.noten-grid,.fact-row{background:#fff!important;gap:.3cm;border:none}
  .proj-card,.tech-card,.note-card,.cb,.fact{page-break-inside:avoid}
  /* Schaltflaechen und die eingebettete Vorschau haben auf Papier nichts
     verloren — der Nachweis selbst wird als eigene Datei mitgeschickt. */
  .note-akt,.zeugnis-vorschau{display:none!important}
  .chip,.badge{border:1px solid #bbb;background:#fff!important}
  .ed-table td,.ed-table th{border-color:#ccc}

  /* Skills: der farbige Punkt ueberlebt den Druck nicht (Hintergruende
     werden weggelassen). Also faellt er weg und die Einstufung, die am
     Bildschirm nur Screenreader hoeren, wird sichtbar gedruckt. */
  .sk-list{display:block!important}
  .sk{page-break-inside:avoid;border-bottom:1px solid #ddd}
  .sk-dot{display:none!important}
  .sk-chip{color:#555!important;border:1px solid #bbb!important;background:#fff!important}
  .sk .vh,.sk-next .vh{position:static;width:auto;height:auto;margin:0;
    overflow:visible;clip:auto;clip-path:none;white-space:normal;
    font-size:9pt;color:#555!important}

  /* Ziel-URLs sichtbar machen — im Ausdruck ist ein Link sonst wertlos */
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:8pt;color:#666;word-break:break-all}
}

/* ═══ DEVICON — original technology icons ═══ */
.tech-icon i, .tech-icon .devicon{font-size:1.8rem;line-height:1}
.skill-ic{font-size:.95rem;margin-right:.45rem;vertical-align:-2px}
.skill-ic.colored{filter:none}
.tech-card .tech-icon{display:flex;align-items:center;justify-content:center;height:1.8rem;margin-bottom:.7rem}
