/*
 * styles.css
 *
 * Drei Seiten: index.html (Fragmente = Bühne, ergänzt um scroll.css), about.html und
 * angaben.html (Impressum). Libre Franklin 400, Zeilenabstand 1,4, 20px Rand -
 * überall gleich, auch im Menü; auf schmalen Fenstern bricht der Name im Menü
 * um. Die Textgröße wächst mit der Fensterbreite, siehe --schrift in :root.
 */

/* --- Schrift: Libre Franklin ---------------------------------------------- */

/* Impallari Type, SIL Open Font License (fonts/OFL.txt), nur der Schnitt
   Regular 400, selbst gehostet in fonts/ - keine Ladeanfrage an Google. Zwei
   Teilmengen: latin (Grundzeichen samt Umlauten) und latin-ext (weitere
   Akzente); der Browser lädt jede nur, wenn ein Zeichen daraus vorkommt. Bis
   die Datei da ist, zeigt er die Rückfallschrift (font-display: swap); die
   Seiten laden die latin-Datei vorab (<link rel="preload">), damit das kaum
   je zu sehen ist. */
@font-face {
    font-family: "Libre Franklin";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/libre-franklin-400-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: "Libre Franklin";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/libre-franklin-400-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
    /* Textgröße. Sie wächst mit der Fensterbreite: bei 320px und darunter
       fest 18,571px, bei 768px und darüber fest 26px, dazwischen
       gleichmäßig - über diese Spanne hinweg also genau um den Faktor 1,4
       (26 / 1,4 = 18,571). Die vier Zahlen stehen ohne Einheit, damit ihre
       Differenzen im calc() reine Zahlen bleiben; px mal px wäre keine
       gültige Länge. Alles Übrige - Zeilenhöhe, Leerzeilen, die Flächen
       hinter Menü und Bildunterschrift - ist in em gerechnet und wächst
       von selbst mit.

       --breite-max ist mit Bedacht dieselbe Zahl wie die Textbreite weiter
       unten (768px, der klassische Umbruchpunkt): Ab dort stehen Schriftgröße
       und Spaltenbreite beide fest, in jedem breiteren Fenster sind die
       Umbrüche darum genau dieselben. Wer die eine Zahl ändert, muss die
       andere mitändern. */
    --schrift-min: 18.571; /* 26 / 1,4 */
    --schrift-max: 26;
    --breite-min: 320;
    --breite-max: 768;
    --schrift: clamp(
        calc(var(--schrift-min) * 1px),
        calc(var(--schrift-min) * 1px
             + (var(--schrift-max) - var(--schrift-min))
             * (100vw - var(--breite-min) * 1px)
             / (var(--breite-max) - var(--breite-min))),
        calc(var(--schrift-max) * 1px)
    );

    --gap: 20px;
    --line: 1.4em; /* eine Textzeile (Zeilenabstand 1,4) */
    --blank: calc(var(--line) / 2); /* eine Leerzeile: halbe Zeilenhöhe, überall gleich */
    /* Helligkeit des Bildhintergrunds hinter den Texten: 10 % dunkler. Einzelne
       Bilder haben in scroll.js (dim) einen eigenen Wert; er steht an der
       Ebene und wird für das Menü auf body übernommen. */
    --dim: 0.9;
    /* Wie weit die Fläche hinter Menü und Bildunterschrift über das Textfeld der
       Zeile hinausragt, seitlich und oben/unten; negativ = innerhalb der Zeile */
    --reach-x: 0.34em;
    --reach-y: -0.16em;
    /* Breite des weichen Rands dieser Fläche, seitlich und oben/unten */
    --fade-x: 0.4em;
    --fade-y: 0.65em;
    /* Höhe der Menüzeile: oberer Rand + eine Textzeile + unterer Rand; nur
       gebraucht, wenn eine Höhe der Menüzeile nötig ist. Wächst mit der
       Textgröße mit. */
    --menu-height: calc(2 * var(--gap) + 1.4 * var(--schrift));
}

* {
    box-sizing: border-box;
}

