/* Fercho Webdesign — neovf.com */

@font-face {
  font-family: "Schibsted Grotesk";
  src: url("../fonts/schibsted-grotesk-latin-wght-normal.woff2") format("woff2");
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Martian Mono";
  src: url("../fonts/martian-mono-latin-wght-normal.woff2") format("woff2");
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #0a0b0e;
  --well: #07080a;        /* the screens set into panels: one step darker than the page */
  --surface: #0f1115;
  --surface-2: #151920;
  --line: rgba(255, 255, 255, .09);
  --line-2: rgba(255, 255, 255, .18);
  --ink: #f2f4f8;
  --ink-2: #aab1bf;
  --ink-3: #7d8696;
  --blue: #4c7dff;        /* the logo's rule, lifted for dark backgrounds */
  --blue-solid: #2a50e0;  /* fills that carry white text */
  --ok: #3ddc84;
  --bad: #ff8a8a;

  --sans: "Schibsted Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: "Martian Mono", ui-monospace, "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;

  /* Small type comes in two sizes only. The mono face has two jobs: prices, and the short
     capitalised labels on controls and drawings, which all share one size and one tracking. */
  --fs-xs: .75rem;
  --fs-s: .875rem;
  --track: .14em;

  --gutter: clamp(20px, 5vw, 48px);
  --ease: cubic-bezier(.2, .8, .2, 1);
  --dots: radial-gradient(circle, rgba(255, 255, 255, .07) 1px, transparent 1.3px) 0 0 / 16px 16px;

  /* Panels are built like instruments: a raised plate whose top edge catches a little light, and
     set into it, with a gap all round, the screen that shows the drawing or the animation. */
  --inset: 8px;
  --lip: inset 0 1px 0 rgba(255, 255, 255, .05);

  color-scheme: dark;
}

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

html {
  scroll-padding-top: 72px;
  -webkit-text-size-adjust: 100%;
  /* Clipped here as well as on body: with html left at "visible" the body's value only reaches the
     viewport, and phone browsers still let a page that is wider than the screen be panned sideways. */
  overflow-x: hidden;
  overflow-x: clip;
}
html.anim { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 1rem/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  overflow-x: clip;
}

/* Film grain over the whole page, so the flat dark surfaces do not look like plain screen black.
   One fixed layer that never moves or repaints; the speckle is an SVG filter inside the style
   sheet, so nothing is loaded for it. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 55; pointer-events: none; opacity: .17; /* over the header, under the progress bar */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 .9 0 0 0 -.4'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
}

