/* Komponenten-Schicht fuer www.coredev.it.
   Farben, Schrift, Groessenstaffel und Raster kommen aus tokens.css, dem
   Design-Token-Satz des Branding-Guides. Hier steht nur, wie die Bausteine
   daraus zusammengesetzt werden - keine eigenen Farbwerte, keine eigene
   Typo-Skala.

   Selbst gehostet, keine Verbindung zu Google Fonts oder einem CDN. Das ist
   zugleich ein Argument: wer On-Prem verkauft, laedt keine Schriften von
   fremden Servern. */

/* Paketnamen und Rufnummern stehen in IBM Plex Mono - dieselbe Familie,
   hier funktional und nicht dekorativ. Der Guide legt drei Sans-Schnitte
   fest und schliesst Mono nicht aus. */
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/assets/webfonts/IBMPlexMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Ersatzschrift metrisch an IBM Plex Sans angeglichen, damit beim Nachladen
   nichts springt (Cumulative Layout Shift). Werte aus der Regular-Datei
   gemessen: unitsPerEm 1000, ascender 1025, descender -275, lineGap 0.
   Die x-Hoehe liegt bei 0,516/em und damit praktisch auf Arial - deshalb
   size-adjust 100%. */
@font-face {
  font-family: "Plex Fallback";
  src: local("Helvetica Neue"), local("Arial"), local("Liberation Sans");
  ascent-override: 102.5%;
  descent-override: 27.5%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

:root {
  --font-sans: "IBM Plex Sans", "Plex Fallback", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* Fliessende Groessen, die bei den Werten des Guides enden statt sie zu
     ueberschreiten: Hero 44px, H1 32px, H2 24px, H3 20px, Lead 18px.
     Auf 320px bleibt es lesbar, auf dem Desktop exakt bei der Staffel. */
  --fs-hero: clamp(2rem,      1.55rem + 2.2vw,  var(--cd-size-hero));
  --fs-h1:   clamp(1.75rem,   1.5rem  + 1.2vw,  var(--cd-size-h1));
  --fs-h2:   clamp(1.375rem,  1.28rem + 0.5vw,  var(--cd-size-h2));
  --fs-h3:   clamp(1.125rem,  1.08rem + 0.25vw, var(--cd-size-h3));
  --fs-lead: clamp(1.0625rem, 1.03rem + 0.2vw,  var(--cd-size-lead));
  --fs-base: var(--cd-size-body);
  --fs-sm:   var(--cd-size-caption);
  --fs-xs:   0.8125rem;

  /* 56rem statt 68rem: der Fliesstext ist auf 34rem begrenzt (~71 Zeichen,
     Obergrenze der Lesbarkeitskonvention). Bei 68rem blieb rechts fast die
     halbe Flaeche leer und der Satzspiegel wirkte schmal und hoch. */
  --container: 56rem;
  --radius:    var(--cd-radius);
  --radius-lg: var(--cd-radius-card);
}

/* Reset und Grundtypografie. */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  hyphens: auto;
  -webkit-hyphens: auto;
  hyphenate-limit-chars: 7 4 3;
}

body {
  margin: 0;
  background: var(--cd-surface);
  color: var(--cd-text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: var(--cd-leading-body);
  font-variant-numeric: proportional-nums;
  -webkit-font-smoothing: antialiased;
}

/* Ueberschriften durchgehend Medium 500. Semibold und Bold werden nicht
   verwendet - Don'ts des Branding-Guides. Light 300 ist ausschliesslich
   dem Claim vorbehalten. */
h1, h2, h3, h4 {
  margin: 0 0 var(--cd-space-3);
  color: var(--cd-text);
  font-weight: 500;
  line-height: var(--cd-leading-tight);
  hyphens: manual;
  text-wrap: balance;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); line-height: 1.35; }
h4 { font-size: 1.0625rem; line-height: 1.40; }

p, ul, ol, dl, table { margin: 0 0 var(--cd-space-3); }
p, li { text-wrap: pretty; max-width: var(--cd-measure); }

