/* ============================================================
   home.css — Landingpage (index.html)
   Tokens + Raster liegen in styles/base.css und styles/site.css.
   ============================================================ */

/* ============================================================
   HERO — Raster-Komposition statt Vollbild-Foto
   Meta oben · Porträt + Satz in der Mitte · riesiger Name unten
   ============================================================ */
/* ============================================================
   HERO als Scroll-Sequenz
   p = 0 → Porträt füllt das Fenster, Name weiss darüber
   p = 1 → Porträt geschrumpft oben links, Textblock rechts sichtbar
   home.js schreibt --p (0…1) und die Zielmasse --x1/--y1/--w1/--h1.
   ============================================================ */
.hero{
  --p: 0;
  /* DREI ETAPPEN auf EINER Laufstrecke:

     --pp  das Porträt. Es ist schon bei --p 0.45 an seinem Platz — vorher lief
           es bis 1 und für die Übergabe blieben nur die letzten 20 %. Der
           gefüllte Zustand blitzte dadurch bloss auf, statt zu stehen
           (User 2026-08-18: «die animation von einleitungssatz finde ich
           nicht gut. es sollte wirklich füllen die leeren räume»).
     dann  die ÜBERGABE: der Name tritt ab (0.44 → 0.56, am Scroll), und ab
           0.52 fährt der Satz Zeile für Zeile herein — als eigene Animation,
           NICHT am Scrollrad (home.js, initHeroFill). Seine Grösse steht da
           schon fest: sie wird gemessen, damit er die Fläche ausfüllt.
     danach steht alles still, bis der Hero loslässt.

     --q ist die Ersatzfassung in reinem CSS, falls das GSAP-CDN ausfällt:
     dann wächst der Satz wie bisher um einen festen Faktor. */
  --pp: clamp(0, calc(var(--p) / 0.45), 1);
  --q: clamp(0, calc((var(--p) - 0.58) * 3.2), 1);
  --lead-scale: 1;   /* setzt GSAP (gemessen); ohne GSAP bleibt es bei 1 */
  --hero-top: clamp(84px, 12vh, 130px);
  position: relative;
  /* Laufstrecke = Höhe minus einem Fenster, hier 140svh. Die Übergabe
     braucht Strecke, sonst ist der Satz fertig gewachsen, bevor man ihn
     überhaupt gesehen hat. */
  height: 240svh;
}

.hero-sticky{
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
}

/* --- Endzustand-Layout (liegt ÜBER dem Porträt) --- */
.hero-inner{
  position: relative;
  z-index: 2;
  height: 100%;
  /* ZWEI GLEICHE HÄLFTEN: Zeile 1 = obere Hälfte (Textblock),
     Zeile 2 = untere Hälfte — die gehört dem grossen Namen.
     Damit die Trennlinie exakt auf 50svh liegt, trägt der Container KEINEN
     `padding-top` und KEINEN `row-gap` mehr (beides verschöbe den Schnitt um
     die halbe Kopfhöhe bzw. die halbe Lücke nach unten). Der Kopfabstand
     sitzt jetzt am Textblock selbst. */
  grid-template-rows: 1fr 1fr;
  row-gap: 0;
  padding-top: 0;
  padding-bottom: 0;      /* das Porträt läuft bis an die Unterkante */
  pointer-events: none;   /* das Porträt darunter bleibt anklickbar */
}
.hero-inner > *{ pointer-events: auto; }

/* Platzhalter: definiert per Raster, WOHIN das Porträt schrumpft.
   Unsichtbar — home.js liest nur seine Masse aus.

   Der Endzustand ist ein HOCHKANT-STREIFEN über die ganze Fensterhöhe,
   randlos an der linken Kante (User 2026-08-04): der negative Margin hebt
   den Raster-Rand auf, `grid-row: 1 / -1` lässt ihn BEIDE Hälften
   überspannen. Das Foto wird darin zugeschnitten
   (object-fit: cover), nicht eingepasst. */
.hero-slot{
  grid-column: 1 / span 3;
  grid-row: 1 / -1;
  align-self: stretch;
  margin-left: calc(var(--pad) * -1);
  visibility: hidden;
}

