/* Farben: nur dunkel */
:root {
  --bg: #131722;
  --fg: #ecebe6;
  --muted: #8d919c;
  --line: #262b38;
  --board: #181d2a;
  --accent: #7aa2f7;
  --soft: #d1d4dc;
  --green: #4ade80;
  --yellow: #facc15;
  --red: #f7768e;
  color-scheme: dark;
}

/* Schriften selbst gehostet, variable Fassungen, nur der Zeichensatz „latin“ von Google Fonts.
   font-display: block mit preload im HTML: Der Text erscheint erst mit der richtigen Schrift,
   statt kurz in einer Ersatzschrift zu stehen und dann zu springen. */
@font-face {
  font-family: "Inter";
  src: url("fonts/inter.woff2") format("woff2");
  font-weight: 400 600;
  font-display: block;
}

@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-mono.woff2") format("woff2");
  font-weight: 400 700;
  font-display: block;
}

@font-face {
  font-family: "Caveat";
  src: url("fonts/caveat.woff2") format("woff2");
  font-weight: 600 700;
  font-display: block;
}

:root {
  --hand: "Caveat", "Segoe Print", cursive;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  line-height: 1.5;
}

a { color: inherit; }


.live { display: inline-flex; align-items: center; gap: 9px; }

.dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
}

/* Grün und sanft pulsierend: die Seite ist live, ein Projekt läuft. Der Puls ist ein
   Ring als eigene Ebene, der wächst und verblasst, nur über transform und opacity. */
.dot-live,
.dot-run { position: relative; background: var(--green); }

.dot-live::after,
.dot-run::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: rgba(74, 222, 128, 0.6);
  animation: live-pulse 2s ease-out infinite;
}

@keyframes live-pulse {
  from { transform: scale(1); opacity: 1; }
  to { transform: scale(3); opacity: 0; }
}

/* Blau und ruhig: Datum der letzten Änderung */
.dot-update { background: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .dot-live,
  .dot-run { box-shadow: 0 0 6px var(--green); }
  .dot-live::after,
  .dot-run::after { animation: none; opacity: 0; }
}

/* Grundraster: am Handy untereinander, am Desktop zwei Spalten */
.layout {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 16px;
}

/* Hero */
.intro { padding: 40px 0 32px; }

/* Terminal-Fenster im Hero */
.term {
  overflow: hidden;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--board);
  font-family: var(--mono);
  font-size: 0.85rem;
}

.term-bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
}

.dots { display: flex; gap: 6px; }

.dots i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

