/* Mobi-Tec GmbH — landing page styles. */

/* Fonts are self-hosted: loading them from the Google CDN would send visitor IPs
   to a third party and require a consent banner. */
@font-face {
  font-family: 'Inter';
  src: url('../fonts/inter-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  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: 'Inter';
  src: url('../fonts/inter-var-ext.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  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 {
  --blau: #00549b;
  --blau-hell: #b0bee2;
  --orange: #ea5c0a;

  /* --orange is 3.48:1 on white: fills only, never small text.
     --orange-text passes AA on light, --orange-hell passes AA on --tief. */
  --orange-tief: #d95307;
  --orange-text: #c24c08;
  --orange-hell: #fb7a2e;

  --papier: #f7f8f3;
  --weiss: #ffffff;
  --tinte: #111512;
  --grau: #5f6a62;
  --linie: #dce5dd;

  --tief: #062e52;
  --tief-linie: rgba(176, 190, 226, 0.22);
  --auf-tief: #e8eef6;
  --auf-tief-leise: #9db0c6;

  /* Surfaces. The page alternates between these as you scroll so it does not
     read as one uninterrupted field. --flaeche-blau is the brand blue at an
     alpha low enough to keep --tinte body text well past AA on top of it. */
  --flaeche-papier: var(--papier);
  --flaeche-weiss: var(--weiss);
  --flaeche-tief: var(--tief);
  /* Stated outright rather than mixed from --blau: --papier is a warm, faintly
     green off-white, and any mix light enough to keep body text legible on it
     came out green rather than blue. */
  --flaeche-blau: #eaf0f7;

  /* Elevation. Tinted with --tief rather than black: a neutral black shadow on
     the warm --papier turns grey and muddy, a blue one stays part of the
     palette. */
  --schatten-ruhig: 0 1px 2px rgba(6, 46, 82, 0.04), 0 2px 8px rgba(6, 46, 82, 0.04);
  --schatten-hover: 0 2px 4px rgba(6, 46, 82, 0.05), 0 10px 24px rgba(6, 46, 82, 0.08);
  --schatten-tief: 0 4px 10px rgba(6, 46, 82, 0.08), 0 20px 48px rgba(6, 46, 82, 0.12);

  --t-hero: clamp(2.125rem, 5.4vw, 4.5rem);
  --t-h2: clamp(1.75rem, 3.2vw, 2.75rem);
  --t-h3: clamp(1.125rem, 1.5vw, 1.3125rem);
  --t-lead: clamp(1.0625rem, 1.4vw, 1.25rem);
  --t-klein: 0.8125rem;
  --t-body-klein: 0.9375rem;
  --t-mikro: 0.6875rem;

  /* Monospace carries the labels — eyebrows, tags, step counters. It is the
     only second typeface on the page and costs no extra request, since every
     target platform ships one. */
  --schrift-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
    'Liberation Mono', monospace;

  --raum-1: 0.5rem;
  --raum-2: 0.875rem;
  --raum-3: 1.25rem;
  --raum-4: 1.75rem;
  --raum-5: 2.5rem;
  --raum-6: 3.5rem;

  /* Texture on the navy surfaces. Composited with screen, so what shows is where
     the image is bright; --textur-deckung keeps it under the text contrast. */
  --textur: url('../img/mobitec_consulting_bg.webp');
  --textur-deckung: 0.06;
  --textur-mischung: screen;
  --textur-lage: center;
  --textur-ueberhang: 14%;

  --shell: 1180px;
  --gutter: clamp(1.25rem, 5vw, 3.5rem);
  --band: clamp(4.5rem, 9vw, 7.5rem);
  /* Height of the sticky masthead. Anchor targets scroll clear of it. */
  --kopf: 76px;

  --radius: 14px;
  --radius-gross: 26px;
  --pille: 999px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html {
  scroll-behavior: smooth;
  /* Without this an anchor jump parks the target under the sticky masthead.
     --kopf-ist is the masthead's measured height, set by main.js; --kopf is the
     design value it falls back to when that measurement is unavailable. */
  scroll-padding-top: calc(var(--kopf-ist, var(--kopf)) + 1.5rem);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--papier);
  color: var(--tinte);
  font-family: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3 {
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.025em;
  margin: 0;
  text-wrap: balance;
}

p {
  margin: 0;
  text-wrap: pretty;
}

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

a {
  color: inherit;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

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

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--tinte);
  color: var(--papier);
  padding: 0.75rem 1.25rem;
  font-size: var(--t-klein);
}

.skip:focus {
  left: 0;
}

/* Layout */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.band {
  padding-block: var(--band);
}

/* Surface modifiers. These bands are full-bleed, so their markup nests a
   .shell inside the <section> rather than combining the two on one element. */

.band--weiss {
  background: var(--flaeche-weiss);
}

.band--blau {
  background: var(--flaeche-blau);
}

.band--tief {
  background: var(--flaeche-tief);
  color: var(--auf-tief);
}

/* Recolouring by context rather than by modifier classes on every child keeps
   the two language files free of styling decisions. */
.band--tief .eyebrow {
  color: var(--auf-tief-leise);
}

.band--tief .section__title {
  color: var(--weiss);
}

.band--tief .lead {
  color: var(--auf-tief-leise);
}

/* The three navy surfaces, listed here rather than carrying a class of their own
   so the language files stay free of it. isolation keeps the blending inside the
   section; without it the layer would reach for whatever is painted underneath.
   --textur-y is the drift offset, written by main.js; at its 0 fallback the
   layer simply sits still. */
.hero,
.band--tief,
.contact {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.hero::before,
.band--tief::before,
.contact::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--textur-ueberhang) * -1);
  bottom: calc(var(--textur-ueberhang) * -1);
  background: var(--textur) var(--textur-lage) / cover no-repeat;
  mix-blend-mode: var(--textur-mischung);
  opacity: var(--textur-deckung);
  transform: translate3d(0, var(--textur-y, 0px), 0);
  z-index: -1;
  pointer-events: none;
}