html, body {
    height: 100%;
    margin: 0;
    padding: 0;
}

/* Safari auf dem iPhone vergrößert im Querformat von sich aus den Fließtext
   (Text-Autosizing), nicht aber das Menü; auf einer Seite stünden dann zwei
   Schriftgrößen. Abgeschaltet: quer gilt dieselbe Größe wie hochkant, sie
   ergibt sich allein aus der Fensterbreite (--schrift). */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

body {
    font-family: "Libre Franklin", "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-weight: 400;
    font-size: var(--schrift);
    line-height: 1.4;
    color: black;
    background-color: white;
}

[hidden] {
    display: none !important;
}

/* Links sehen aus wie der übrige Text: keine eigene Farbe, keine
   Unterstreichung. Unter der Maus werden sie unterstrichen - die
   Bildunterschrift "mit ...", der Lebenslauf, die E-Mail-Adresse und der
   Impressum-Link auf der About-Seite. Nur die Einträge der Menüzeile werden
   nie unterstrichen. (Das Impressum hat keine Links.) */
a {
    color: inherit;
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

#menu a:hover {
    text-decoration: none;
}

/* --- Menü: oben, vor allem anderen ---------------------------------------
   Links der Name, seit 2026-09-12 immer mindestens zweizeilig ("Michel
   Kleinbrahm" / "Architekt", <br> im Quelltext, kein Komma), rechts
   "Fragmente" in einer Zeile. */

#menu {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    /* Lücke zwischen Name und "Fragmente": Platz für den Ball des Wesens
       (0,76 em, die Höhe eines O) mit je 0,5 em Luft zum Wort und zum Namen
       (WESEN_WORTABSTAND in scroll.js), plus Reserve. Mit nur 1 em rückte der
       Ball auf schmalen Fenstern in die Mitte der Lücke (Ausweichfall in
       wesenWarteplatz) und hatte zum Wort nur 0,14 em statt 0,5 em. */
    gap: 1.85em;
    padding: var(--gap);
    pointer-events: none; /* Klicks neben den beiden Einträgen erreichen die Bühne */
    /* Reiner Text ohne Hintergrund, schwarz auf weißem Grund; über einem Foto
       weiß (siehe unten). */
    color: black;
}

/* Die Farbe der Texte auf der Bühne folgt dem Grund: Über einem Foto ist das
   Menü weiß (body.photo-shown, von scroll.js gesetzt, sobald ein Foto geladen
   und zu sehen ist), auf weißem Grund schwarz: Figur, fehlendes oder noch
   ladendes Bild, Textseiten. Die Bildunterschrift gehört zu ihrer Ebene und
   wird in scroll.css geregelt (.layer.photo .layer-caption). */
body.photo-shown #menu {
    color: white;
}

/* Über Fotos liegt außerdem hinter Menü und Bildunterschrift ein kaum
   merklicher Weichzeichner, der den Bildhintergrund beruhigt und ihn leicht
   abdunkelt (--dim, 10 %). Er sitzt
   auf einem Pseudo-Element hinter dem Text (::before, z-index -1), nicht auf
   dem Text selbst, denn die Maske, die die Fläche zu den Rändern hin auslaufen
   lässt, würde sonst auch die Buchstaben ausblenden. Das Feld ist um eine
   Reserve (--field-x, --field-y) größer als die Textzeile. Wie weit die
   sichtbare Fläche über die Textzeile hinausragt, bestimmen --reach-x und
   --reach-y (negativ = innerhalb der Zeile), unabhängig von der Breite des
   weichen Rands (--fade-x, --fade-y); beide Paare stehen in :root. Im Menü
   liegt die Fläche
   nicht auf dem Eintrag als Ganzem, sondern auf jeder Zeile für sich
   (span.line, von scroll.js gesetzt): Bricht der Name um, bleibt neben der
   kürzeren Zeile unverwischt, was kein Text ist. Auf der Figur und den
   Textseiten gibt es keinen Weichzeichner. Für die Bildunterschrift steht
   dasselbe Feld in scroll.css, dort an ihrer Ebene. */
