/* ==========================================================================
   Basis: Reset, Typografie, Flächen, Raster, Bewegungsgrundlagen
   ========================================================================== */

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

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1.5rem);
  /* Auch am Wurzelelement clippen. Nur am body reicht nicht: die
     Dokumentbreite (documentElement.scrollWidth) wächst sonst weiter durch
     die überstehenden Dekorflächen, auch wenn nichts scrollbar ist. */
  overflow-x: clip;
}

body {
  min-height: 100svh;
  /* Der Grundton wechselt im Phasenlauf zwischen drei Werten (tokens.css). */
  background: var(--ground);
  color: var(--ink);
  font-family: var(--text);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: 1.62;
  letter-spacing: -0.006em;
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}

/* <picture> erzeugt keine eigene Box. Sonst hängt das <img> in einem Wrapper
   mit automatischer Höhe, und jedes `height:100%` bzw. jedes aspect-ratio des
   umgebenden Rahmens läuft ins Leere - das Bild fällt dann auf die Pixelhöhe
   aus dem height-Attribut zurück. */
picture {
  display: contents;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

/* --- Grundkorn --------------------------------------------------------
   Eine sehr feine Störung über der ganzen Seite. Nimmt den Flächen das
   Digital-Glatte und lässt Dunkelflächen wie Material wirken statt wie
   leeres Schwarz. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --- Typografie -------------------------------------------------------- */

h1,
h2,
h3,
h4 {
  font-family: var(--display);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.032em;
  text-wrap: balance;
}

h1 {
  font-size: var(--step-5);
  font-weight: 900;
  letter-spacing: -0.042em;
}

h2 {
  font-size: var(--step-4);
}

h3 {
  font-size: var(--step-2);
  letter-spacing: -0.026em;
}

h4 {
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.02em;
}

p {
  text-wrap: pretty;
}

a {
  color: inherit;
  text-decoration-color: color-mix(in srgb, var(--orange) 55%, transparent);
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
}

a:hover {
  text-decoration-color: var(--orange);
}

::selection {
  background: var(--orange);
  color: var(--orange-ink);
}

:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 3px;
  border-radius: 1px;
}

/* --- Wiederkehrende Textrollen ----------------------------------------- */

/* Technische Annotation - das Vokabular der Bauzeichnung. */
.tag {
  font-family: var(--mono);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.tag--orange {
  color: var(--orange);
}

/* Nummerierte Marke, wie eine Positionsnummer im Plan.
   font-style:normal ist Pflicht - die Marke steht in einem <i>, und von
   Geist Mono ist bewusst nur der aufrechte Schnitt geladen. Ohne die Regel
   rechnet der Browser eine falsche Kursive aus. */
.pos {
  font-family: var(--mono);
  font-style: normal;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--orange);
  font-variant-numeric: tabular-nums;
}

.lead {
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--ink-2);
  letter-spacing: -0.014em;
  max-width: 46ch;
  text-wrap: pretty;
}

.prose {
  max-width: var(--measure);
}

.prose p + p {
  margin-top: 1.1em;
}

.prose > * + h3 {
  margin-top: 2.2em;
}

.prose h3 + p {
  margin-top: 0.7em;
}

.prose strong {
  font-weight: 700;
  color: var(--ink);
}

/* --- Raster ------------------------------------------------------------ */

.shell {
  width: min(100% - var(--gutter) * 2, var(--shell));
  margin-inline: auto;
}

.content {
  width: min(100% - var(--gutter) * 2, var(--content));
  margin-inline: auto;
}

.section {
  position: relative;
  padding-block: var(--sp-8);
}

.section--tight {
  padding-block: var(--sp-7);
}

/* Ein sichtbares Bauraster: dünne Fugenlinien, die Sektionen zusammenhalten.
   Der Bezug ist das Fugenbild der Pflasterflächen, nicht ein Deko-Grid. */
.ruled {
  border-top: 1px solid var(--edge-soft);
}