/* One image serves all three, so each takes the part of it that suits what sits
   on top. */
.hero::before {
  --textur-lage: center right;
}

.contact::before {
  --textur-deckung: 0.05;
}

/* The orange focus ring is 3.3:1 on --papier and weaker still on navy, so dark
   bands get the light ring instead. */
.band--tief :focus-visible,
.hero :focus-visible,
.contact :focus-visible {
  outline-color: var(--orange-hell);
}

/* Scroll reveal. Nothing is hidden until main.js sets data-reveal="on" on the
   root, and it only does so when motion is allowed — so a visitor without JS,
   or with reduced motion, gets the finished page with no hidden content. */
[data-reveal='on'] [data-reveal] {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}

[data-reveal='on'] [data-reveal][data-revealed='true'] {
  opacity: 1;
  transform: none;
}

.eyebrow {
  font-family: var(--schrift-mono);
  font-size: var(--t-klein);
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--grau);
  margin-bottom: var(--raum-3);
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.eyebrow::before {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--orange);
  flex: none;
}

.eyebrow--auf-tief {
  color: var(--auf-tief-leise);
}

.lead {
  font-size: var(--t-lead);
  color: var(--grau);
  max-width: 60ch;
  line-height: 1.6;
}

.section__title {
  font-size: var(--t-h2);
  max-width: 20ch;
}

.section__title--tight {
  max-width: 15ch;
}

.section__lead {
  margin-top: 1.5rem;
}

/* Header */

.masthead {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--papier) 90%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--linie);
}

.masthead__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: var(--kopf);
}

.logo {
  display: inline-flex;
  align-items: center;
  flex: none;
}

.logo__img {
  height: 34px;
  width: auto;
}

.nav {
  display: flex;
  align-items: center;
  gap: clamp(0.85rem, 2vw, 2rem);
}

.nav__link {
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  color: var(--tinte);
  padding-block: 0.35rem;
  border-bottom: 2px solid transparent;
  transition: border-color 0.18s var(--ease), color 0.18s var(--ease);
}

.nav__link:hover {
  color: var(--blau);
  border-bottom-color: var(--orange);
}

/* Scrollspy. Matches .lang__item--on so the page has a single "you are here"
   vocabulary; the underline is drawn with the border the link already carries. */
.nav__link[aria-current='true'] {
  color: var(--blau);
  border-bottom-color: var(--orange);
}

/* The masthead has room for four in-page links beside the logo, language
   switch and CTA. The remaining sections are reachable from the burger panel,
   where vertical space is not the constraint. */
.nav__link--nur-mobil {
  display: none;
}

.lang {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--t-klein);
  font-weight: 600;
  letter-spacing: 0.04em;
}

.lang__sep {
  color: var(--linie);
}

.lang__item {
  text-decoration: none;
  color: var(--grau);
}

.lang__item:hover {
  color: var(--tinte);
}

.lang__item--on {
  color: var(--tinte);
  text-decoration: underline;
  text-decoration-color: var(--orange);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}

.burger {
  display: none;
  background: none;
  border: 1px solid var(--linie);
  border-radius: var(--pille);
  padding: 0.55rem 0.75rem;
  cursor: pointer;
  color: var(--tinte);
}

/* Buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  font-size: 0.9375rem;
  font-weight: 600;
  text-decoration: none;
  padding: 0.8rem 1.5rem;
  border-radius: var(--pille);
  border: 1px solid transparent;
  white-space: nowrap;
  transition: transform 0.18s var(--ease), background-color 0.18s var(--ease),
    border-color 0.18s var(--ease);
}

.btn:hover {
  transform: translateY(-2px);
}

/* Ink on orange, not white: white on --orange is 3.48:1. */
.btn--primary {
  background: var(--orange);
  color: var(--tinte);
}

.btn--primary:hover {
  background: var(--orange-tief);
}

.btn--dunkel {
  background: var(--tinte);
  color: var(--weiss);
}