body.photo-shown #menu .line {
    position: relative; /* Bezug für das Pseudo-Element */
}

body.photo-shown #menu .line::before {
    --field-x: 2em; /* Reserve des Felds über die Zeile hinaus, muss --reach-x übersteigen */
    --field-y: 2em; /* dito oben und unten */
    content: "";
    position: absolute;
    inset: calc(-1 * var(--field-y)) calc(-1 * var(--field-x));
    z-index: -1;
    pointer-events: none;
    -webkit-backdrop-filter: blur(2px) brightness(var(--dim));
    backdrop-filter: blur(2px) brightness(var(--dim));
    -webkit-mask-image:
        linear-gradient(to right,
            transparent calc(var(--field-x) - var(--reach-x)),
            black calc(var(--field-x) - var(--reach-x) + var(--fade-x)),
            black calc(100% - var(--field-x) + var(--reach-x) - var(--fade-x)),
            transparent calc(100% - var(--field-x) + var(--reach-x))),
        linear-gradient(to bottom,
            transparent calc(var(--field-y) - var(--reach-y)),
            black calc(var(--field-y) - var(--reach-y) + var(--fade-y)),
            black calc(100% - var(--field-y) + var(--reach-y) - var(--fade-y)),
            transparent calc(100% - var(--field-y) + var(--reach-y)));
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to right,
            transparent calc(var(--field-x) - var(--reach-x)),
            black calc(var(--field-x) - var(--reach-x) + var(--fade-x)),
            black calc(100% - var(--field-x) + var(--reach-x) - var(--fade-x)),
            transparent calc(100% - var(--field-x) + var(--reach-x))),
        linear-gradient(to bottom,
            transparent calc(var(--field-y) - var(--reach-y)),
            black calc(var(--field-y) - var(--reach-y) + var(--fade-y)),
            black calc(100% - var(--field-y) + var(--reach-y) - var(--fade-y)),
            transparent calc(100% - var(--field-y) + var(--reach-y)));
    mask-composite: intersect;
}

/* Der Name ist die Überschrift jeder Seite (h1 für Suchmaschinen), sieht aber
   aus wie der übrige Menütext. */
#menu h1 {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
}

#menu a {
    pointer-events: auto;
    white-space: nowrap;
}

/* Reicht der Platz nicht, bricht der Name auch vor "Kleinbrahm" um (dann drei
   Zeilen), "Fragmente" bleibt einzeilig rechts oben. */
#menu a:first-child {
    white-space: normal;
}

/* scroll.js fasst jede Zeile eines Eintrags in ein eigenes, nicht umbrechendes
   Feld, das den Weichzeichner trägt; das <br> vor "Architekt" bleibt dabei
   erhalten. */
#menu .line {
    display: inline-block;
    white-space: nowrap;
}

/* Einmal je Sitzung zeigt "Fragmente" beim Start, dass es sich klicken lässt:
   Eine Unterstreichung wandert von links nach rechts unter den Text und zieht
   sich dann, wieder von links nach rechts, zurück. scroll.js setzt dafür die
   Klasse hint und nimmt sie nach der Animation weg. Der Strich ist die echte
   Text-Unterstreichung eines unsichtbaren Zwillings des Texts (::after mit
   durchsichtiger Schrift, deckungsgleich über dem Link), mit Aussparung am g
   wie bei den Links auf den Textseiten. Er ist so stark wie die Querbalken
   des F (0,077 em = 2,0 px bei 26 px, gemessen in Libre Franklin) und
   beginnt 2 px unter der Grundlinie, beides ausdrücklich gesetzt, damit
   Safari und Chrome denselben Strich zeigen. Sichtbar wird er durch einen
   wandernden Ausschnitt (clip-path): erst öffnet sich der rechte Rand, dann
   schließt sich der linke. */
#menu a.hint {
    position: relative;
}