/* --- Das Porträt: von vollflächig zu klein --- */
.hero-portrait{
  position: absolute;
  z-index: 1;
  margin: 0;
  overflow: hidden;
  left:   calc(var(--pp) * var(--x1, 0px));
  top:    calc(var(--pp) * var(--y1, 0px));
  width:  calc(100% + var(--pp) * (var(--w1, 100%) - 100%));
  height: calc(100% + var(--pp) * (var(--h1, 100%) - 100%));
  /* Nur die rechte Kante wird rund — links, oben und unten sitzt der
     Streifen randlos am Fenster, dort wäre ein Radius falsch. */
  /* border-radius: 0 calc(var(--pp) * var(--r-lg)) calc(var(--pp) * var(--r-lg)) 0; */
  box-shadow: var(--shadow-soft);
}

.hero-portrait .media{
  /* Beim Schrumpfen entsättigen — im Vollbild soll das Foto wirken */
  filter: grayscale(calc(var(--pp) * 0.85)) contrast(1.02);
}

/* Verlauf, damit der weisse Name im Vollbild lesbar bleibt.
   Verschwindet, sobald das Porträt geschrumpft ist. */
.hero-scrim{
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* verschwindet im selben Takt wie der Farbwechsel des Namens */
  opacity: clamp(0, calc(1 - (var(--pp) - 0.18) * 3.3), 1);
  /* Der Verlauf liegt dort, wo der weisse Name steht — seit er die UNTERE
     Hälfte besetzt, also unten. Oben bleibt eine leichte Abdunklung für die
     Status-Pille. */
  background:
    linear-gradient(to top, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 55%),
    linear-gradient(to bottom, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0) 24%);
}

/* --- Textblock rechts: blendet erst in der zweiten Hälfte ein --- */
.hero-intro{
  grid-column: 5 / -1;
  /* obere Hälfte, unten angeschlagen — steht damit direkt über dem Namen */
  grid-row: 1;
  align-self: end;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(10px, 1.6vh, 18px);   /* eng — keine Leeräume im Hero */
  /* der Kopfabstand, den früher .hero-inner trug */
  padding-top: var(--hero-top);
  padding-bottom: clamp(20px, 3.5vh, 44px);
  /* Erst wenn das Porträt sein Format erreicht hat — vorher reicht es noch
     unter den Textblock (Streifenbreite ≈ 1440 − 1075pp, Text ab Spalte 5).
     Mit GSAP übernimmt die Zeitleiste das Einblenden (siehe unten). */
  opacity: clamp(0, calc((var(--p) - 0.46) * 8), 1);
  /* Zwei Bewegungen in einer Zeile: das kurze Auffahren beim Einblenden
     (18px → 0) und danach das Nachrücken in die Mitte, wenn der Name
     abtritt (--q). Ohne das zweite stünde der Satz oben und die untere
     Hälfte wäre leer. */
  transform: translateY(calc(
    clamp(0, (1 - var(--p)) * 6, 1) * 18px + var(--q) * 12vh));
}

/* DER SATZ. Solange der Name noch da ist, ist er Fliesstext; wenn der Name
   abtritt (--q), wächst er auf das Anderthalbfache und wird zur Aussage der
   Seite. Ein zweiter Schriftgrad wäre auch gegangen — die Multiplikation
   hält beide Zustände aber zwingend im selben Verhältnis, egal wie der
   clamp() bei einer Fensterbreite ausfällt. */
.hero-lead{
  margin: 0;
  max-width: 30ch;
  font-size: calc(clamp(1.3rem, 2.3vw, 2.05rem) * calc(1 + var(--q) * 0.55));
  line-height: 1.24;
  letter-spacing: calc(-0.01em - var(--q) * 0.01em);
  text-wrap: pretty;
}
.hero-lead .serif-accent{ color: inherit; }

/* Zweite Zeile: gedämpft, etwas kleiner, Links unterstrichen */
.hero-sub{
  margin: 0;
  max-width: 34ch;
  font-size: calc(clamp(1.3rem, 2.3vw, 2.05rem) * calc(1 + var(--q) * 0.55) * 0.72);
  line-height: 1.28;
  color: var(--dim);
  text-wrap: pretty;
}
.hero-sub a{
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  transition: color 0.2s ease;
}
.hero-sub a:hover,
.hero-sub a:focus-visible{ color: var(--blue); }