.btn--dunkel:hover {
  background: var(--blau);
}

.btn--geist {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--tief-linie);
  color: var(--auf-tief);
}

.btn--geist:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: var(--blau-hell);
}

.btn--klein {
  padding: 0.6rem 1.15rem;
  font-size: 0.875rem;
}

.btn__arrow {
  transition: transform 0.18s var(--ease);
}

.btn:hover .btn__arrow {
  transform: translateX(3px);
}

/* Hero */

.hero {
  background: var(--tief);
  color: var(--auf-tief);
  border-radius: 0 0 var(--radius-gross) var(--radius-gross);
  padding-block: clamp(3.5rem, 7vw, 6rem) clamp(4rem, 8vw, 7rem);
  position: relative;
  overflow: hidden;
}

.hero__ring {
  position: absolute;
  top: 50%;
  right: -14%;
  width: min(46rem, 70vw);
  aspect-ratio: 1;
  /* --ring-drehung is set from the scroll position in main.js; without JS the
     ring simply stays where it is. */
  transform: translateY(-50%) rotate(var(--ring-drehung, 0deg));
  transform-origin: 50% 50%;
  opacity: 0.16;
  pointer-events: none;
}

.hero__inner {
  position: relative;
  z-index: 1;
}

.hero__title {
  font-size: var(--t-hero);
  max-width: 20ch;
  color: var(--weiss);
  /* Wrap whole words rather than hyphenating; longer German compounds like
     "Entwicklerteam" then break only if they would otherwise overflow. */
  overflow-wrap: break-word;
}

.hero__accent {
  color: var(--orange-hell);
}

.hero__lead {
  margin-top: 1.75rem;
  max-width: 54ch;
  color: var(--auf-tief-leise);
}

.hero__actions {
  margin-top: 2.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem;
}

.hero__facts {
  margin-top: 3.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--tief-linie);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 3rem;
  font-size: 0.9375rem;
  color: var(--auf-tief-leise);
}

.hero__fact {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  animation: text-hell 6s var(--ease) infinite;
}

.hero__fact::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--orange);
  flex: none;
  animation: punkt-puls 6s var(--ease) infinite;
}

/* The three facts take turns being highlighted rather than pulsing in unison,
   so the eye is led along the row. */
.hero__fact:nth-child(2)::before,
.hero__fact:nth-child(2) {
  animation-delay: 2s;
}

.hero__fact:nth-child(3)::before,
.hero__fact:nth-child(3) {
  animation-delay: 4s;
}

@keyframes punkt-puls {
  0%,
  70%,
  100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(234, 92, 10, 0);
  }

  8% {
    transform: scale(1.5);
    box-shadow: 0 0 0 0 rgba(234, 92, 10, 0.55);
  }

  35% {
    transform: scale(1);
    box-shadow: 0 0 0 10px rgba(234, 92, 10, 0);
  }
}

/* The extra weight is painted, not a heavier font axis: a real 'wght' change
   widens the glyphs and pushes the following facts sideways by a few pixels
   every cycle. A shadow in the text colour thickens the strokes instead, so the
   metrics — and the row — hold still. Only its alpha animates, which keeps the
   fade smooth (-webkit-text-stroke-width would work too, but browsers snap it
   between values instead of interpolating). */
@keyframes text-hell {
  0%,
  45%,
  100% {
    color: var(--auf-tief-leise);
    text-shadow: 0 0 0.6px rgba(232, 238, 246, 0);
  }

  10%,
  25% {
    color: var(--auf-tief);
    text-shadow: 0 0 0.6px rgba(232, 238, 246, 0.95);
  }
}

/* Problem cards */

.pains {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1rem, 2vw, 1.75rem);
  margin-top: var(--raum-6);
  /* Stretched, not start-aligned: the cards flip to photographs, and equal
     heights are what make all six crop to the same shape. */
  align-items: stretch;
  perspective: 1400px;
}

.pain {
  position: relative;
  transition: transform 0.18s var(--ease);
}

.pain__inner {
  position: relative;
  height: 100%;
  transform-style: preserve-3d;
  /* Leaving is instant; arriving waits. A pointer crossing the row on its way
     down the page never triggers a flip, one that settles on a card does. */
  transition: transform 0.5s var(--ease);
}

.pain__face {
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  box-shadow: var(--schatten-ruhig);
  backface-visibility: hidden;
  transition: box-shadow 0.18s var(--ease), border-color 0.18s var(--ease);
}

.pain__front {
  position: relative;
  height: 100%;
  background: var(--weiss);
  padding: clamp(1.75rem, 2.4vw, 2.25rem);
}

.pain__back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
  background-color: var(--papier);
  background-position: center;
  background-size: cover;
  /* Nearly monochrome. Only a trace of colour is left, which is what keeps six
     separately generated photographs from disagreeing about white balance in a
     row that shows two of them at once. */
  filter: grayscale(88%) contrast(1.06);
}

.pain:hover {
  transform: translateY(-2px);
}