#menu a.hint::after {
    content: attr(data-text);
    position: absolute;
    inset: 0;
    pointer-events: none;
    color: transparent; /* nur der Strich ist zu sehen, in der Farbe des Menüs */
    text-decoration: underline;
    text-decoration-color: black;
    text-decoration-thickness: 0.077em; /* Stärke der Querbalken des F */
    text-underline-offset: 0.083em; /* 2 px unter der Grundlinie */
    clip-path: inset(0 100% 0 0);
    animation: hint-underline 1.8s ease-in-out forwards;
}

body.photo-shown #menu a.hint::after {
    text-decoration-color: white;
}

@keyframes hint-underline {
    0% {
        clip-path: inset(0 100% 0 0);
    }

    50% {
        clip-path: inset(0 0 0 0);
    }

    100% {
        clip-path: inset(0 0 0 100%);
    }
}

/* Auf den Textseiten steht das Menü im Textfluss und bleibt beim Scrollen oben
   kleben, schwarz auf Weiß. */
body.page-text #menu {
    position: sticky;
    background-color: white;
}

/* --- Bühne (index.html) ---------------------------------------------------
   Die Bühne selbst - Scrollbereich, Ebenen, Bilder und Bildunterschriften -
   steht in scroll.css. Hier bleibt nur, was auch die Textseiten brauchen. */

/* Lässt sich ein Bild nicht laden, setzt scroll.js an seine Stelle nur die
   Projektnummer (z.B. 003), mittig auf weißem Grund, in der Schrift des
   Menüs. */
.missing {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: white;
    color: black;
}

/* --- Figur: interaktive Arbeit auf der Bühne ------------------------------ */

.figure {
    position: absolute;
    inset: 0;
    overflow: hidden;
    background-color: white;
}

/* Die Zeichenfläche nimmt selbst keine Zeiger-Ereignisse an. Gegriffen wird
   nur am Wesen (siehe unten); daneben bleibt die Fläche frei, damit über der
   Figur weitergescrollt werden kann wie über jedem anderen Fragment. */
.figure-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
}

/* --- Das Wesen: der Ball am Kopfende der Bank (wesen.js) ------------------ */

/* Der sichtbare Ball liegt auf zwei eigenen, fensterfesten Flächen über allem
   anderen - auch über dem Menü (z-index 100), denn er wartet direkt vor dem
   Wort "Fragmente".

   Untere Fläche: difference kehrt um, was darunter liegt - auf weißem Grund
   erscheint der Ball schwarz, über der dunklen Bank und über Fotos hell.
   Obere Fläche: keine Umkehrung, der Ball wird einfach in der Farbe des
   Menütexts gezeichnet.

   Zwei Flächen, weil sich eine Umkehrung nicht anteilig einstellen lässt: beim
   Wechsel zwischen Warteplatz und Bank blendet wesen.js über die Deckkraft
   zwischen ihnen über, statt die Darstellung umzuschalten. */
.wesen-flaeche,
.wesen-klar {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
}

.wesen-flaeche {
    z-index: 101;
    mix-blend-mode: difference;
}

.wesen-klar {
    z-index: 102;
}

/* Trefferfläche: rund, unsichtbar und deutlich größer als der Ball, damit er
   mit dem Finger zu fassen ist. Lage und Größe setzt wesen.js. Sie trägt
   absichtlich keinen eigenen Zeiger - hier gelten weiter die Dreiecke der
   Bühne, obwohl gegriffen werden kann. */
.wesen-griff {
    position: fixed;
    z-index: 103;
    border-radius: 50%;
    touch-action: none; /* mit dem Finger zeichnen, nicht scrollen */
}

/* Genau der sichtbare Ball, innerhalb der Trefferfläche. Nur hier wird der
   Zeiger zum Kreuz; Ereignisse sprudeln nach außen an den Griff. */
.wesen-kern {
    position: absolute;
    border-radius: 50%;
    cursor: crosshair;
}

/* Ist der Ball nur anklickbar (verfügbar, noch nicht aktiviert), zeigt der
   Kern die Hand statt des Kreuzes; wird ein Klick abgewiesen (nicht verfügbar),
   bleibt es beim Pfeil - der Ball schüttelt sich dann nur. */
.wesen-griff--klick .wesen-kern {
    cursor: pointer;
}