h1, h2, h3, p { margin: 0; }
a { color: inherit; }
svg { display: block; }
::selection { background: var(--blue-solid); color: #fff; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 4px; }

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

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.sr-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.skip {
  position: fixed; left: 16px; top: -64px; z-index: 100;
  padding: 12px 16px; background: var(--blue-solid); color: #fff;
  font: 500 var(--fs-s)/1 var(--sans); text-decoration: none;
  transition: top .2s;
}
.skip:focus { top: 16px; }

/* ── Logo colours ─────────────────────────────────────────── */

.fw-logo__ink, .lockup__ink { fill: var(--ink); }
.fw-logo__grey, .lockup__grey { fill: #9aa3b5; }
.fw-logo__rule, .lockup__rule { fill: var(--blue); }

/* ── Shared pieces ────────────────────────────────────────── */

/* The rule from the logo, reused as the lead-in for labels and buttons. */
.label {
  display: flex; align-items: center; gap: 16px;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-2);
}
.label__rule { flex: none; width: 48px; height: 3px; background: var(--blue); transform-origin: 0 50%; }

.btn {
  position: relative;
  display: inline-flex; align-items: center; gap: 14px;
  padding: 14px 0;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink); text-decoration: none;
  background: none; border: 0; border-radius: 2px; cursor: pointer;
  transition: transform .2s var(--ease);
}
.btn__rule {
  flex: none; width: 28px; height: 2px; background: var(--blue);
  transform-origin: 100% 50%;
  transition: transform .5s var(--ease);
}
/* The rule runs out to the left instead of the button lifting or glowing; a press sets it down. */
.btn:hover .btn__rule, .btn:focus-visible .btn__rule { transform: scaleX(1.72); }
.btn:active { transform: translateY(1px); }
.btn--solid {
  padding: 18px 24px 18px 20px; background: var(--blue-solid); color: #fff;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16);
  transition: transform .2s var(--ease), background-color .35s var(--ease);
}
.btn--solid:hover { background: #3259ea; }
.btn--solid .btn__rule { background: #fff; }
.btn--solid:focus-visible { outline-color: #fff; outline-offset: 3px; }

/* ── Scroll progress + header ─────────────────────────────── */

.progress {
  position: fixed; left: 0; top: 0; z-index: 60;
  width: 100%; height: 2px; background: var(--blue);
  transform: scaleX(0); transform-origin: 0 50%;
}

.top {
  position: fixed; left: 0; right: 0; top: 0; z-index: 50;
  display: flex; align-items: center; gap: 32px;
  height: 72px; padding-inline: var(--gutter);
  border-bottom: 1px solid transparent;
  transition: background-color .4s, border-color .4s;
}
/* Solid, not a backdrop blur: blurring whatever scrolls underneath is costly on weak GPUs, and a
   see-through bar would show the hero logo ghosting behind the small one during the handoff. */
.top.is-stuck { background: var(--bg); border-bottom-color: var(--line); }
.top__logo { display: block; margin-right: auto; }
.top__logo svg { height: 34px; width: auto; }
/* With animations on, main.js fades this in from the scroll position as the hero logo leaves. */
.anim .top__logo { opacity: 0; visibility: hidden; will-change: transform, opacity; }

.top__nav { display: flex; gap: 32px; }
.top__nav a {
  position: relative; padding: 8px 0;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-2); text-decoration: none;
  transition: color .25s;
}
.top__nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--blue); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .45s var(--ease);
}
/* The link of the section on screen keeps its rule (set by main.js as the page scrolls). */
.top__nav a:hover, .top__nav a:focus-visible, .top__nav a[aria-current] { color: var(--ink); }
.top__nav a:hover::after, .top__nav a:focus-visible::after, .top__nav a[aria-current]::after { transform: scaleX(1); }

.lang { position: relative; display: flex; }
.lang button {
  width: 38px; height: 34px; padding: 0;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track);
  color: var(--ink-3); background: none; border: 0; cursor: pointer;
  transition: color .25s;
}
.lang button:hover, .lang button[aria-pressed="true"] { color: var(--ink); }
.lang__rule {
  position: absolute; left: 8px; bottom: 2px; width: 20px; height: 2px;
  background: var(--blue); transition: transform .45s var(--ease);
}
html[lang="de"] .lang__rule { transform: translateX(38px); }

/* ── Hero ─────────────────────────────────────────────────── */

.hero {
  position: relative;
  display: flex; flex-direction: column; justify-content: center;
  min-height: 100vh; min-height: 100svh;
  /* a little tighter on low screens, so the whole hero still fits a 768px-high laptop */
  padding: clamp(104px, 14vh, 128px) 0 clamp(96px, 14vh, 124px);
  overflow: hidden;
}
.hero__field { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero__inner { position: relative; }
.hero__brand { width: min(100%, 860px); }
.lockup { width: 100%; height: auto; overflow: visible; }
.lockup__ink { stroke: var(--ink); stroke-width: 1.5; stroke-opacity: 0; stroke-linejoin: round; }
.lockup__rule { transform-box: fill-box; transform-origin: 0 50%; }

.hero__lead {
  max-width: 34em; margin-top: clamp(32px, 5vw, 60px);
  font-size: clamp(1.125rem, .8rem + 1.1vw, 1.5rem); line-height: 1.45;
  color: var(--ink-2);
}
.hero__cta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 44px; margin-top: 40px; }