.pain:hover .pain__face {
  box-shadow: var(--schatten-hover);
  border-color: var(--blau-hell);
}

/* These are voices, not headings — the oversized mark is what tells the eye so
   before it reads a word. It is decorative: the quotation marks in the copy
   itself are what a screen reader announces. */
.pain__front::before {
  content: '\201C';
  position: absolute;
  top: 0.35rem;
  right: 1.25rem;
  font-size: 5rem;
  line-height: 1;
  font-weight: 800;
  color: var(--blau);
  opacity: 0.13;
  pointer-events: none;
}

.pain__field {
  font-family: var(--schrift-mono);
  font-size: var(--t-mikro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--orange-text);
  margin-bottom: var(--raum-2);
}

.pain__title {
  font-size: clamp(1.1875rem, 1.7vw, 1.4375rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.28;
  color: var(--blau);
  margin-bottom: var(--raum-2);
  position: relative;
}

.pain__text {
  font-size: var(--t-body-klein);
  color: var(--grau);
}

/* Rest on one card and the other two turn over to photographs of the situation
   it describes. Gated on a fine pointer: touch has no hover, and the media query
   also keeps phones from ever fetching the six images. */
@media (hover: hover) and (pointer: fine) {
  /* The pair opens away from the card under the cursor: the one to its right
     turns one way, the one to its left the other. */
  .pain:hover ~ .pain .pain__inner {
    transform: rotateY(180deg);
    transition-delay: 0.14s;
  }

  .pain:has(~ .pain:hover) .pain__inner {
    transform: rotateY(-180deg);
    transition-delay: 0.14s;
  }

  /* Both photographs belong to the hovered card, not to the cards showing them.
     Of the two that turn, the earlier one in the row takes image a. */
  .pain:nth-child(1):hover ~ .pain:nth-child(2) .pain__back {
    background-image: url('../img/pain-1a.webp');
  }

  .pain:nth-child(1):hover ~ .pain:nth-child(3) .pain__back {
    background-image: url('../img/pain-1b.webp');
  }

  .pain:nth-child(1):has(~ .pain:nth-child(2):hover) .pain__back {
    background-image: url('../img/pain-2a.webp');
  }

  .pain:nth-child(2):hover ~ .pain:nth-child(3) .pain__back {
    background-image: url('../img/pain-2b.webp');
  }

  .pain:nth-child(1):has(~ .pain:nth-child(3):hover) .pain__back {
    background-image: url('../img/pain-3a.webp');
  }

  .pain:nth-child(2):has(~ .pain:nth-child(3):hover) .pain__back {
    background-image: url('../img/pain-3b.webp');
  }
}

/* Staggering the middle card breaks the flat three-across row. Below the
   breakpoint the grid is single-column, where an offset would just read as a
   stray gap. */
@media (min-width: 861px) {
  .pain:nth-child(2) {
    transform: translateY(1.75rem);
  }

  .pain:nth-child(2):hover {
    transform: translateY(calc(1.75rem - 2px));
  }

  .pain:nth-child(3) {
    transform: translateY(0.875rem);
  }

  .pain:nth-child(3):hover {
    transform: translateY(calc(0.875rem - 2px));
  }
}

/* Services */

/* Two columns, not four. Inside the 1180px shell auto-fit produced four ~277px
   tracks, too narrow for three lines of copy plus a row of tags. */
.grid-services {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--linie);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  overflow: hidden;
  margin-top: var(--raum-6);
  box-shadow: var(--schatten-ruhig);
}

@media (max-width: 700px) {
  .grid-services {
    grid-template-columns: minmax(0, 1fr);
  }
}

.service {
  position: relative;
  background: var(--weiss);
  padding: clamp(1.75rem, 2.4vw, 2.375rem);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  transition: background-color 0.2s var(--ease);
}

.service:hover {
  background: var(--papier);
}

/* A blue rule wipes in along the top edge on hover. Inside a 1px-gap hairline
   grid there is no room to lift a card, so the feedback has to be flat. */
.service::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--blau);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.24s var(--ease);
}

.service:hover::before,
.service:focus-within::before {
  transform: scaleX(1);
}

.service__field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--schrift-mono);
  font-size: var(--t-mikro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blau);
}

/* One outline mark per card, drawn from assets/img/icons.svg. It rides in the
   label row rather than sitting above it: at 18px on stroke-only artwork it
   reads as part of the label, and the card keeps its height. Colour comes from
   the inherited --blau, which the sprite picks up through currentColor. */
.service__icon {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
}

.service__title {
  font-size: var(--t-h3);
  line-height: 1.22;
}

.service__text {
  font-size: 0.9375rem;
  color: var(--grau);
}

.service__list {
  margin-top: auto;
  padding-top: 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.tag {
  font-family: var(--schrift-mono);
  font-size: var(--t-mikro);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--blau);
  border: 1px solid var(--linie);
  border-radius: var(--pille);
  padding: 0.22rem 0.65rem;
  background: var(--papier);
}

/* Approach */

.approach {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}