/* ============================================================
   MIT GSAP: der Satz füllt die freie Fläche
   home/home.js setzt die Klasse, sobald GSAP + ScrollTrigger geladen sind
   (https://gsap.com/docs/v3/Plugins/ScrollTrigger/).

   Der Unterschied zur CSS-Fassung ist nicht die Bewegung, sondern das MASS:
   ScrollTrigger scrubbt eine Zeitleiste, und home.js hat vorher GEMESSEN,
   wie gross der Satz werden darf, damit er die Fläche vom Kopfabstand bis
   zur Unterkante genau ausfüllt. Ein fester Faktor (die --q-Fassung) trifft
   das nie: derselbe Satz braucht bei 900px Fensterhöhe vier Zeilen und bei
   1400px drei.

   Darum liegt der Block hier über BEIDE Rasterzeilen und ist OBEN
   angeschlagen: er wächst nach unten in die Hälfte hinein, die der Name
   räumt. Opazität und Y-Versatz setzt GSAP direkt am Element (inline, damit
   schlägt es die Regeln oben); hier steht nur, was Sache des Stylesheets
   bleibt: Raster und Schriftgrad.                                        */
.hero--gsap .hero-intro{
  grid-row: 1 / -1;
  align-self: start;
  justify-content: flex-start;
  /* Der Block selbst blendet NICHT mehr ein — das tun seine Zeilen, jede in
     ihrer eigenen Maske (siehe home.js, buildReveal). Vorher lag hier eine
     Opazität an --p; die ist mit dem Zeilenauftritt hinfällig. */
  opacity: 1;
  transform: none;
}

/* Die Maske um jede Zeile (SplitText, `mask: 'lines'`) klippt an der
   PADDING-Kante. Ohne das Polster schnitte sie die Unterlängen ab (g, p, y) —
   der negative Margin nimmt die Höhe gleich wieder weg, damit der gemessene
   Satz weiter genau in die Fläche passt. */
.hero--gsap .hero-line-mask{
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

/* --lead-scale ist der gemessene Faktor (1 … ~3.4). Er steht FEST, sobald
   gemessen wurde — der Satz wächst nicht mehr beim Scrollen (User
   2026-08-18: «ohne skalieren … sondern die zeile erscheint mit einer
   animation»). */
.hero--gsap .hero-lead{
  font-size: calc(clamp(1.3rem, 2.3vw, 2.05rem) * var(--lead-scale));
  letter-spacing: calc(-0.01em - clamp(0, (var(--lead-scale) - 1) * 0.02, 0.012em));
  max-width: min(30ch, 100%);
}

/* Die zweite Zeile wächst nur zur HÄLFTE mit: sie soll die Aussage stützen,
   nicht mit ihr konkurrieren. Ohne die Dämpfung stehen am Ende zwei gleich
   laute Sätze da. */
.hero--gsap .hero-sub{
  font-size: calc(clamp(1.3rem, 2.3vw, 2.05rem)
    * calc(1 + (var(--lead-scale) - 1) * 0.5) * 0.72);
  max-width: min(34ch, 100%);
}

/* .hero-facts: Markup entfernt (2026-08-17) — die Eckdaten stehen jetzt
   als Satz in .hero-sub */

/* --- Der grosse Name: weiss über dem Foto, dann wieder dunkel --- */
/* Der Name bleibt die ganze Sequenz über an derselben Stelle — das Foto
   zieht UNTER ihm weg. Ein wandernder Name kreuzte sonst mitten durchs
   Gesicht und wurde dabei kontrastlos. */
/* Name + Leistungszeile sitzen zusammen in der unteren Hälfte, unten bündig.
   Die Rasterangaben liegen deshalb HIER und nicht mehr am Namen selbst —
   zwei Elemente mit `align-self: end` in derselben Zelle lägen übereinander. */
.hero-title-block{
  grid-column: 5 / -1;
  grid-row: 2;
  align-self: end;
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 1.4vh, 16px);
  /* DER ABTRITT: sobald das Porträt seinen Streifen erreicht hat, blendet
     der Name aus und sinkt dabei ein Stück ab — die Bühne gehört dann dem
     Satz. Bewusst `opacity` und NICHT `display: none`: der Name ist das
     `h1` der Seite und muss für Screenreader und Suchmaschinen stehen
     bleiben. `pointer-events` schaltet sich mit ab, damit der unsichtbare
     Block keine Klicks abfängt. */
  opacity: calc(1 - var(--q));
  transform: translateY(calc(var(--q) * 24px));
  pointer-events: none;
}