.hero__foot {
  position: absolute; left: 0; right: 0; bottom: 28px;
  display: flex; justify-content: space-between; align-items: flex-end;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-3); pointer-events: none;
}
.hero__scroll { display: flex; align-items: flex-end; gap: 12px; }
.hero__scroll i { width: 1px; height: 40px; background: linear-gradient(var(--blue), transparent); transform-origin: 50% 0; }
.anim .hero__scroll i { animation: drip 2.2s ease-in-out infinite; }
@keyframes drip {
  0% { transform: scaleY(0); opacity: 1; }
  50% { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}

/* ── Sections ─────────────────────────────────────────────── */

.section { position: relative; padding-block: clamp(96px, 13vw, 184px); }

.head { max-width: 820px; }
.head h2 {
  margin-top: 28px;
  font-size: clamp(2.25rem, 1.1rem + 4.4vw, 4.25rem); font-weight: 600;
  line-height: 1.02; letter-spacing: -.04em; text-wrap: balance; overflow-wrap: break-word;
}
/* No "text-wrap: pretty" on paragraphs that animate in themselves (this, the hero text, the
   pricing note): it is a young feature and these were the texts reported to vanish on a phone. */
.head__intro { max-width: 36em; margin-top: 24px; font-size: 1.125rem; color: var(--ink-2); }

/* ── Package cards: each one behaves like a frame in a design tool ── */

/* A card is one wide row: the drawing in its screen on the left, what the size is and what it
   includes next to it, and along the bottom the price with the way to ask for it. */
.cards { display: grid; gap: 72px; margin: 112px 0 0; padding: 0; list-style: none; }

.card {
  position: relative; display: grid;
  grid-template-columns: 316px minmax(0, 5fr) minmax(0, 4fr); grid-template-rows: 1fr auto;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--lip);
}
.card__frame {
  position: absolute; left: -1px; bottom: calc(100% + 10px);
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); color: var(--ink-3);
  transition: color .25s;
}
.card.is-selected .card__frame { color: var(--blue); }

.card__art {
  position: relative; grid-row: 1 / -1; min-height: 252px; margin: var(--inset);
  border: 1px solid var(--line); background: var(--well) var(--dots);
}
/* Own layer: the one-off drawing of the wireframe repaints only this picture, not the card. */
.wf { width: 100%; height: 100%; color: rgba(242, 244, 248, .62); transition: color .4s; will-change: transform; }
.wf__l { fill: none; stroke: currentColor; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; }
.wf__b { fill: rgba(255, 255, 255, .15); }
.wf__a { fill: var(--blue); }
.card.is-selected .card__art > .wf { color: rgba(76, 125, 255, .7); }

/* Light band over the wireframe of a selected card (markup added by main.js).
   .wf-fx__band is a soft-edged window that travels from above the picture to below it. Inside it
   sits a brighter copy of the wireframe that travels the opposite way by the same distance, so
   the copy never appears to move: only the lit strip does. Both are plain transform animations,
   which the browser runs on the compositor without repainting anything. */