/* Fensterknöpfe wie bei macOS */
.dots i:nth-child(1) { background: #ff5f57; }
.dots i:nth-child(2) { background: #febc2e; }
.dots i:nth-child(3) { background: #28c840; }

/* Der Fenstername ist hervorgehoben, nicht grau */
.term-title { font-size: 0.72rem; color: var(--fg); }

.term-replay {
  justify-self: end;
  padding: 0;
  background: none;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: 0.68rem;
  text-transform: uppercase;
  cursor: pointer;
}

.term-replay:hover,
.term-replay:focus-visible { color: var(--accent); }

/* Ausgaben stehen mittig zwischen Titelleiste und Eingabezeile */
.term-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 190px;
  padding: 14px 14px 15.5px;
}

.term-body .t-line:last-child { margin-bottom: 0; }

.t-line { margin: 0 0 12px; }
.t-line[hidden] { display: none; }

.t-cmd { color: var(--fg); }

.pr { color: var(--accent); }

.t-spin { color: var(--accent); }
.t-spin .star { display: inline-block; animation: spin 1.2s linear infinite; }
.t-muted { color: var(--muted); }

@keyframes spin { to { transform: rotate(360deg); } }

.t-out {
  display: flex;
  align-items: center;
  gap: 10px;
  animation: rise 0.45s ease-out;
}

@keyframes rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.bullet { color: var(--accent); font-weight: 700; flex: none; }
.t-out:not(.t-name) { align-items: flex-start; }

/* Die Handschrift sitzt tief in ihrer Zeile. Das > rückt um 11 % der
   Schriftgröße nach unten, dann steht es mittig zu den Buchstaben. */
.t-name { --ns: clamp(3.4rem, 15vw, 4.6rem); }

/* Über der Handschrift bleibt in ihrer Zeile viel Leerraum. Der negative
   Abstand gleicht ihn aus: whoami steht genauso weit über den Buchstaben
   wie die Zeile darunter. Werte am gerenderten Text gemessen, 375 und 1280 px. */
.t-name { margin-top: calc(4.5px - var(--ns) * 0.33); }
.t-name .bullet { transform: translateY(calc(var(--ns) * 0.11)); }

.name {
  margin: 0;
  font-family: var(--hand);
  font-size: var(--ns);
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
}

.term-input {
  margin: 0 14px;
  padding: 8px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.caret {
  display: inline-block;
  width: 0.55em;
  height: 1.1em;
  margin-left: 2px;
  vertical-align: -0.2em;
  background: var(--fg);
  animation: blink 1s steps(1) infinite;
}

.term.typing .caret { animation: none; }

@keyframes blink { 50% { opacity: 0; } }

.tool { color: var(--accent); font-weight: 700; }

/* Statuszeile: Modell links, Verbrauch rechts */
.term-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 6px 12px;
  padding: 8px 14px 10px;
  font-size: 0.7rem;
  color: var(--muted);
}

.model { color: var(--fg); }

.usage { display: flex; gap: 10px; }
.usage > span { display: inline-flex; align-items: center; gap: 5px; color: var(--soft); }

/* Verbrauchsbalken: schmal, heller Grund statt Schraffur.
   Ab 80 % Gelb wie die Warnung in Claude Code. */
.meter {
  display: inline-block;
  width: 30px;
  height: 6px;
  border-radius: 2px;
  background:
    linear-gradient(var(--accent), var(--accent)) left / var(--v) 100% no-repeat,
    rgba(236, 235, 230, 0.12);
  transition: background-size 0.3s;
}

.meter.warn {
  background:
    linear-gradient(#f0c22a, #f0c22a) left / var(--v) 100% no-repeat,
    rgba(236, 235, 230, 0.12);
}

@media (prefers-reduced-motion: reduce) {
  .caret, .t-spin .star, .t-out { animation: none; }
}

/* Metazeilen: Etikett oben, Wert darunter, ohne Zeichen davor.
   STATUS und AKTUALISIERT nebeneinander. */
.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 40px;
  margin: 24px 0 28px;
}
.meta dt,
.tree-meta > div > dt,
.board h3,
.eq {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}

.meta dd {
  margin: 4px 0 0;
  font-size: 0.9rem;
}

/* Jahr als Tagesraster wie bei GitHub: 7 Zeilen für die Wochentage, eine Spalte
   je Woche. Vergangene Tage blau, heute hell und blinkend, der Rest in Linienfarbe.
   Die Kästchen wachsen mit der Spaltenbreite. */
.year {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--fg);
}

/* Mittig über dem Raster und im Stil der Etiketten wie STATUS. Links bündig
   wirkte es neben den fehlenden Kästchen der ersten Woche schief. */
.year-text {
  display: block;
  margin-bottom: 8px;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.days {
  display: grid;
  grid-template-rows: repeat(7, auto);
  grid-template-columns: repeat(53, 1fr); /* JavaScript setzt 53 oder 54, je nach Jahr */
  grid-auto-flow: column;
  gap: 2px;
}

.days i { aspect-ratio: 1; border-radius: 1px; background: var(--line); }
.days i.off { background: none; }
.days i.past { background: var(--accent); opacity: 0.8; }
.days i.today { background: var(--fg); animation: today-blink 1.6s ease-in-out infinite; }

@keyframes today-blink { 50% { opacity: 0.3; } }

@media (prefers-reduced-motion: reduce) {
  .days i.today { animation: none; }
}

/* Szene unter der Jahresleiste: das Maskottchen vibt zur Musik */
.scene {
  position: relative;
  height: 190px;
  margin-top: 24px;
  overflow: hidden;
  font-family: var(--mono);
  /* Die Szene läuft an den Seiten aus wie die Linien der Titel */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
}

.vibe {
  position: absolute;
  left: 50%;
  bottom: 8px;
  width: 120px;
  margin-left: -60px;
}

/* Nicken im Takt, etwa 90 BPM: ein Nicken je Schlag, links und rechts im Wechsel.
   Beim Schlag staucht es leicht, dazwischen streckt es sich. */
.mascot {
  display: block;
  width: 120px;
  height: auto;
  transform-origin: 50% 100%;
  animation: groove 1.333s ease-in-out infinite;
}

@keyframes groove {
  0%   { transform: rotate(-4deg) scale(1.02, 0.97); }
  25%  { transform: rotate(0deg) translateY(-6px) scale(0.99, 1.02); }
  50%  { transform: rotate(4deg) scale(1.02, 0.97); }
  75%  { transform: rotate(0deg) translateY(-6px) scale(0.99, 1.02); }
  100% { transform: rotate(-4deg) scale(1.02, 0.97); }
}

/* Noten steigen versetzt auf und verblassen */
.note {
  position: absolute;
  top: 10px;
  font-size: 1.1rem;
  color: var(--accent);
  opacity: 0;
  animation: note-up 4s ease-out infinite;
}

/* Sechs Noten, alle 0,67 s eine neue, also im Takt. Jede steigt etwas anders:
   Größe, Startpunkt und Richtung (--dx) wechseln, links driften sie nach links. */
.n1 { left: 100px; }
.n2 { left: 118px; animation-delay: 1.33s; font-size: 0.95rem; }
.n3 { left: -6px;  animation-delay: 2.67s; --dx: -14px; }
.n4 { left: 14px;  animation-delay: 0.67s; font-size: 0.9rem; --dx: -10px; }
.n5 { left: 86px;  animation-delay: 2s;    font-size: 0.85rem; --dx: 18px; }
.n6 { left: 50px;  animation-delay: 3.33s; font-size: 1rem; --dx: 6px; }

@keyframes note-up {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0deg); }
  15%  { opacity: 0.9; }
  100% { opacity: 0; transform: translate(var(--dx, 14px), -70px) rotate(12deg); }
}