.steps {
  counter-reset: step;
}

/* The three steps arrive one after another instead of as one block. The <ol> is
   still the observed element, but it stays visible and hands the reveal to its
   children — the list holds its final height from the start, so nothing below
   it shifts as the steps appear.

   Nothing moves: the steps fade in place. Slower than the page's other reveals
   on purpose, since the point here is to be read as a sequence of three rather
   than noticed as motion. They start dimmed rather than fully transparent, so
   all three steps are legible from the outset and the reveal reads as the list
   coming up to strength instead of assembling itself. */
[data-reveal='on'] .steps[data-reveal] {
  opacity: 1;
  transform: none;
}

[data-reveal='on'] .steps[data-reveal] .step {
  opacity: 0.07;
  transition: opacity 1.9s var(--ease);
}

[data-reveal='on'] .steps[data-reveal][data-revealed='true'] .step {
  opacity: 1;
}

[data-reveal='on'] .steps[data-reveal] .step:nth-child(2) {
  transition-delay: 720ms;
}

[data-reveal='on'] .steps[data-reveal] .step:nth-child(3) {
  transition-delay: 1440ms;
}

.step {
  position: relative;
  padding-left: 4.25rem;
  padding-bottom: 2.75rem;
}

.step:last-child {
  padding-bottom: 0;
}

.step::before {
  content: '';
  position: absolute;
  left: 17px;
  top: 2.6rem;
  bottom: 0;
  width: 2px;
  background: var(--linie);
}

.step:last-child::before {
  display: none;
}

.step::after {
  counter-increment: step;
  content: '0' counter(step);
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--schrift-mono);
  font-size: var(--t-klein);
  font-weight: 600;
  color: var(--weiss);
  background: var(--blau);
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 6px var(--flaeche-papier);
}

.step__title {
  font-size: var(--t-h3);
  margin-bottom: 0.5rem;
  line-height: 1.25;
  padding-top: 0.25rem;
}

.step__text {
  font-size: 0.9375rem;
  color: var(--grau);
  max-width: 52ch;
}

/* Experience: booking dial */

.proof {
  background: var(--weiss);
  border: 1px solid var(--linie);
  border-radius: var(--radius-gross);
  padding: clamp(1.5rem, 3vw, 2.75rem);
  margin-top: var(--raum-6);
  box-shadow: var(--schatten-tief);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}

.proof__title {
  font-size: var(--t-h3);
  margin-bottom: 0.875rem;
}

.proof__text {
  font-size: 0.9375rem;
  color: var(--grau);
}

.proof__text + .proof__text {
  margin-top: 0.875rem;
}

.proof__legend {
  margin-top: 1.75rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  font-size: var(--t-klein);
  font-weight: 500;
}

.proof__key {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.proof__swatch {
  width: 14px;
  height: 6px;
  border-radius: var(--pille);
  flex: none;
}

.proof__swatch--book {
  background: var(--blau);
}

.proof__swatch--idle {
  background: var(--orange);
}

.proof__swatch--track {
  background: var(--blau-hell);
}

.proof__caption {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--linie);
  font-size: var(--t-klein);
  color: var(--grau);
}

.dial {
  width: 100%;
  max-width: 26rem;
  margin-inline: auto;
}

/* An SVG circle path starts at 3 o'clock, so the arcs are rotated to put hour 00
   at the top. Ticks and labels already carry that offset and stay outside. */
.dial__arcs {
  transform: rotate(-90deg);
  transform-origin: 200px 200px;
}

.dial__track {
  fill: none;
  stroke: var(--blau-hell);
  stroke-width: 13;
  opacity: 0.42;
}

.dial__book,
.dial__idle {
  fill: none;
  stroke-width: 13;
  stroke-linecap: butt;
}

.dial__book {
  stroke: var(--blau);
}

.dial__idle {
  stroke: var(--orange);
}

/* The arcs used to draw themselves on page load, five screens above where the
   dial actually sits, so the animation was never once seen. It now runs when
   the figure scrolls into view. Undrawn arcs are the default, so no JS means a
   complete dial rather than an empty one. */
[data-revealed='true'] .dial__book {
  animation: arc-in 0.55s var(--ease) backwards;
  animation-delay: calc(var(--i, 0) * 70ms + 200ms);
}

[data-revealed='true'] .dial__idle {
  animation: arc-in 0.7s var(--ease) 0.55s backwards;
}

@keyframes arc-in {
  from {
    opacity: 0;
    stroke-width: 3;
  }
  to {
    opacity: 1;
    stroke-width: 13;
  }
}

.dial__tick {
  stroke: var(--linie);
  stroke-width: 1.5;
}

.dial__tick--major {
  stroke: var(--grau);
  stroke-width: 2;
}

.dial__hour,
.dial__stat,
.dial__stat-label {
  text-anchor: middle;
  dominant-baseline: central;
}

.dial__hour {
  font-size: 15px;
  font-weight: 600;
  fill: var(--grau);
  font-variant-numeric: tabular-nums;
}