.grid-lines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background-image: linear-gradient(90deg, var(--edge-soft) 1px, transparent 1px);
  background-size: calc(100% / 6) 100%;
  mask-image: linear-gradient(180deg, transparent, #000 18%, #000 82%, transparent);
}

@media (max-width: 900px) {
  .grid-lines {
    background-size: calc(100% / 3) 100%;
  }
}

/* --- Die Diagonale ------------------------------------------------------
   Das einzige Element, das das Raster brechen darf. Direkt aus den
   Logo-Balken abgeleitet. */

.diag-rule {
  height: 3px;
  width: 100%;
  background: repeating-linear-gradient(
    var(--brand-angle),
    var(--orange) 0 14px,
    transparent 14px 28px
  );
  opacity: 0.9;
}

/* Diagonal beschnittene Sektionskante. */
.cut-top {
  clip-path: polygon(0 var(--cut), 100% 0, 100% 100%, 0 100%);
  margin-top: calc(var(--cut) * -1);
  padding-top: calc(var(--sp-8) + var(--cut));
}

.cut-bottom {
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), 0 100%);
  padding-bottom: calc(var(--sp-8) + var(--cut));
}

/* --- Flächen ----------------------------------------------------------- */

.panel {
  background: var(--panel);
  border: 1px solid var(--edge-soft);
  border-radius: var(--r-md);
}

.on-stone {
  background: var(--stone-050);
  color: var(--ink);
}

/* --- Bilder ------------------------------------------------------------ */

.fig {
  position: relative;
  overflow: hidden;
  background: var(--panel-2) center / cover;
  border-radius: var(--r-md);
}

.fig img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --- Bewegung: der Fugenwisch ------------------------------------------
   EINE Reveal-Geste für die ganze Seite: eine Maske läuft im Logo-Winkel
   über das Element, wie der Besen über die frisch verfüllte Fuge. Kein
   Fade, kein Blur, kein Scale - und vor allem kein clip-path: der schnitte
   auch das Rechteck, das der IntersectionObserver auswertet, und das
   Element bliebe für immer unsichtbar (FEHLER-191). mask-image tut das nicht.

   Basiszustand ist sichtbar. Erst wenn JS die Klasse setzt, wird verborgen -
   ohne JS bleibt die Seite vollständig lesbar. */

@property --w {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 120%;
}

.js .reveal,
.js .reveal-cut {
  --w: -22%;
  mask-image: linear-gradient(45deg, #000 var(--w), transparent calc(var(--w) + 9%));
  -webkit-mask-image: linear-gradient(45deg, #000 var(--w), transparent calc(var(--w) + 9%));
  transition: --w 620ms var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.js .reveal-cut {
  transition-duration: 900ms;
}

.js .reveal.is-in,
.js .reveal-cut.is-in {
  --w: 124%;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .js .reveal,
  .js .reveal-cut {
    --w: 124%;
    mask-image: none;
    -webkit-mask-image: none;
    opacity: 1;
    transform: none;
    clip-path: none;
    transition: opacity 180ms linear;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* --- Barrierefreiheit --------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: -100px;
  z-index: 10000;
  padding: 0.7rem 1.1rem;
  background: var(--orange);
  color: var(--orange-ink);
  font-weight: 700;
  border-radius: var(--r-sm);
  transition: top var(--t-fast) var(--ease-out);
}

.skip-link:focus {
  top: 0.7rem;
}

/* --- Einheitliche Bildanmutung ------------------------------------------
   Die fünf Firmenfotos sind zu unterschiedlichen Tageszeiten mit dem Handy
   entstanden und haben deutlich verschiedene Farbtemperaturen. Nebeneinander
   lesen sie sich sonst wie eine Kamerarolle statt wie eine Bildsprache.
   Eine leichte, einheitliche Korrektur zieht sie zusammen, ohne dass die
   Bilder bearbeitet aussehen. SVG-Zeichnungen sind ausgenommen. */
.fig img,
.detail img,
.proj__media img,
.hero__plate img,
.hero__inset img,
.svc__hero img,
.railfig img,
.artvisual--img img {
  filter: saturate(0.9) contrast(1.05) brightness(0.98);
}