/* Meer aus ≈. Jede Zeile wiederholt ihr Muster, gleitet um die
   halbe Länge weiter und setzt dann nahtlos wieder an. */
.tide {
  position: absolute;
  left: 0;
  width: max-content;
  white-space: pre;
  color: var(--accent);
  animation: flow linear infinite;
}

.tide::before { content: var(--wave); }

/* Fünf Zeilen, hinten locker und blass, vorne dicht und kräftig. Die hinteren drei
   liegen hinter dem Maskottchen, die vorderen zwei davor: so steht es im Wasser. */
.t1 {
  bottom: 64px;
  font-size: 0.65rem;
  opacity: 0.18;
  animation-duration: 34s;
  animation-direction: reverse;
  --wave: "  ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈     ≈    ≈  ≈     ≈   ";
}

.t2 {
  bottom: 48px;
  font-size: 0.72rem;
  opacity: 0.3;
  animation-duration: 26s;
  animation-direction: normal;
  --wave: "≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ≈ ≈≈   ≈  ≈≈≈  ≈   ";
}

.t3 {
  bottom: 32px;
  font-size: 0.78rem;
  opacity: 0.45;
  animation-duration: 20s;
  animation-direction: reverse;
  --wave: "≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ≈≈≈ ≈ ≈≈  ≈≈≈≈ ≈≈ ";
}

.t4 {
  bottom: 16px;
  font-size: 0.85rem;
  opacity: 0.65;
  animation-duration: 15s;
  animation-direction: normal;
  --wave: "≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ≈≈≈≈ ≈≈≈ ≈≈≈≈≈ ≈≈ ";
}

.t5 {
  bottom: 0;
  font-size: 0.92rem;
  opacity: 0.85;
  animation-duration: 11s;
  animation-direction: reverse;
  --wave: "≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ≈≈≈≈≈≈ ≈≈≈≈≈ ≈≈≈≈≈≈≈ ";
}

@keyframes flow {
  to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .mascot, .note, .tide { animation: none; }
  .note { display: none; }
}
/* Mini-Player unter der Szene, so breit wie die Spalte. Der Fortschritt aus ≈
   füllt den freien Platz zwischen Dateiname und Zeit. */
.player {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--muted);
  white-space: nowrap;
}

.progress { flex: 1; min-width: 0; overflow: hidden; }

.play {
  flex: none;
  width: 2.4ch;
  padding: 0;
  background: none;
  border: 0;
  color: var(--accent);
  font: inherit;
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
}

.play:hover,
.play:focus-visible { color: var(--fg); }

#prog-fill { color: var(--accent); }
#prog-rest { color: var(--line); }

.time { font-variant-numeric: tabular-nums; }

/* Dateiname und Zeit hervorgehoben, nicht grau */
.track,
.time { color: var(--fg); }

/* Lautstärke: fünf Balken, von links nach rechts höher, aktive Stufen blau */
.vol {
  display: inline-flex;
  flex: none;
  align-items: flex-end;
  gap: 3px;
  height: 16px;
  margin-left: 2px;
}

.vol button {
  width: 4px;
  padding: 0;
  border: 0;
  border-radius: 1px;
  background: var(--line);
  cursor: pointer;
}

.vol button:nth-child(1) { height: 5px; }
.vol button:nth-child(2) { height: 8px; }
.vol button:nth-child(3) { height: 11px; }
.vol button:nth-child(4) { height: 13px; }
.vol button:nth-child(5) { height: 16px; }

.vol[hidden] { display: none; }

.vol button.on { background: var(--accent); }
.vol button:hover,
.vol button:focus-visible { background: var(--fg); }

/* Größere Klickfläche, ohne die Balken optisch zu verbreitern */
.vol button { position: relative; }
.vol button::after { content: ""; position: absolute; inset: -6px -1px; }


/* Bereiche */
section { padding: 0 0 32px; }

/* Tafeln */
.boards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}

.board {
  display: flex;
  flex-direction: column;
  padding: 18px;
  background: var(--board);
  border: 1px solid var(--line);
  border-radius: 8px;
}