.lead {
  font-size: var(--fs-lead);
  line-height: 1.55;
  color: var(--cd-text);
  max-width: var(--cd-measure);
}

/* Ausnahme von der Zeilenlaenge, bewusst gesetzt.
   Der Kontakt-Lead ist kein Fliesstext, sondern eine einzelne Zeile aus
   E-Mail-Adresse und Rufnummer. Sie braucht gemessene 36,1rem; die 34rem des
   Guides zwingen sie in zwei Zeilen, und der Umbruch faellt dann mitten
   zwischen die beiden Kontaktangaben. Die 34rem-Regel schuetzt die
   Lesbarkeit mehrzeiliger Absaetze - hier greift ihr Zweck nicht.
   40rem statt 36,1rem als Reserve, falls die Ersatzschrift minimal breiter
   baut als IBM Plex Sans. Ab 620px Viewport steht der Satz einzeilig (bei
   600px noch zweizeilig), darunter bricht er - dort ist eine Zeile ohnehin
   unmoeglich. Die Rufnummer haelt in jedem Fall zusammen, sie traegt
   geschuetzte Leerzeichen. */
.lead--wide { max-width: 40rem; }

small, .small { font-size: var(--fs-sm); }
.muted { color: var(--cd-text-muted); }

a {
  color: var(--cd-primary);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
a:hover { color: var(--cd-primary-hover); }

/* Nie ausschalten - der sichtbare Fokus ist Pflicht (WCAG 2.4.7). */
:focus-visible {
  outline: 2px solid var(--cd-primary);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Sprungziele duerfen nicht unter dem klebenden Kopf verschwinden
   (WCAG 2.2, 2.4.11 Focus Not Obscured). */
:target, [id] { scroll-margin-top: 5.5rem; }

code, kbd, samp, .mono {
  font-family: var(--font-mono);
  font-size: 0.875em;
  font-variant-ligatures: none;
}

hr {
  border: 0;
  border-top: var(--cd-border);
  margin: var(--cd-space-6) 0;
}

img, svg { max-width: 100%; height: auto; }

::selection { background: var(--cd-line); color: var(--cd-text); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Container, Sektionsrhythmus, Raster. */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 2.5rem);
}

.section {
  padding-block: clamp(3rem, 8vw, var(--cd-space-8));
  border-top: var(--cd-border);
}
.section:first-of-type { border-top: 0; }
.section--tint { background: var(--cd-surface-alt); }

.section__head { margin-bottom: var(--cd-space-5); }
.section__head p { color: var(--cd-text-muted); max-width: var(--cd-measure); }

/* Karten passen sich ohne Media Query an.
   min() um die Mindestbreite herum ist noetig: ein blankes minmax(22rem, 1fr)
   zwingt die Spalte auch dann auf 352px, wenn der Bildschirm schmaler ist -
   das Dokument wird dann breiter als das Viewport. Mit min(…, 100%) faellt
   die Mindestbreite auf die verfuegbare Breite zurueck. */
.grid {
  display: grid;
  gap: var(--cd-space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
}
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr)); }

.stack > * + * { margin-top: var(--cd-space-3); }
.stack-lg > * + * { margin-top: var(--cd-space-5); }