.dial__hub-ring {
  fill: none;
  stroke: var(--orange);
  stroke-width: 3;
  opacity: 0.35;
}

.dial__stat {
  font-size: 40px;
  font-weight: 800;
  letter-spacing: -0.03em;
  fill: var(--tinte);
}

.dial__stat-label {
  font-size: 12px;
  font-weight: 600;
  fill: var(--grau);
  letter-spacing: 0.02em;
}

/* Reference: the product we shipped */

/* The band itself now carries the navy, so this is a bare layout grid rather
   than the inset card it used to be. Copy left, the product previews right. */
.reference {
  margin-top: var(--raum-6);
  display: grid;
  /* Weighted towards the previews: the backoffice screenshot is wide and dense,
     and loses legibility fast as the column narrows. */
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(1.75rem, 3.5vw, 3rem);
  align-items: center;
}

.reference__label {
  font-family: var(--schrift-mono);
  font-size: var(--t-mikro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--auf-tief-leise);
  margin-bottom: 0.875rem;
}

.reference__title {
  font-size: var(--t-h3);
  line-height: 1.22;
  color: var(--weiss);
  max-width: 20ch;
}

.reference__text {
  margin-top: 1rem;
  font-size: 0.9375rem;
  color: var(--auf-tief-leise);
  max-width: 46ch;
}

.reference__points {
  margin-top: 1.75rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.reference__chip {
  position: relative;
  font-family: var(--schrift-mono);
  font-size: var(--t-mikro);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--auf-tief);
  border: 1px solid var(--tief-linie);
  border-radius: var(--pille);
  padding: 0.24rem 0.7rem;
  background: rgba(255, 255, 255, 0.04);
}

/* A light travels along the row of chips, one after the next.

   The glow is its own layer sitting exactly over the chip's border, and only
   its opacity animates. Animating border-color and box-shadow directly would
   repaint each chip every frame; opacity is composited, which matters when the
   loop never ends. Nothing here affects layout or the label's contrast — the
   text keeps its colour throughout, so the chip stays equally readable lit or
   at rest.

   Near-white rather than the border's own blue: it is the colour the chip's
   text is already set in, and it only works against the navy band — which is
   why this stays scoped to .reference__chip instead of reaching .tag and
   .why__stack on the light surfaces. */
.reference__chip::after {
  content: '';
  position: absolute;
  inset: -1px;
  border: 1px solid rgba(232, 238, 246, 0.45);
  border-radius: inherit;
  box-shadow: 0 0 10px -1px rgba(232, 238, 246, 0.22);
  opacity: 0;
  pointer-events: none;
}

/* Declared only when motion is welcome. The global reduced-motion block forces
   animation-duration to ~0, which for an infinite animation leaves it cycling
   rather than stopping; not declaring it at all actually stops it, and the
   resting state above is already the un-glowed one. */
@media (prefers-reduced-motion: no-preference) {
  .reference__chip::after {
    animation: chip-schimmer 13s var(--ease) infinite;
  }

  .reference__chip:nth-child(2)::after {
    animation-delay: 1.8s;
  }

  .reference__chip:nth-child(3)::after {
    animation-delay: 3.6s;
  }

  .reference__chip:nth-child(4)::after {
    animation-delay: 5.4s;
  }

  .reference__chip:nth-child(5)::after {
    animation-delay: 7.2s;
  }
}

/* Lit for under a fifth of the cycle, so the row spends most of its time at
   rest and the wave reads as a passing highlight rather than a blink. */
@keyframes chip-schimmer {
  0%,
  18%,
  100% {
    opacity: 0;
  }

  5%,
  11% {
    opacity: 1;
  }
}

.reference__actions {
  margin-top: 2rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.75rem;
}

.reference__credit {
  font-size: var(--t-klein);
  color: var(--auf-tief-leise);
}

/* Reference: the backoffice behind browser chrome with the booking app
   overlapping its lower-right corner, as the platform site presents them. The
   browser chrome is part of the screenshot, not drawn in CSS. Both files are
   self-hosted; loading them from the platform domain would put a third-party
   request on the page.

   What fixes the composition is the phone's width relative to the frame — 27%
   of it, which is what makes the two roughly equal in height. The source hits
   that with 67%/18% of a full-width wrapper; this sits in a column beside the
   copy, so both scale up together and the proportion is preserved. */
/* A link to the same place as the call to action beside it. The images keep
   their own alt text, and the anchor carries an aria-label so it announces as
   one destination rather than reading both descriptions as its name. */
.platform {
  display: block;
  text-decoration: none;
  color: inherit;
  position: relative;
  /* The phone hangs below the frame; without this the next section would
     collide with it. */
  padding-bottom: 4%;

  /* The tilt angles are written here by main.js as the pointer moves. Applying
     perspective() inside this element's own transform — rather than the
     perspective property on a parent — lets one element both define the 3D
     space and rotate within it, so no extra wrapper is needed in the markup.
     preserve-3d then puts the frame and the phone in that shared space, which
     is what makes the phone parallax against the frame instead of sliding with
     it. Both angles default to 0, so without JS this is simply a flat scene. */
  transform: perspective(1100px) rotateX(var(--neigung-x, 0deg))
    rotateY(var(--neigung-y, 0deg));
  transform-style: preserve-3d;
  /* Short enough to feel like it is following the pointer rather than catching
     up with it, long enough to smooth the return to flat on the way out. */
  transition: transform 0.25s var(--ease);
}