/* Titel mittig über die volle Breite, links und rechts eine blaue Linie,
   die nach außen ausläuft und den Blick zur Mitte lenkt */
.board h3,
.eq {
  display: flex;
  align-items: center;
  gap: 1.5ch;
  margin: 0 0 14px;
  font-size: 0.9rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--fg);
  white-space: nowrap;
}

/* Die Linien sind feine Wellen, gezeichnet als kleines SVG und wiederholt */
.board h3::before,
.board h3::after,
.eq::before,
.eq::after {
  content: "";
  flex: 1;
  height: 8px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='8' viewBox='0 0 24 8'%3E%3Cpath d='M0 4 Q6 0.5 12 4 T24 4' fill='none' stroke='%237aa2f7' stroke-width='1.2'/%3E%3C/svg%3E") repeat-x center / 24px 8px;
}

/* Nach außen laufen die Wellen aus */
.board h3::before,
.eq::before {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000);
  mask-image: linear-gradient(90deg, transparent, #000);
  background-position: right center;
}

.board h3::after,
.eq::after {
  -webkit-mask-image: linear-gradient(90deg, #000, transparent);
  mask-image: linear-gradient(90deg, #000, transparent);
  background-position: left center;
}

.eq { margin-bottom: 4px; }

/* Ohne Rahmen: die Terminals darin sind selbst die Kästen */
.board-free {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}

/* Titel über frei stehenden Terminals (WILLKOMMEN, HARDWARE) */
.board-free h3,
.eq-intro { margin: 6px 0 14px; }

/* PROJEKTE steht oben rechts, auf derselben Höhe wie WILLKOMMEN */
#projekte > .eq { margin-top: 6px; }

.tree-meta { margin: 0; }

.tree-meta > div + div { margin-top: 12px; }

.tree-meta > div > dt { color: var(--fg); text-transform: none; letter-spacing: 0; font-size: 0.85rem; font-weight: 700; }

/* Rangfolge auf der ganzen Seite: Weiß ist Information, Grau ist alles drumherum
   (Etiketten, Kommentare, Linien, Verbindungswörter), fett nur für Überschriften,
   Blau für Marke und Bedienung. Die Einträge sind die Information, also weiß. */
.tree-meta > div > dd {
  margin: 2px 0 0;
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--fg);
}

/* Verbindungswörter wie „via“, „von“, „per“ treten zurück */
.conn { color: var(--muted); }

/* Baum aus echten Linien statt Zeichen: eine durchgehende senkrechte Linie,
   je Eintrag ein waagrechter Abzweig, beim letzten endet die Linie am Abzweig.
   Die Einträge haben keinen Außenabstand, sonst reißt die Linie. */
.tree-meta > div > dd {
  position: relative;
  margin: 0;
  padding: 3px 0 0 1.4em;
}

.tree-meta > div > dd::before,
.tree-meta > div > dd::after {
  content: "";
  position: absolute;
  left: 0.4em;
  border-color: var(--muted);
  opacity: 0.75;
}

/* Senkrecht: über die volle Höhe, beim letzten Eintrag nur bis zum Abzweig */
.tree-meta > div > dd::before { top: 0; bottom: 0; border-left: 1px solid; }

.tree-meta > div > dd:last-of-type::before { bottom: auto; height: calc(3px + 0.75em); }

/* Waagrecht: der Abzweig zur Textzeile */
.tree-meta > div > dd::after { top: calc(3px + 0.75em); width: 0.6em; border-top: 1px solid; }

/* Fußzeile einer Tafel: grauer Kommentar unten, abgetrennt durch eine gestrichelte
   Linie. Sitzt immer am unteren Rand, auch wenn die Tafel durch ihre Nachbarn höher ist. */
.board-foot {
  margin: auto 0 0;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--muted);
  /* Mittig wie der Titel: Die Tafel beginnt und endet auf derselben Achse */
  text-align: center;
}

.board > :has(+ .board-foot) { margin-bottom: 16px; }

/* Siegerpodest: Platz 1 in der Mitte und am höchsten, 2 links, 3 rechts.
   Gold, Silber und Bronze gibt es nur hier. Namen zweizeilig: Hersteller grau, Modell weiß. */
.podium {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 10px;
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
  border-bottom: 1px solid var(--line);
}

.podium li {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 82px;
  text-align: center;
  white-space: nowrap;
}

.podium .p1 { order: 2; }
.podium .p2 { order: 1; }
.podium .p3 { order: 3; }

.pod-name {
  margin-bottom: 6px;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--fg);
}

.pod-maker { display: block; color: var(--muted); }

.pod-step {
  align-self: stretch;
  padding-top: 3px;
  border-radius: 3px 3px 0 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 700;
}