/* Das Angebot in einem Satz — die einzige Aussage neben dem Namen, die im
   ersten Bildschirm steht. Farbe läuft im GLEICHEN Takt wie der Name
   (--p 0.18 → 0.48): weiss auf dem Foto, danach dunkel auf Creme.
   Beim Ändern der Namensfarbe hier mitziehen. */
.hero-claim{
  margin: 0;
  max-width: 30ch;
  font-size: clamp(0.95rem, 1.25vw, 1.25rem);
  line-height: 1.35;
  text-wrap: balance;
  color: color-mix(in srgb,
    var(--dim) calc(clamp(0, (var(--pp) - 0.18) * 3.3, 1) * 100%), rgba(255, 255, 255, 0.92));
  /* Der Name ist riesig und trägt sich selbst; diese Zeile ist klein und liegt
     über der HELLEN Partie des Fotos — dort reicht der Scrim nicht. Der
     Schatten verschwindet im selben Takt wie er, auf Creme bliebe er sonst
     als schmutziger Rand stehen. */
  text-shadow: 0 1px 16px rgba(0, 0, 0,
    calc(0.62 * (1 - clamp(0, (var(--pp) - 0.18) * 3.3, 1))));
}
.hero-claim strong{
  font-weight: 700;
  color: color-mix(in srgb,
    var(--ink) calc(clamp(0, (var(--pp) - 0.18) * 3.3, 1) * 100%), #ffffff);
}

.hero-name{
  margin: 0;
  padding-bottom: clamp(10px, 2vh, 26px);
  font-family: var(--display-font);
  font-weight: var(--title-weight);   /* siehe styles/base.css, Rolle TITEL */
  font-size: var(--t-huge);
  line-height: 0.82;
  letter-spacing: -0.045em;
  text-transform: uppercase;
  /* Farbwechsel FRÜH und im Gleichtakt mit dem Verlauf (--p 0.18 → 0.48):
     Solange der Verlauf liegt, ist der Name weiss; danach dunkel — dann
     steht er auf dem hellen Teil des Fotos bzw. auf Creme.
     Später umzuschalten hiess: weisser Name auf cremefarbenem Grund,
     sobald die Fotokante unter den Namen gewandert war. */
  color: color-mix(in srgb,
    var(--ink) calc(clamp(0, (var(--pp) - 0.18) * 3.3, 1) * 100%), #ffffff);
}

.hero-name-line{ display: block; }

.hero-name em{
  font-size: 0.52em;
  letter-spacing: -0.01em;
  text-transform: none;
}

/* Nach LINKS gerückt: unten rechts steht jetzt der Name — dort lag der
   Hinweis sonst mitten auf «fankhauser». Links liegt das Foto (bzw. später
   der Streifen), weisse Schrift bleibt dort lesbar. */
.hero-scroll{
  position: absolute;
  left: var(--pad);
  bottom: clamp(18px, 3vh, 30px);
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  color: #ffffff;
  opacity: clamp(0, calc(1 - var(--pp) * 4), 1);
}
.hero-scroll span{
  display: inline-block;
  animation: scroll-cue 2s ease-in-out infinite;
}
@keyframes scroll-cue{
  0%, 100%{ transform: translateY(0); opacity: 0.5; }
  50%{ transform: translateY(5px); opacity: 1; }
}

/* Status-Pille im Vollbild hell halten */
.hero.is-full .status{
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

/* Ohne Scroll-Animation (reduced motion / kein JS-Scrub):
   direkt der Endzustand, ohne Laufstrecke. */
/* «Bewegung reduzieren»: die Sequenz entfällt, die Seite zeigt direkt den
   Endzustand. Dann darf die Übergabe NICHT stattfinden (--q: 0) — sonst
   wäre der Name nie zu sehen, weil es gar keinen Moment davor gibt. Hier
   stehen Name und Satz nebeneinander, der Satz in seiner ruhigen Grösse. */
.hero.no-scrub{ --p: 1; --q: 0; height: auto; }
.hero.no-scrub .hero-sticky{ position: static; height: auto; }
.hero.no-scrub .hero-inner{ min-height: 100svh; }
.hero.no-scrub .hero-scroll{ opacity: 1; }

/* ============================================================
   (02) WORK — die Rubrik liegt seit 2026-08-17 auf DIESER Seite
   ============================================================
   Der frühere «Selected work»-Streifen ist ersatzlos entfernt (User:
   «selected work bin ich noch nicht zufrieden … stattdessen den work
   rubrik gerade dort einfügen»). Alles Aussehen der Rubrik selbst steht
   unverändert in home/work.css — hier stehen nur die zwei Zeilen, die
   nötig sind, weil sie jetzt MITTEN in einer Seite sitzt und nicht mehr
   ganz oben auf einer eigenen.                                         */
.work-section{ padding-bottom: 0; }

/* Auf der eigenen Seite hielt dieser Abstand den Kopf unter der festen
   Kopfzeile frei. Hier kommt der Abstand schon von `.section` — beides
   zusammen risse ein leeres Feld von über 300px auf. */
.work-section .work-head{ padding-top: 0; }

/* ============================================================
   (03) STATEMENT — Wort-für-Wort-Reveal beim Scrollen
   ============================================================ */
/* Die helle Sektion zwischen der blauen «Selected work»-Zone und dem
   schwarzen Footer braucht Höhe — sonst blitzt das Weiss beim Scrollen nur
   kurz auf, statt als eigener Moment zu wirken. */
.statement{
  min-height: 88vh;
  align-content: center;
}

.statement-text{
  grid-column: 1 / span 10;
  margin: 0;
  font-family: var(--display-font);
  font-weight: var(--title-weight);   /* siehe styles/base.css, Rolle TITEL */
  font-size: clamp(1.4rem, 3.9vw, 3.2rem);
  line-height: 1.08;
  letter-spacing: -0.025em;
  text-transform: uppercase;
  text-wrap: balance;
}

@media (max-width: 900px){
  .hero-slot{ grid-column: 1 / span 4; }
  /* .hero-title-block statt .hero-name — die Rasterangabe sitzt seit der
     Leistungszeile am gemeinsamen Block, nicht mehr am Namen */
  .hero-title-block,
  .hero-intro{ grid-column: 6 / -1; }
}

@media (max-width: 760px){
  /* Auf dem Handy fehlt die Breite für Streifen UND Text nebeneinander:
     Das Foto bleibt ein schmaler Streifen, Name und Text rücken daneben. */
  .hero-slot{ grid-column: 1 / span 1; }
  .hero-title-block,
  .hero-intro{ grid-column: 2 / -1; }
  /* Auf dem Handy steht der Name schon eng — die Leistungszeile bleibt,
     wird aber kleiner und darf die volle Spaltenbreite nutzen */
  .hero-claim{ max-width: none; font-size: 0.92rem; }
  /* --t-huge nimmt an, der Name habe die volle Fensterbreite — hier bleiben
     ihm nur die 11 Spalten neben dem Streifen (~64% der Breite). Ungekürzt
     lief «FANKHAUSER» an jeder Handybreite über den Rand hinaus (gemessen
     320–428px: 16–39%). 8.8vw ist auf genau diese Spaltenbreite gerechnet. */
  .hero{ --t-huge: clamp(1.8rem, 8.8vw, 3.6rem); }
  /* Die Übergabe (Name tritt ab, Satz übernimmt) bleibt — aber gedämpft:
     bei dieser Breite braucht der Satz ohnehin fünf bis sechs Zeilen, und
     das Anderthalbfache sprengte die obere Hälfte. Gemessen bei 390 und
     500px Breite: so bleibt er innerhalb seiner Zeile. Gilt nur OHNE GSAP —
     mit GSAP wird die Grösse gemessen und passt sich von selbst an. */
  .hero{ --q: clamp(0, calc((var(--p) - 0.58) * 3.2), 1); }
  .hero-lead{ font-size: calc(clamp(1.3rem, 2.3vw, 2.05rem) * calc(1 + var(--q) * 0.22)); }
  .hero-intro{
    transform: translateY(calc(
      clamp(0, (1 - var(--p)) * 6, 1) * 18px + var(--q) * 6vh));
  }
  .hero-facts{ grid-template-columns: 1fr; }
  .statement-text{ grid-column: 1 / -1; }
}

@media (prefers-reduced-motion: reduce){
  .hero-scroll span{ animation: none; }
}