/* The default ring would hug the column box, which is wider and taller than the
   artwork inside it; a rounded, offset ring reads as belonging to the frame. */
.platform:focus-visible {
  outline-offset: 6px;
  border-radius: 14px;
}

.platform__frame {
  width: 84%;
  aspect-ratio: 1320 / 775;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 28px 90px rgba(0, 0, 0, 0.34);
  overflow: hidden;
}

.platform__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top left;
}

/* Lifted towards the viewer so it swings further than the frame as the scene
   tilts. Kept small: translateZ also magnifies, and too much would inflate the
   phone past the frame proportion the composition depends on. */
.platform__phone {
  position: absolute;
  top: 6%;
  left: 77%;
  width: 22.5%;
  transform: translateZ(38px);
  border-radius: 24px;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
}

.platform__phone img {
  width: 100%;
  height: auto;
}

/* Below the point where the phone would be too small to read, the two sit one
   above the other instead of overlapping — the same answer the platform site
   gives at this width. */
@media (max-width: 820px) {
  .platform {
    display: grid;
    justify-items: center;
    gap: clamp(1.5rem, 4vw, 2.5rem);
    padding-bottom: 0;
  }

  .platform__frame {
    width: 100%;
  }

  .platform__phone {
    position: static;
    width: min(46%, 210px);
  }
}

/* Why MobiTec: positioning, delivery model, tech stack */

.why {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}

.why__lead {
  margin-top: 1.5rem;
}

.why__panel {
  background: var(--weiss);
  border: 1px solid var(--linie);
  border-radius: var(--radius-gross);
  padding: clamp(1.5rem, 3vw, 2.5rem);
  box-shadow: var(--schatten-tief);
}

.why__kicker {
  font-family: var(--schrift-mono);
  font-size: var(--t-mikro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--orange-text);
  margin-bottom: var(--raum-2);
}

.why__claim {
  font-size: var(--t-h3);
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--tinte);
  max-width: 24ch;
}

.why__text {
  margin-top: 1rem;
  font-size: 0.9375rem;
  color: var(--grau);
  max-width: 52ch;
}

.why__stack {
  margin-top: 1.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--linie);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

/* Contact */

.contact {
  background: var(--tief);
  color: var(--auf-tief);
  border-radius: var(--radius-gross);
  padding: clamp(2rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.contact__title {
  font-size: var(--t-h2);
  max-width: 14ch;
  color: var(--weiss);
}

.contact__lead {
  margin-top: 1.25rem;
  color: var(--auf-tief-leise);
}

.contact__rows {
  margin-top: 2.5rem;
}

.contact__row {
  display: block;
  padding-block: 1.125rem;
  border-bottom: 1px solid var(--tief-linie);
  text-decoration: none;
}

.contact__row:first-child {
  padding-top: 0;
}

.contact__row:last-child {
  border-bottom: 0;
}

.contact__label {
  display: block;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--auf-tief-leise);
  margin-bottom: 0.35rem;
}

.contact__value {
  display: block;
  font-weight: 700;
  font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
  letter-spacing: -0.015em;
  color: var(--weiss);
  word-break: break-word;
}

a.contact__row:hover .contact__value {
  color: var(--orange-hell);
}

/* Contact form — lives on --tief, so every control is styled for a dark ground. */

.form {
  display: grid;
  gap: 1.25rem;
  /* Makes the native select dropdown and the checkbox render dark. */
  color-scheme: dark;
}

.form__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.25rem;
}

.form__field {
  display: grid;
  gap: 0.45rem;
}

.form__label {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--auf-tief-leise);
}

/* Decorative: the required attribute is what screen readers announce. */
.form__required {
  color: var(--orange-hell);
}

.form__hint {
  font-size: var(--t-klein);
  color: var(--auf-tief-leise);
}

.form__control {
  width: 100%;
  font: inherit;
  /* Below 16px iOS Safari zooms the viewport when a field takes focus. */
  font-size: 1rem;
  color: var(--weiss);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--tief-linie);
  border-radius: var(--radius);
  padding: 0.75rem 0.9rem;
  transition: border-color 0.18s var(--ease), background-color 0.18s var(--ease);
}

.form__control:hover {
  border-color: var(--blau-hell);
}

/* No extra opacity: --auf-tief-leise is 5.6:1 on the field, 3.5:1 once faded. */
.form__control::placeholder {
  color: var(--auf-tief-leise);
  opacity: 1;
}

textarea.form__control {
  resize: vertical;
  min-height: 6.5rem;
}