/* Sized like the drawing itself (the 320 × 200 picture is centred in its panel), not like the panel. */
.wf-fx {
  position: absolute; inset: 0; margin: auto; width: 100%; aspect-ratio: 320 / 200;
  overflow: hidden; pointer-events: none; opacity: 0; transition: opacity .45s;
}
.card.is-selected .wf-fx { opacity: 1; }
.wf-fx__band {
  position: absolute; left: 0; top: 0; width: 100%; height: 46%; overflow: hidden;
  -webkit-mask-image: linear-gradient(transparent, #000 38%, #000 62%, transparent);
  mask-image: linear-gradient(transparent, #000 38%, #000 62%, transparent);
  transform: translateY(-100%);
  animation: band-window 2.8s linear -.5s infinite paused; /* starts just as it enters the picture */
  will-change: transform;
}
.wf.wf--lit {
  position: absolute; left: 0; top: 0; height: calc(100% / .46);
  color: #d3dfff; transition: none;
  transform: translateY(46%);
  animation: band-picture 2.8s linear -.5s infinite paused;
}
.wf--lit .wf__l { stroke-width: 1.6; }
.wf--lit .wf__b { fill: rgba(110, 150, 255, .5); }
.wf--lit .wf__a { fill: #dbe5ff; }
.card.is-live .wf-fx__band, .card.is-live .wf--lit { animation-play-state: running; }
/* window: from one own-height above the top to the bottom edge (picture height = 100% / .46) */
@keyframes band-window { to { transform: translateY(calc(100% / .46)); } }
/* picture: from one window-height down to one own-height up, i.e. exactly the opposite */
@keyframes band-picture { to { transform: translateY(-100%); } }

.card__body { padding: 30px 32px 30px 24px; }
.card h3, .extra h3, .step h3 { margin-top: 0; font-size: 1.375rem; font-weight: 600; line-height: 1.2; letter-spacing: -.02em; }
.card h3 { font-size: 1.625rem; letter-spacing: -.025em; }
.card__desc { margin-top: 14px; color: var(--ink-2); text-wrap: pretty; }

.card__list { --pad: 11px; margin: 22px 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
.card__list li { position: relative; padding: var(--pad) 0 11px 24px; border-bottom: 1px solid var(--line); }
.card__list li::before { content: ""; position: absolute; left: 0; top: calc(var(--pad) + .74em); width: 12px; height: 2px; background: var(--blue); }
/* In the row the list is a column of its own; its first line sits level with the name beside it. */
.card > .card__list { margin: 30px 32px 30px 0; border-top: 0; }
.card > .card__list li:first-child { --pad: 5px; }
.card > .card__list li:last-child { border-bottom: 0; }

.card__buy {
  grid-column: 2 / -1; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 32px;
  margin: 0 32px 0 24px; padding: 20px 0 16px; border-top: 1px solid var(--line);
}
.card__cost { line-height: 1; white-space: nowrap; }
.card__price {
  font: 600 clamp(2.5rem, 2rem + 1.4vw, 3.25rem)/1 var(--mono); letter-spacing: -.05em;
  font-variant-numeric: tabular-nums;
}
.card__from {
  margin-right: 8px;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3);
}
.card__cur { margin-left: .3em; font-size: .45em; font-weight: 500; letter-spacing: 0; color: var(--ink-3); vertical-align: .75em; }

/* Selection chrome (added and driven by main.js): outline bars, corner handles, size pill.
   Everything here animates through transform and opacity only. */
.sel { position: absolute; inset: -1px; z-index: 2; pointer-events: none; }
.sel__e { position: absolute; background: var(--blue); will-change: transform; }
.sel__e--t, .sel__e--b { left: 0; width: 100%; height: 1.5px; transform: scaleX(0); transform-origin: 0 50%; }
.sel__e--l, .sel__e--r { top: 0; width: 1.5px; height: 100%; transform: scaleY(0); transform-origin: 50% 0; }
.sel__e--t { top: 0; }
.sel__e--b { bottom: 0; }
.sel__e--l { left: 0; }
.sel__e--r { right: 0; }
.sel__h {
  position: absolute; width: 9px; height: 9px; background: #fff; border: 1.5px solid var(--blue);
  transform: scale(0); will-change: transform;
}
.sel__h--tl { left: -4px; top: -4px; }
.sel__h--tr { right: -4px; top: -4px; }
.sel__h--br { right: -4px; bottom: -4px; }
.sel__h--bl { left: -4px; bottom: -4px; }
.sel__size {
  position: absolute; right: 0; bottom: calc(100% + 7px);
  padding: 5px 8px; border-radius: 3px;
  background: var(--blue-solid); color: #fff;
  font: 500 var(--fs-xs)/1 var(--mono); white-space: nowrap;
  opacity: 0; will-change: transform, opacity;
}

.note { max-width: 40em; margin-top: 80px; font-size: 1.125rem; color: var(--ink-2); }
.note__mark {
  color: var(--ink);
  text-decoration-line: underline; text-decoration-color: var(--blue); text-decoration-thickness: 2px;
  text-underline-offset: .3em;
}
.rough .note__mark { text-decoration-line: none; }

/* ── Extras ───────────────────────────────────────────────── */

.extras { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 28px; margin-top: 80px; }
.extra { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--line); box-shadow: var(--lip); }
.extra__body { display: flex; flex-direction: column; flex: 1; padding: 28px; }
.extra h3 { margin-top: 0; }
.extra__body > p:not(.extra__price) { margin-top: 18px; color: var(--ink-2); text-wrap: pretty; }
/* This paragraph and whatever follows it sit at the bottom of the panel; when the panel next to
   it is taller, the spare room opens up above instead of below. */
.extra__body > p.extra__end { margin-top: auto; padding-top: 18px; }
.extra__price { margin-top: 10px; font-size: var(--fs-s); line-height: 1.2; color: var(--ink-3); }
.extra__num { margin-right: .15em; font: 600 2rem/1 var(--mono); letter-spacing: -.04em; word-spacing: -.35em; color: var(--ink); }

/* Both panels carry their screen in the same 340px, so the texts under them start level. */
.uptime {
  display: flex; flex-direction: column;
  height: 340px; padding: 0;
  border-bottom: 1px solid var(--line);
}
/* Status monitor: Conway's Game of Life on the screen's dot grid (see initLife in main.js). The
   canvas only holds the cells; the dots under them are the screen's own background, and the cells
   are spaced so that every other one sits exactly on a dot. */
.life {
  position: relative; flex: 1; min-height: 0; margin: var(--inset) var(--inset) 0; overflow: hidden;
  border: 1px solid var(--line); background: var(--well) var(--dots);
}
.life__grid { position: absolute; left: 0; top: 0; width: 100%; height: 100%; }
.life__read {
  position: absolute; left: 10px; top: 10px; display: flex; gap: 12px;
  padding: 6px 8px; background: rgba(7, 8, 10, .86); border: 1px solid var(--line);
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); font-variant-numeric: tabular-nums;
  color: var(--ink-3); pointer-events: none;
}
html:not(.js) .life__read { display: none; }
@media (hover: hover) and (pointer: fine) { .anim .life { cursor: crosshair; } }

.uptime__cap {
  display: flex; justify-content: space-between; gap: 16px; padding: 15px 28px 14px; white-space: nowrap;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3);
}
/* The sites and whether they are online. A row only says so once status.php (or the address the
   page itself was loaded from) has confirmed it; until then it is just the link. */