.p1 .pod-step { height: 100px; background: linear-gradient(#e8c25b, #b8902f); color: #3a2a05; }
.p2 .pod-step { height: 72px; background: linear-gradient(#cfd4dc, #959ba6); color: #2a2e36; }
.p3 .pod-step { height: 50px; background: linear-gradient(#d0915a, #9c6233); color: #3a1d07; }

/* Platzhalter: gestrichelt, ohne Fläche, mittig das Logo in hellem Blaugrau und „soon…“.
   Nur sichtbar, wenn die Tafeln in Spalten stehen und sonst eine Lücke bliebe. */
.board-placeholder {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: none;
  border-style: dashed;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--muted);
}

.board-placeholder img { width: 180px; height: auto; opacity: 0.85; }
.board-placeholder p { margin: 0; font-size: 0.9rem; letter-spacing: 0.2em; }

/* KOSTEN: Baum wie in den anderen Tafeln, der Preis steht rechtsbündig in derselben Zeile */
.prices > div > dd {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}

.price { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* fastfetch: ein statisches Terminal je Rechner, der Name steht in der Titelleiste */
.ff-list > div + div { margin-top: 24px; }

.ff-term { background: var(--board); }

/* Einklappen ohne JavaScript: jede Titelleiste klappt ihr Terminal ein. Standard ist offen. */
.ff-term > summary {
  list-style: none;
  cursor: pointer;
}

.ff-term > summary::-webkit-details-marker { display: none; }

.fold-mark {
  font-family: var(--mono);
  font-weight: 400;
  color: var(--muted);
}

.ff-term > summary .fold-mark { justify-self: end; font-size: 0.68rem; }
.ff-term[open] > summary .fold-mark::before { content: "[−]"; }
.ff-term:not([open]) > summary .fold-mark::before { content: "[+]"; }

.ff-term > summary:hover .fold-mark,
.ff-term > summary:focus-visible .fold-mark { color: var(--accent); }

/* Eingeklappt bleibt nur die Titelleiste, ohne Linie darunter */
.ff-term:not([open]) > summary { border-bottom: 0; }

.ff-body {
  container-type: inline-size;
  padding: 14px 14px 16px;
  font-size: 0.72rem;
  line-height: 1.6;
}

/* Farben wie in der echten Ausgabe: Logo und Titel, dazu die Feldnamen */
.ff-windows { --logo: var(--accent); --key: var(--yellow); }
.ff-debian { --logo: var(--red); --key: var(--red); }
.ff-ubuntu { --logo: var(--red); --key: var(--red); }

/* Raster: Befehl über dem Logo, der Kommentar bündig über den Feldern.
   Am Handy fällt das Logo weg, die Felder nehmen die volle Breite. */
.ff-out {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "cmd note"
    "info info";
  column-gap: 2ch;
  align-items: start;
}

/* Logo-Spalte überall so breit wie das breiteste Logo (Ubuntu, 43 Zeichen
   in 0.75 der Schriftgröße). So beginnen die Felder in allen Terminals gleich. */
@container (min-width: 600px) {
  .ff-out {
    grid-template-columns: calc(43ch * 0.75) 1fr;
    grid-template-areas:
      "cmd note"
      "logo info";
    column-gap: 16px;
  }
}

.ff-cmd { grid-area: cmd; margin: 0 0 10px; white-space: nowrap; }

/* Shell-Kommentar: sagt, was der Rechner ist */
.ff-note { grid-area: note; margin: 0 0 10px; color: var(--muted); }

/* Am Handy fehlt der Platz für Logo und Felder nebeneinander */
.ff-logo {
  grid-area: logo;
  justify-self: center;
  display: none;
  margin: 0;
  font-family: var(--mono);
  font-size: 0.75em;
  line-height: 1.25;
  color: var(--logo);
}

@container (min-width: 600px) {
  .ff-logo { display: block; }
}

.ff-info { grid-area: info; min-width: 0; }

.ff-title { margin: 0; }
.ff-title b { font-weight: 400; color: var(--logo); }

.ff-sep { margin: 0; }

.ff-info dl { margin: 0; }

.ff-info dl div {
  display: grid;
  grid-template-columns: 8ch 1fr;
}

@container (min-width: 600px) {
  .ff-info dl div { grid-template-columns: 10ch 1fr; }
}

.ff-info dt { color: var(--key); }
.ff-info dd { margin: 0; overflow-wrap: anywhere; }

/* Am Handy bricht die Zeile vor dem |, nicht mitten in „CPU-Threads“ */
.ff-info .nw { white-space: nowrap; }

/* Projektliste mit aufklappbaren Zeilen */
.list details { border-bottom: 1px solid var(--line); }

/* Projekte eingeklappt: Punkt, Name, [+] rechts, darunter die Kurzbeschreibung.
   Am Handy darf sie in zwei Zeilen umbrechen. */
.list summary {
  display: grid;
  grid-template-columns: 16px 1fr auto;
  align-items: baseline;
  gap: 3px 10px;
  padding: 14px 0;
  cursor: pointer;
  list-style: none;
}

.list summary::-webkit-details-marker { display: none; }

.list summary .dot { align-self: center; }

/* Status als Punkt wie STATUS links: „läuft“ pulsiert grün wie „live“ (oben definiert),
   „in arbeit“ ruht gelb */
.dot-work { background: var(--yellow); }

.title {
  font-size: 1.05rem;
  font-weight: 600;
}

.list summary:hover .title,
.list summary:focus-visible .title { color: var(--accent); }

.list .fold-mark { font-size: 0.8rem; }
.list details[open] .fold-mark::before { content: "[−]"; }
.list details:not([open]) .fold-mark::before { content: "[+]"; }

.list summary:hover .fold-mark,
.list summary:focus-visible .fold-mark { color: var(--accent); }

.desc {
  grid-column: 2;
  font-size: 0.85rem;
  color: var(--soft);
}

/* Projekt aufgeklappt: oben der Weg als Kette, darunter Gruppen wie in den Tafeln.
   Die Kette zeigt Harness, Rechner und Dienste.
   Container: Ob die Kette waagrecht passt, hängt an der Breite der Spalte, nicht des Fensters. */
.proj {
  container: proj / inline-size;
  padding: 8px 0 22px;
}

/* Ein offenes Projekt bekommt einen dezenten gestrichelten Rahmen um Kopf und Inhalt,
   wie die Rahmen in der Kette, nur ohne Farbe. Es wird zur aufgeklappten Tafel.
   Verworfen: Rahmen nur um den Inhalt, weil Kopf und Inhalt dann wie zwei Teile wirken. */
.list details[open] {
  margin: 8px 0;
  padding: 0 14px;
  border: 1px dashed var(--line);
  border-radius: 10px;
}

/* Die Trennlinie einer geschlossenen Zeile direkt über einem offenen Projekt entfällt,
   der Rahmen ersetzt sie. Ein offenes Projekt behält seinen Rahmen ganz. */
.list details:not([open]):has(+ details[open]) { border-bottom-color: transparent; }

/* Ab Tablet ragt der Rahmen seitlich über die Liste hinaus.
   So bleiben Punkt und Name bündig mit den geschlossenen Zeilen. */
@media (min-width: 720px) {
  .list details[open] { margin: 8px -18px; padding: 0 18px; }
  .proj { padding: 8px 0 24px 26px; }
}

/* Die Kette ist bewusst bunt und sticht aus der ruhigen Seite heraus. Jeder Knoten nimmt
   seine Farbe aus einem Verlauf von Cyan über Violett nach Pink, je nach Position --n.
   --len steht am .flow und ist die Zahl der Schritte, also Knoten minus eins. */
.flow {
  --from: #7dcfff;
  --to: #f7768e;
  --mid: color-mix(in oklch, var(--from), var(--to));
}

/* Am Handy senkrecht und mittig. Mittig zur ganzen Zeile, nicht zum eingerückten Inhalt. */
.flow,
.host {
  display: flex;
  flex-direction: column;
  align-items: center;
}


.node {
  --c: color-mix(in oklch, var(--to) calc(var(--n, 0) / var(--len, 1) * 100%), var(--from));
  position: relative;
  /* Senkrecht gleich breit, damit die Säule ruhig wirkt */
  min-width: 9.5rem;
  padding: 7px 12px;
  background: color-mix(in srgb, var(--c) 16%, var(--board));
  border: 1px solid color-mix(in srgb, var(--c) 75%, var(--line));
  border-radius: 6px;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--fg);
  text-align: center;
  white-space: nowrap;
}

/* Nebeninfo im Knoten: klein, in der Farbe des Knotens */
.node small { display: block; font-size: 0.66rem; color: color-mix(in srgb, var(--c) 80%, var(--muted)); }

/* Das Ergebnis oder der Kern der Kette: Rand im ganzen Verlauf, dazu ein Schein */
.node-hot {
  background:
    linear-gradient(color-mix(in srgb, var(--c) 12%, var(--board)), color-mix(in srgb, var(--c) 12%, var(--board))) padding-box,
    linear-gradient(135deg, var(--from), var(--to)) border-box;
  border-color: transparent;
  box-shadow: 0 0 18px color-mix(in srgb, var(--c) 30%, transparent);
}

/* Bewegung: Beim Aufklappen erscheinen die Knoten nacheinander. Danach leuchtet jeder
   Knoten in seiner Farbe auf, wenn das Datenpaket ankommt.
   Das Paket auf Leitung i kommt bei i × 0,5 s + 0,45 s an, also bei n × 0,5 s − 0,05 s.
   Alle Bewegungen ändern nur transform und opacity. Das erledigt die Grafikkarte,
   der Browser muss weder Layout rechnen noch neu malen. */
.node { animation: node-in 0.45s ease-out calc(var(--n, 0) * 70ms) both; }

.host { animation: node-in 0.6s ease-out both; }

@keyframes node-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

/* Das Aufleuchten ist eine eigene Ebene über dem Knoten: Rand und Schein in seiner Farbe,
   sichtbar gemacht nur über die Deckkraft */
.node::after {
  content: "";
  position: absolute;
  inset: -1px;
  border: 1px solid var(--c);
  border-radius: 7px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 25%, transparent), 0 0 20px color-mix(in srgb, var(--c) 50%, transparent);
  opacity: 0;
  pointer-events: none;
  animation: node-glow 3s linear calc(var(--n, 0) * 0.5s - 0.05s) infinite;
}

/* Am Ziel stärker und länger, der Verlaufsrand bleibt sichtbar */
.node-hot::after {
  border-color: transparent;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--c) 30%, transparent), 0 0 34px color-mix(in srgb, var(--c) 65%, transparent);
  animation-name: node-glow-hot;
}

@keyframes node-glow {
  0% { opacity: 1; }
  18%, 100% { opacity: 0; }
}

@keyframes node-glow-hot {
  0% { opacity: 1; }
  24%, 100% { opacity: 0; }
}

/* Gestrichelter Rahmen um alles, was auf einem Rechner läuft, Name oben mittig im Rahmen.
   Senkrecht über die volle Breite, sonst ist der Rahmen schmaler als sein Name. */
.host {
  position: relative;
  align-self: stretch;
  padding: 26px 12px 18px;
  border: 1px dashed color-mix(in srgb, var(--mid) 60%, var(--line));
  border-radius: 10px;
}

.host-label {
  position: absolute;
  top: -0.7em;
  left: 50%;
  transform: translateX(-50%);
  padding: 0 6px;
  background: var(--bg);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--mid) 85%, var(--muted));
  white-space: nowrap;
}