.wesen-griff--stumpf .wesen-kern {
    cursor: default;
}

/* Beim Ziehen führt man den Ball selbst - der Systemzeiger verschwindet. */
.wesen-zieht,
.wesen-zieht * {
    cursor: none;
}

/* --- Textseiten ----------------------------------------------------------- */

/* Das Menü klebt beim Scrollen oben (position: sticky, siehe oben), aber nur
   innerhalb seines Elternelements body. Das ist sonst genau fensterhoch (html,
   body: height 100%), der Text läuft darüber hinaus, und nach einer
   Fensterhöhe scrollte das Menü mit. Auf den Textseiten wächst body darum mit
   dem Inhalt. */
body.page-text {
    height: auto;
    min-height: 100%;
}

/* Abstände in Leerzeilen (eine Leerzeile = eine halbe Zeilenhöhe, --blank):
   Nach der Menüzeile vier Leerzeilen, dann die erste Zeile (E-Mail-Adresse
   bzw. "Impressum"), danach eine Leerzeile nach jedem Absatz und jedem
   Lebenslaufeintrag. Der untere Rand des Menüs (--gap) zählt zu den vier
   Leerzeilen; main bekommt oben den Rest. Lange Wörter brechen notfalls um,
   statt die Seite zu verbreitern (bedingte Trennstriche &shy; stehen an den
   passenden Stellen in der Impressum-Quelle). */
body.page-text main {
    padding: calc(4 * var(--blank) - var(--gap)) var(--gap) var(--gap);
    overflow-wrap: break-word;
}

/* Textbreite: Bis 768px Fensterbreite (iPad hochkant, der klassische
   Umbruchpunkt) läuft der Text über die ganze Breite, also auf jedem iPhone
   hochkant. In jedem breiteren Fenster, auch auf dem iPhone quer, bleibt der
   Textblock so breit wie bei 768px, mit denselben Umbrüchen; er steht links,
   die Ränder (--gap) sind in den 768px enthalten. Das Menü bleibt davon
   unberührt, "Fragmente" steht weiter rechts oben.

   Dieselbe Zahl steht als --breite-max in :root: Genau ab hier hört auch die
   Schrift auf zu wachsen. Beides zusammen hält die Umbrüche in allen breiteren
   Fenstern fest. In em gerechnet käme dasselbe heraus (768 / 26 = 29,54em),
   weil die Schrift oberhalb feststeht - px sagt hier klarer, was gemeint ist. */
body.page-text main {
    max-width: 768px;
}

body.page-text main p,
body.page-text main li {
    margin: 0 0 var(--blank);
}

/* Der Lebenslauf ist eine Liste (für Suchmaschinen), sieht aber aus wie
   Absätze. */
body.page-text main ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* About-Seite: Der Impressum-Link sitzt unten links am Fensterrand; ist der
   Text länger als das Fenster, rückt er drei Leerzeilen unter den Text (eine
   vom letzten Eintrag, zwei als eigener Abstand). Dafür füllt main die
   restliche Fensterhöhe und schiebt den letzten Absatz nach unten. */
body.page-about {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

body.page-about main {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
}

body.page-about main p:last-child {
    margin-top: auto;
    padding-top: calc(2 * var(--blank));
    margin-bottom: 0;
}

/* Blaue Platzhalter im Impressum, bis die echten Angaben eingetragen sind */
.placeholder {
    color: #0000ff;
}

/* Rückwärts notierter Text (E-Mail-Adresse, Impressum wortweise), ohne
   JavaScript per Bidi-Override richtig herum angezeigt; mit JavaScript stellt
   contact.js den Text im DOM wieder her und entfernt die Klasse. */
.reversed {
    unicode-bidi: bidi-override;
    unicode-bidi: -webkit-isolate-override;
    unicode-bidi: isolate-override; /* jedes Wort für sich, Wortfolge bleibt links-nach-rechts */
    direction: rtl;
    white-space: nowrap; /* kein Umbruch innerhalb eines gespiegelten Worts, auch nicht am Bindestrich */
}