.sites { margin: 0; padding: 0; list-style: none; }
.site {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  padding: 11px 28px; border-top: 1px solid var(--line);
}
.site a { color: var(--ink); text-decoration: none; }
.site a:hover { text-decoration: underline; text-decoration-color: var(--blue); text-decoration-thickness: 2px; text-underline-offset: .3em; }
/* A steady green light; the word "online" is there for screen readers only. */
.site__state { display: none; }
.site.is-up .site__state { display: block; }
.site__state i { display: block; width: 8px; height: 8px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px rgba(61, 220, 132, .14); }

/* Workflow diagram: nodes can be pulled around, the wires follow. */
.flow {
  position: relative; height: 323px; margin: var(--inset) var(--inset) 9px; overflow: hidden;
  border: 1px solid var(--line); background: var(--well) var(--dots);
  --dir: row;
}
.flow__wires { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.flow__wires path { fill: none; stroke: rgba(255, 255, 255, .22); stroke-width: 1.5; }
.flow__dot {
  position: absolute; left: 0; top: 0; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
  border-radius: 50%; background: var(--blue); opacity: 0; pointer-events: none;
  will-change: transform, opacity;
}
.flow.is-dragging .flow__dot { visibility: hidden; }
.node {
  position: absolute; left: var(--x); top: var(--y); translate: -50% -50%; z-index: 1;
  padding: 11px 14px; border: 1px solid var(--line-2); border-radius: 6px;
  background: var(--surface-2); box-shadow: var(--lip);
  font: 500 var(--fs-xs)/1 var(--mono); white-space: nowrap;
  cursor: grab; user-select: none; -webkit-user-select: none; touch-action: none;
}
.node:active { cursor: grabbing; }
.node--hub { background: var(--blue-solid); border-color: transparent; color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16); }
.node__ring {
  position: absolute; inset: -5px; border: 1.5px solid var(--blue); border-radius: 10px;
  opacity: 0; pointer-events: none; will-change: transform, opacity;
}
.node[data-node="site"]  { --x: 11%; --y: 50%; }
.node[data-node="n8n"]   { --x: 36%; --y: 50%; }
.node[data-node="ai"]    { --x: 65%; --y: 21%; }
.node[data-node="inv"]   { --x: 65%; --y: 79%; }
.node[data-node="inbox"] { --x: 90%; --y: 50%; }
.flow__hint {
  position: absolute; left: 14px; bottom: 12px;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3);
}
html:not(.js) .flow__hint { display: none; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 22px 0 0; padding: 0; list-style: none; }
.chips li { padding: 8px 11px; border: 1px solid var(--line-2); border-radius: 3px; font-size: var(--fs-s); line-height: 1; color: var(--ink-2); }