.skiplink {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--cd-primary);
  color: #fff;
  padding: var(--cd-space-2) var(--cd-space-3);
  border-radius: 0 0 var(--radius) 0;
}
.skiplink:focus { left: 0; color: #fff; }

@media print {
  .site-header, .site-footer nav, .skiplink { display: none; }
  body { color: #000; background: #fff; font-size: 11pt; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; }
  .section { border: 0; padding-block: 1rem; }
  .portrait { display: none; }
}

/* Kopf, Karten, Tabellen, Fuss. Kein JavaScript noetig. */

/* --- Kopfzeile -------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--cd-surface) 92%, transparent);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: var(--cd-border);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--cd-space-4);
  min-height: 4rem;
}
.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  flex: 0 0 auto;
}
/* Kompaktfassung, Mindestbreite laut Guide 130px. Die Wortmarke steckt im
   SVG - kein zusaetzlicher Text daneben, sonst stuende der Name doppelt.

   Unter 30rem waere sie mit 243px zu breit: zusammen mit dem Abstand bliebe
   fuer die Navigation kein Platz und das Dokument wuerde breiter als der
   Bildschirm. Dort steht deshalb das Signet, das der Guide ab 24px freigibt.
   Es traegt seine eigene Flaeche und funktioniert in beiden Modi. */
/* Die Selektoren muessen mindestens so spezifisch sein wie `.brand img`,
   sonst gewinnt dessen display:block und beide Fassungen stehen nebeneinander. */
/* 2rem = 32px: oberes Ende der im Guide erlaubten Kopfzeilen-Hoehe (28-32px).
   Bei 28px stand das Logo zu schwach neben der 44px-Hauptueberschrift. */
.brand img { height: 2rem; width: auto; display: block; }
.brand img.brand__signet { display: none; }
@media (max-width: 30rem) {
  .brand .brand__full { display: none; }
  .brand img.brand__signet { display: block; height: 2rem; width: 2rem; }
}

/* Auf schmalen Schirmen ein scrollbarer Streifen statt Hamburger-Menue:
   kein JavaScript, kein Zustand, nichts das kaputtgehen kann. */
.site-nav {
  display: flex;
  gap: var(--cd-space-1);
  overflow-x: auto;
  scrollbar-width: none;
  margin-inline: calc(-1 * var(--cd-space-2));
  /* Ohne min-width:0 schrumpft ein Flex-Element nie unter seine Inhaltsbreite -
     die Navigation zieht dann das ganze Dokument breiter als den Bildschirm. */
  min-width: 0;
  flex: 0 1 auto;
}
.site-nav::-webkit-scrollbar { display: none; }
.site-nav a {
  --min-target: 44px;                 /* WCAG 2.2, 2.5.8 */
  display: inline-flex;
  align-items: center;
  min-height: var(--min-target);
  padding: 0 var(--cd-space-2);
  color: var(--cd-text);
  text-decoration: none;
  font-size: var(--fs-sm);
  white-space: nowrap;
  border-radius: var(--radius);
}
.site-nav a:hover { color: var(--cd-primary); background: var(--cd-surface-alt); }

/* --- Hero ------------------------------------------------------------- */
.hero { padding-block: clamp(3rem, 9vw, 6.5rem); }

/* Der Claim ist die einzige Stelle mit Light 300 und der einzigen Sperrung
   im Fliesstext - so steht er auch im Logo unter der Wortmarke. */
.hero__claim {
  font-size: var(--fs-sm);
  font-weight: 300;
  letter-spacing: 0.115em;
  color: var(--cd-text-muted);
  margin-bottom: var(--cd-space-3);
}
/* 24ch statt 18ch: die Ueberschrift bricht damit zweizeilig statt dreizeilig.
   Ueberschriften werden gescannt, nicht zeilenweise gelesen - laengere Zeilen
   sind hier unkritisch, anders als beim Fliesstext. */
.hero h1 { font-size: var(--fs-hero); max-width: 24ch; }

.actions { display: flex; flex-wrap: wrap; gap: var(--cd-space-2); margin-top: var(--cd-space-5); }
.btn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--cd-space-4);
  border-radius: var(--radius);
  font-weight: 500;
  text-decoration: none;
  border: 1px solid transparent;
}
.btn--primary { background: var(--cd-primary); color: #fff; }   /* 7,3:1 */
.btn--primary:hover { background: var(--cd-primary-hover); color: #fff; }

/* Im dunklen Modus wird --cd-primary zu #8FA9D0 - einer hellen Flaeche.
   Weisser Text darauf kommt auf 2,4:1 und faellt damit klar durch
   WCAG 1.4.3 (gemessen 29.08.2026). Der Knopf dreht deshalb um: dunkler
   Text auf heller Flaeche, das ergibt 6,45:1.

   Lighthouse misst im hellen Modus und schlaegt hier nicht an - der Fehler
   stand seit dem 27.08. unbemerkt in der Datei. */
@media (prefers-color-scheme: dark) {
  .btn--primary,
  .btn--primary:hover { color: var(--cd-surface-dark); }
}
/* Rahmen in --cd-text-muted statt --cd-line: Bedienelemente brauchen 3:1
   gegen den Hintergrund (WCAG 1.4.11), die Linienfarbe kommt auf 1,3:1. */
.btn--ghost { border-color: var(--cd-text-muted); color: var(--cd-text); }
.btn--ghost:hover { border-color: var(--cd-primary); color: var(--cd-primary); }

/* --- Karten ----------------------------------------------------------- */
.card {
  padding: var(--cd-space-4);
  border: var(--cd-border);
  border-radius: var(--radius-lg);
  background: var(--cd-surface);
}
.card h3 { margin-bottom: var(--cd-space-2); }
.card p:last-child { margin-bottom: 0; }
.card__kicker {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--cd-text-muted);
  display: block;
  margin-bottom: var(--cd-space-2);
}

/* --- Portraet --------------------------------------------------------- */
/* Zweispaltig statt Float: ein Float haette unter dem Bild eine tote
   Spalte stehen lassen, weil das Grid der Definitionsliste nicht um ihn
   herumfliesst. Ab 52rem nebeneinander, darunter untereinander. */
.about {
  display: grid;
  gap: var(--cd-space-5);
  align-items: start;
}
.portrait { margin: 0; max-width: 20rem; }
.portrait img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
}