/* Zweiter Name unten im Rahmen: was in allen Schritten arbeitet, etwa das Modell */
.host-label-bottom { top: auto; bottom: -0.7em; }

/* Zusatz im Rahmennamen, der auf schmalen Handys nicht mehr in den Rahmen passt */
@media (max-width: 439px) {
  .label-extra { display: none; }
}
.host:has(.host-label-bottom) { padding-bottom: 26px; }

/* Rahmen mit Knoten ohne Leitung: Dinge, die nebeneinander liegen statt nacheinander */
.host-apart { gap: 14px; }

/* Leitung zwischen zwei Knoten, im Verlauf vom vorigen zum nächsten Knoten.
   --i ist die Position in der Kette. Zwischen Rahmen und Knoten mehr Abstand. */
.wire {
  --c1: color-mix(in oklch, var(--to) calc(var(--i) / var(--len, 1) * 100%), var(--from));
  --c2: color-mix(in oklch, var(--to) calc((var(--i) + 1) / var(--len, 1) * 100%), var(--from));
  position: relative;
  flex: none;
  width: 2px;
  height: 26px;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--c1) 75%, transparent), color-mix(in srgb, var(--c2) 75%, transparent));
}

.flow > .wire { height: 38px; }

/* Senkrecht sitzen die Rahmennamen mittig auf der Linie. Die Leitungen halten deshalb
   Abstand zum Namen, statt direkt aus der Schrift zu kommen. */