/* ── Process ──────────────────────────────────────────────── */

/* A plain numbered list on the page itself, no panels: the number, what happens, and the detail. */
.steps { margin: 80px 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line-2); }
.step {
  display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 4fr) minmax(0, 6fr); align-items: baseline; gap: 8px 28px;
  padding: 30px 0 32px; border-bottom: 1px solid var(--line);
}
.step__no { font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); color: var(--ink-3); }
.step h3 { text-wrap: balance; }
.step p { max-width: 34em; color: var(--ink-2); text-wrap: pretty; }

/* ── Contact ──────────────────────────────────────────────── */

.contact { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: clamp(48px, 7vw, 112px); align-items: start; }
.contact .head h2 { font-size: clamp(2.125rem, 1.2rem + 3.6vw, 3.5rem); line-height: 1.05; letter-spacing: -.035em; }
.contact__direct { margin-top: 28px; color: var(--ink-2); }
.contact__direct a, .foot__meta a {
  color: var(--ink);
  text-decoration-line: underline; text-decoration-color: var(--blue); text-decoration-thickness: 2px;
  text-underline-offset: .3em;
}

.form { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 30px 28px; padding-top: 6px; }
.field { position: relative; display: flex; flex-direction: column; }
.field--wide, .form__foot, .form__status { grid-column: 1 / -1; }
.field label {
  font-size: var(--fs-s); font-weight: 500; line-height: 1.2; color: var(--ink-3);
  transition: color .25s;
}
.field:focus-within label { color: var(--blue); }
.field input, .field select, .field textarea {
  width: 100%; margin: 0; padding: 14px 0 13px;
  font: inherit; color: var(--ink);
  background: transparent; border: 0; border-bottom: 1px solid var(--line-2); border-radius: 0;
  outline: none;
  transition: border-color .25s;
}
.field input:hover, .field select:hover, .field textarea:hover { border-bottom-color: rgba(255, 255, 255, .32); }
.field textarea { min-height: 148px; resize: vertical; line-height: 1.5; }
.field select {
  -webkit-appearance: none; appearance: none; padding-right: 28px; cursor: pointer;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%237d8696' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat right 2px center / 12px 8px;
}
.field select option { background: var(--surface-2); color: var(--ink); }
.field input:-webkit-autofill {
  -webkit-text-fill-color: var(--ink); caret-color: var(--ink);
  box-shadow: 0 0 0 100px var(--bg) inset;
}
/* Focus draws the logo rule under the field. */
.field__line {
  position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--blue);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .55s var(--ease);
}
.field:focus-within .field__line { transform: scaleX(1); }
/* A field that was filled in wrongly keeps its rule, in red, once the visitor has left it.
   Each in a rule of its own: a browser that does not know ":user-invalid" drops only these. */