/* Name unter dem Portraet, verlinkt aufs LinkedIn-Profil. */
.portrait figcaption {
  margin-top: var(--cd-space-2);
  font-size: var(--fs-sm);
  font-weight: 500;
}

@media (min-width: 52rem) {
  /* Zwei Spalten: links das Portraet, rechts die Liste. Beide in derselben
     Zeile, oben ausgerichtet.
   *
   * Diese Fassung ist die dritte und bewusst die einfachste. Die beiden
   * Vorgaenger sind an ihrer eigenen Cleverness gescheitert:
   *
   * 1. `grid-template-columns: auto` liess die Spaltenbreite aus dem Bild
   *    folgen, dessen Breite aus seiner Hoehe kam, die an einer Prozentkette
   *    bis zur Zeilenhoehe hing. Brach die Kette beim Wiederherstellen aus dem
   *    Back-Forward-Cache, fiel die Spalte auf die Breite der Bildunterschrift
   *    zusammen und `img { max-width: 100% }` stauchte das Portraet auf 89px.
   *
   * 2. `grid-template-rows: repeat(4, auto)` plus `subgrid` band das Raster an
   *    die Anzahl der Listeneintraege. Als ein fuenfter Eintrag dazukam, bekam
   *    er keine eigene Zeile mehr und landete deckungsgleich auf dem vierten -
   *    zwei Textbloecke uebereinander, auf der oeffentlichen Seite.
   *
   * Beides hatte denselben Grund: Das Portraet sollte exakt so hoch sein wie
   * der erste Listeneintrag. Diese Kopplung ist aufgegeben. Die Spalte hat
   * eine feste Breite, das Bild folgt ihr, seine Hoehe ergibt sich aus dem
   * Seitenverhaeltnis - 192px breit, 128px hoch, plus 31px Unterschrift.
   * Nichts davon haengt an einer Prozentkette oder an einer Anzahl.
   *
   * Preis: Sollte der erste Listeneintrag je unter 159px hoch werden, ragt das
   * Portraet darunter hinaus. Das ist sichtbar und stabil - im Gegensatz zu
   * zwei Textbloecken, die stillschweigend uebereinanderliegen. */
  .about {
    grid-template-columns: 12rem minmax(0, 1fr);
    gap: var(--cd-space-4) var(--cd-space-6);
    align-items: start;
  }
  .portrait { grid-column: 1; grid-row: 1; max-width: none; }

  /* width/height kommen aus der Grundregel oben (100% / auto). aspect-ratio
     haelt den Platz frei, bevor das Bild geladen ist. */
  .portrait img { aspect-ratio: 960 / 641; }

  .deflist { grid-column: 2; grid-row: 1; }
}