.wire:has(+ .host) { margin-bottom: 14px; }
.host:has(.host-label-bottom) + .wire { margin-top: 14px; }

/* Das Paket ist ein Komet in der Farbe des Ziels: helle Spitze in Laufrichtung,
   der Schweif läuft aus. Die Ebene ist so lang wie die Leitung, der Komet sitzt an
   ihrem Ende. Sie wird per transform verschoben: am Anfang steht der Komet am Start
   der Leitung, am Ende an ihrem Ziel. Auf kurzen Leitungen höchstens so lang wie die Leitung. */
.wire::after {
  content: "";
  position: absolute;
  top: 0;
  left: -1px;
  width: 4px;
  height: 100%;
  background: linear-gradient(to bottom, transparent calc(100% - min(28px, 100%)), var(--c2));
  filter: drop-shadow(0 0 4px var(--c2));
  opacity: 0;
  animation: packet-down 3s linear infinite;
  animation-delay: calc(var(--i) * 0.5s);
}

@keyframes packet-down {
  0% { transform: translateY(calc(-100% + min(28px, 100%))); opacity: 0; }
  3% { opacity: 1; }
  15% { transform: translateY(0); opacity: 1; }
  17%, 100% { transform: translateY(0); opacity: 0; }
}

@keyframes packet-right {
  0% { transform: translateX(calc(-100% + min(28px, 100%))); opacity: 0; }
  3% { opacity: 1; }
  15% { transform: translateX(0); opacity: 1; }
  17%, 100% { transform: translateX(0); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .wire::after,
  .node,
  .node::after,
  .host { animation: none; }
}

/* Beschriftung einer Leitung, etwa die Schnittstelle. Senkrecht rechts neben der Linie. */
.wire:has(.wire-label) { height: 58px; }

.wire-label {
  position: absolute;
  top: 50%;
  left: 14px;
  transform: translateY(-50%);
  font-family: var(--mono);
  font-size: 0.66rem;
  font-style: normal;
  /* In der Farbe der Leitung, wie die Rahmennamen in der Farbe des Rahmens */
  color: color-mix(in srgb, var(--c2) 75%, var(--muted));
  white-space: nowrap;
}

/* Gruppen unter der Kette, aufgebaut wie die Tafeln. So viele Spalten, wie nebeneinander passen. */
.proj-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 14px 16px;
  margin-top: 28px;
}