.form__consent {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.75rem;
  align-items: start;
  font-size: var(--t-klein);
  line-height: 1.55;
  color: var(--auf-tief-leise);
}

.form__consent input {
  width: 1.1rem;
  height: 1.1rem;
  margin: 0.1rem 0 0;
  accent-color: var(--orange);
}

.form__consent a {
  color: var(--auf-tief);
}

/* Off-screen rather than display:none — a hidden field is not autofilled by bots. */
.form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.form__submit {
  justify-self: start;
  margin-top: 0.25rem;
}

.form__submit[disabled] {
  opacity: 0.6;
  transform: none;
  cursor: progress;
}

/* --orange-hell is the only orange that passes AA on --tief. */
.form__error {
  font-size: var(--t-klein);
  color: var(--orange-hell);
}

.form__note {
  font-size: var(--t-klein);
  color: var(--auf-tief-leise);
}

/* Success dialog */

.danke::backdrop {
  background: rgba(6, 46, 82, 0.72);
}

.danke {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: min(30rem, calc(100vw - 2 * var(--gutter)));
}

.danke__card {
  background: var(--weiss);
  border-radius: var(--radius-gross);
  padding: clamp(1.75rem, 4vw, 2.75rem);
  text-align: center;
}

.danke__title {
  font-size: var(--t-h3);
}

.danke__text {
  margin-top: 0.9rem;
  margin-bottom: 1.75rem;
  color: var(--grau);
  line-height: 1.6;
}

/* Footer */

.footer {
  border-top: 1px solid var(--linie);
  padding-block: 2rem;
  margin-top: clamp(3rem, 5vw, 4.5rem);
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem 2.5rem;
  align-items: center;
  justify-content: space-between;
}

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  font-size: var(--t-klein);
}

.footer__legal a {
  text-decoration: none;
  color: var(--grau);
  border-bottom: 1px solid transparent;
}

.footer__legal a:hover {
  color: var(--tinte);
  border-bottom-color: var(--orange);
}

/* Full width forces the note onto its own row; the flex row-gap spaces it. */
.footer__note {
  font-size: var(--t-klein);
  color: var(--grau);
  width: 100%;
}

/* Responsive */

@media (max-width: 980px) {
  .proof {
    grid-template-columns: 1fr;
  }

  .proof__figure {
    order: -1;
  }
}

@media (max-width: 900px) {
  .approach,
  .contact,
  .reference,
  .why {
    grid-template-columns: 1fr;
  }
}


/* Keep the 820px breakpoint in sync with main.js. */
@media (max-width: 820px) {
  .burger {
    display: inline-flex;
  }

  .nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 0.5rem var(--gutter) 1.25rem;
    background: var(--papier);
    border-bottom: 1px solid var(--linie);
  }

  .nav[data-open='true'] {
    display: flex;
  }

  .nav__link--nur-mobil {
    display: block;
  }

  .nav__link {
    width: 100%;
    padding-block: 0.8rem;
    border-bottom: 1px solid var(--linie);
  }

  /* The panel stacks links, so the bottom border is a divider between rows —
     it cannot double as the active marker the way it does on desktop. */
  .nav__link[aria-current='true'] {
    border-bottom-color: var(--linie);
    box-shadow: inset 3px 0 0 var(--orange);
    padding-left: 0.75rem;
  }

  .nav__link:hover {
    border-bottom-color: var(--linie);
  }

  .nav .btn {
    margin-top: 1rem;
  }

  .lang {
    padding-top: 1rem;
  }

  .hero__ring {
    right: -35%;
    opacity: 0.1;
  }
}

@media (max-width: 560px) {
  .hero__actions .btn {
    width: 100%;
  }

  .form__row {
    grid-template-columns: 1fr;
  }

  .form__submit {
    justify-self: stretch;
  }
}

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

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.001ms !important;
    /* Staggered reveals set transition-delay; without this the delay outlives
       the duration and content would still arrive late, just instantly. */
    transition-delay: 0ms !important;
  }

  .btn:hover {
    transform: none;
  }

  /* The rules above only flatten the duration, which would leave the problem
     cards snapping into photographs under the cursor — louder than the rotation
     it replaces. Someone who asked for less motion gets the plain text row. */
  .pain:hover ~ .pain .pain__inner,
  .pain:has(~ .pain:hover) .pain__inner {
    transform: none;
  }
}

@media print {
  /* Blocks not yet scrolled into view are still transparent on screen; on paper
     there is no scrolling to do, so they must all be present. The steps are
     listed separately because they are staggered one level below the observed
     element, so the attribute selector alone does not reach them. */
  [data-reveal],
  [data-reveal] .step {
    opacity: 1 !important;
    transform: none !important;
  }

  .masthead,
  .hero__actions,
  .hero__ring,
  .hero::before,
  .band--tief::before,
  .contact::before {
    display: none;
  }

  body {
    background: #fff;
  }

  .hero,
  .contact,
  .band--tief,
  .reference {
    background: #fff;
    color: #000;
  }
}