/* --- Tabellen --------------------------------------------------------- */
.table-wrap {
  overflow-x: auto;
  border: var(--cd-border);
  border-radius: var(--radius-lg);
}
.table-wrap:focus-visible { outline: 2px solid var(--cd-primary); }
table { width: 100%; border-collapse: collapse; margin: 0; font-size: var(--fs-sm); }
th, td { text-align: left; padding: var(--cd-space-2) var(--cd-space-3); vertical-align: top; }
thead th {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--cd-text-muted);
  border-bottom: var(--cd-border);
  font-weight: 500;
}
tbody tr + tr td, tbody tr + tr th { border-top: var(--cd-border); }
td .mono, th .mono { color: var(--cd-text); }

/* Referenzprojekte: je Bereich eine eigene Tabelle statt einer grossen mit
   wiederholter Bereichsspalte. Der Bereichsname steht in der caption - sie
   beschriftet zugleich die scrollbare Region fuer Screenreader. */
.reftables { display: grid; gap: var(--cd-space-4); }
caption {
  text-align: left;
  padding: var(--cd-space-3) var(--cd-space-3) var(--cd-space-2);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--cd-text-muted);
  font-weight: 500;
  border-bottom: var(--cd-border);
}
/* Ohne Tabellenkopf traegt der Projektname die Zeile - als th, nicht als td. */
tbody th[scope="row"] { font-weight: 500; color: var(--cd-text); }

/* Status immer zusaetzlich als Text - Farbe darf nicht der einzige
   Bedeutungstraeger sein (WCAG 1.4.1). Auf coredev.it derzeit ungenutzt,
   die Klassen kommen von der Diensteliste auf coredev.org.
   #0E7C86 ist der einzige Signalton, den der Guide zulaesst (4,95:1). */
.badge {
  display: inline-block;
  padding: 0.1em 0.6em;
  border-radius: 999px;
  font-size: var(--fs-xs);
  border: 1px solid currentColor;
  white-space: nowrap;
}
.badge--open   { color: #0E7C86; }
.badge--auth   { color: var(--cd-text-muted); }
.badge--closed { color: var(--cd-text-muted); }
@media (prefers-color-scheme: dark) {
  .badge--open { color: color-mix(in srgb, #0E7C86 55%, #FFFFFF); }
}

/* --- Definitionsliste (Referenzen, Kontakt) --------------------------- */
.deflist { display: grid; gap: var(--cd-space-4); }
.deflist > div {
  display: grid;
  gap: var(--cd-space-1);
  padding-bottom: var(--cd-space-4);
  border-bottom: var(--cd-border);
}
.deflist > div:last-child { border-bottom: 0; padding-bottom: 0; }
.deflist dt { font-weight: 500; color: var(--cd-text); }
.deflist dd { margin: 0; color: var(--cd-text); max-width: var(--cd-measure); }

/* --- Hinweiskasten ---------------------------------------------------- */
/* Der Balken ist ein grafisches Element - dort ist die Kernfarbe zulaessig,
   als Textfarbe waere sie es nicht. */
.note {
  border-left: 3px solid var(--cd-core);
  background: var(--cd-surface-alt);
  padding: var(--cd-space-3) var(--cd-space-4);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.note p:last-child { margin-bottom: 0; }

/* --- Fuss ------------------------------------------------------------- */
.site-footer {
  border-top: var(--cd-border);
  padding-block: var(--cd-space-6);
  font-size: var(--fs-sm);
  color: var(--cd-text-muted);
}
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cd-space-4);
  justify-content: space-between;
  align-items: start;
}
.site-footer__brand img { height: 1.75rem; width: auto; display: block; margin-bottom: var(--cd-space-2); }
.site-footer nav { display: flex; flex-wrap: wrap; gap: var(--cd-space-3); }
.site-footer a { color: var(--cd-text); }
.site-footer a:hover { color: var(--cd-primary); }