.proj-cols > div + div { margin-top: 0; }

/* Waagrecht, sobald die längste Kette (content-pipeline, rund 805 px) in die Spalte passt.
   Alle Ketten wechseln gemeinsam, damit die Liste einheitlich bleibt. */
@container proj (min-width: 810px) {
  .flow,
  .host { flex-direction: row; align-items: center; }
  .host { align-self: auto; justify-content: center; }
  /* Ein einzelner Knoten füllt seinen Rahmen, sonst wirkt der Rahmen leer */
  .host > .node:only-of-type { flex: 1; }
  .node { min-width: 0; }
  .wire,
  .flow > .wire,
  .wire:has(.wire-label) {
    flex: 1;
    width: auto;
    min-width: 22px;
    height: 2px;
    margin: 0;
    background: linear-gradient(to right, color-mix(in srgb, var(--c1) 75%, transparent), color-mix(in srgb, var(--c2) 75%, transparent));
  }
  .wire::after {
    top: -1px;
    left: 0;
    width: 100%;
    height: 4px;
    background: linear-gradient(to right, transparent calc(100% - min(28px, 100%)), var(--c2));
    animation-name: packet-right;
  }
  .wire:has(+ .host),
  .host:has(.host-label-bottom) + .wire { margin: 0; }
  /* Waagrecht steht die Beschriftung mittig über der Linie */
  .wire:has(.wire-label) { min-width: var(--lw, 124px); }
  .wire-label { top: auto; bottom: 7px; left: 50%; transform: translateX(-50%); }
  /* Gruppen linksbündig mit gleichem Abstand zwischen den Textblöcken.
     Verworfen: über die ganze Breite verteilt, weil kurze Gruppen rechts verloren wirkten. */
  .proj-cols { display: flex; flex-wrap: wrap; column-gap: 64px; }
}

/* Tablet */
@media (min-width: 720px) {
  .boards { grid-template-columns: repeat(2, 1fr); }
  /* Alle kleinen Tafeln 270 px hoch, so hoch wie heute die höchste (AGENTEN).
     min-height: Bricht in schmalen Spalten Text um, wächst die Reihe statt überzulaufen */
  .boards > .board:not(.board-wide) { min-height: 270px; }
  .board-wide { grid-column: 1 / -1; }
  .board-placeholder { display: flex; }

  /* Am Handy bleibt fastfetch kleiner, damit weniger Zeilen umbrechen */
  .ff-body { font-size: 0.8rem; }

  /* Projekte ab Tablet: Kurzbeschreibung grau neben dem Namen, eine Zeile pro Projekt.
     Wird es eng, endet sie mit „…“ statt umzubrechen */
  .list summary { grid-template-columns: 16px auto minmax(0, 1fr) auto; column-gap: 14px; }
  /* Alle Namen so breit wie der längste, damit die Kurzbeschreibungen bündig beginnen.
     Fest in em statt per JavaScript gemessen, sonst springt der Text nach dem Laden der Schrift.
     Bei einem neuen längsten Namen anpassen: heute „nonprofit-website“ */
  .title { min-width: 8.8em; white-space: nowrap; }
  .desc {
    grid-column: 3;
    grid-row: 1;
    overflow: hidden;
    color: var(--muted);
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* Desktop: Hero links fest, Inhalt rechts */
@media (min-width: 1040px) {
  .layout {
    display: grid;
    grid-template-columns: 380px 1fr;
    gap: 56px;
  }

  .intro {
    position: sticky;
    top: 0;
    align-self: start;
    padding-top: 56px;
  }

  main { padding-top: 56px; }
}

/* Breite Bildschirme: die kleinen Tafeln in drei Spalten, die Seite wird kürzer statt voller */
@media (min-width: 1400px) {
  .boards { grid-template-columns: repeat(3, 1fr); }
}