.field input:user-invalid ~ .field__line { transform: scaleX(1); background: var(--bad); }
.field textarea:user-invalid ~ .field__line { transform: scaleX(1); background: var(--bad); }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form__foot { display: flex; flex-wrap: wrap; align-items: center; gap: 20px 28px; }
.form__privacy { max-width: 26em; font-size: var(--fs-s); line-height: 1.5; color: var(--ink-3); }
.form__status { min-height: 1.6em; color: var(--ink); }
.form__status.is-error { color: var(--bad); }
/* While the message is on its way the button's rule keeps running out and back. */
.form .btn[disabled] { opacity: .7; cursor: default; }
.form .btn[disabled] .btn__rule { animation: sending .7s var(--ease) infinite alternate; }
@keyframes sending { from { transform: scaleX(.3); } to { transform: scaleX(1.72); } }

/* Frame-rate readout, only shown when the address contains ?fps (see main.js). */
.fps {
  position: fixed; left: 12px; bottom: 12px; z-index: 200; padding: 7px 10px;
  background: #000; border: 1px solid var(--line-2); color: var(--ok);
  font: 500 var(--fs-xs)/1 var(--mono); pointer-events: none;
}

/* ── Footer ───────────────────────────────────────────────── */

.foot { padding: 48px 0 52px; border-top: 1px solid var(--line); }
.foot__inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 24px 32px; }
.foot__logo { height: 42px; width: 126px; }
.foot__meta { font-size: var(--fs-s); color: var(--ink-3); }
.foot__links { display: flex; flex-wrap: wrap; gap: 12px 32px; }
.foot__link {
  padding: 6px 0;
  font: 500 var(--fs-xs)/1 var(--mono); letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-2); text-decoration: none;
  background: none; border: 0; cursor: pointer;
  transition: color .25s;
}
.foot__link:hover, .foot__link[aria-current] { color: var(--ink); }
html:not(.js) .foot__link[data-motion] { display: none; }

/* ── Legal pages (impressum.html, datenschutz.html) ───────── */

/* Both languages are in the markup; the one matching <html lang> is shown (see legal.js). */
html[lang="de"] .only-en, html[lang="en"] .only-de { display: none; }

.top__logo img { display: block; height: 34px; width: auto; }
.top.top--page .top__nav { margin-left: auto; }
.top.top--page .top__nav a { display: inline-block; }
.top.top--page .top__logo { margin-right: 0; }

.legal { padding: 152px 0 112px; }
.legal__body { max-width: 44em; }
.legal h1 {
  margin-top: 26px;
  font-size: clamp(2.125rem, 1.2rem + 3.6vw, 3.25rem); font-weight: 600;
  line-height: 1.05; letter-spacing: -.035em;
}
.legal h2 { margin-top: 56px; font-size: 1.375rem; font-weight: 600; line-height: 1.25; letter-spacing: -.02em; }
.legal p, .legal ul, .legal address { margin-top: 16px; color: var(--ink-2); }
.legal address { font-style: normal; line-height: 1.75; color: var(--ink); }
.legal ul { margin-bottom: 0; padding-left: 1.25em; }
.legal li + li { margin-top: 6px; }
.legal strong { font-weight: 600; color: var(--ink); }
.legal a, .form__privacy a {
  color: var(--ink);
  text-decoration-line: underline; text-decoration-color: var(--blue); text-decoration-thickness: 2px;
  text-underline-offset: .3em;
}
.legal__note { font-size: var(--fs-s); color: var(--ink-3) !important; }
.legal h1 + .legal__note { margin-top: 20px; }
.legal__body > .legal__note:last-child { margin-top: 56px; }
/* A detail that still has to be filled in. Loud on purpose: none of these may go live. */
.todo { padding: .05em .4em; border-radius: 2px; background: #ffd84d; color: #14120a; font-weight: 600; }

/* ── Start states (only while animations are enabled) ─────── */

.anim .top, .anim .lockup, .anim .hero__lead, .anim .hero__cta, .anim .hero__foot,
.anim [data-reveal] { opacity: 0; }

/* ── Responsive ───────────────────────────────────────────── */

/* Wide screens: a section's introduction moves out to the right of its headline, level with its
   last line, instead of sitting underneath. */
@media (min-width: 1041px) {
  .head--split { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); align-items: end; column-gap: clamp(48px, 7vw, 104px); max-width: none; }
  .head--split .label { grid-column: 1 / -1; }
  .head--split .head__intro { margin: 0 0 .4em; }
}

@media (max-width: 1040px) {
  /* The row folds to two columns: the drawing, and next to it everything there is to read. */
  .card { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); grid-template-rows: auto 1fr auto; }
  .card__art { min-height: 280px; }
  .card__body { padding: 28px 28px 0 20px; }
  .card > .card__list { margin: 22px 28px 0 20px; border-top: 1px solid var(--line); }
  .card > .card__list li:first-child { --pad: 11px; }
  .card__buy { grid-column: 2; margin: 0 28px 0 20px; padding: 22px 0 18px; border-top: 0; }
  .extras, .contact { grid-template-columns: minmax(0, 1fr); }
  .step { grid-template-columns: 3.5em minmax(0, 1fr); }
  .step p { grid-column: 2; }
  /* The three footer parts no longer fit on one line here: stacked and centred. Plain block layout,
     so the centring does not depend on how a browser sizes a wrapping column flexbox. */
  .foot__inner { display: block; text-align: center; }
  .foot__logo { display: block; margin-inline: auto; }
  .foot__meta, .foot__links { margin-top: 24px; }
  .foot__links { justify-content: center; }
}

@media (max-width: 680px) {
  .top { gap: 20px; height: 64px; }
  .top__nav a:not([href="#contact"]) { display: none; }
  .hero { padding: 104px 0 96px; }
  .lockup__ink { stroke-width: 3; }
  .cards { margin-top: 88px; }
  .card { display: block; }
  .card__art { aspect-ratio: 320 / 200; min-height: 0; }
  .card__body { padding: 22px 22px 0; }
  .card > .card__list { margin: 20px 22px 0; }
  .card__buy { margin: 0 22px; padding: 20px 0 14px; }
  .extra__body { padding: 24px 22px; }
  .uptime { height: 330px; }
  .uptime__cap, .site { padding-inline: 22px; }
  .uptime__cap [data-checked] { display: none; } /* no room for it next to the caption */
  .form { grid-template-columns: minmax(0, 1fr); }
  .steps { margin-top: 56px; }
  .step { grid-template-columns: 2.75em minmax(0, 1fr); padding: 24px 0 26px; }

  .flow { height: 400px; --dir: column; }
  .node[data-node="site"]  { --x: 50%; --y: 11%; }
  .node[data-node="n8n"]   { --x: 50%; --y: 34%; }
  .node[data-node="ai"]    { --x: 24%; --y: 59%; }
  .node[data-node="inv"]   { --x: 68%; --y: 59%; }
  .node[data-node="inbox"] { --x: 50%; --y: 84%; }
}

/* Reduced mode (no "anim" class, chosen with the footer switch) drops what plays by itself
   or moves things across the page: the entrances, the hero drawing, the loops. Feedback to the
   visitor's own pointer (button rules, outlines, colour fades) stays animated in both modes,
   so nothing here switches transitions off. */
