/* ================= fonts (original product files) ================= */
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("/fonts/inter-latin-400-normal.woff2") format("woff2")}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("/fonts/inter-latin-600-normal.woff2") format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:400;font-display:swap;src:url("/fonts/jetbrainsmono-latin-400.woff2") format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:500;font-display:swap;src:url("/fonts/jetbrainsmono-latin-500.woff2") format("woff2")}

/* ================= tokens ================= */
:root{
  --schwarz:#14110F;
  --wolke:#FAFAFA;
  --weiss:#ffffff;
  --fuge:#F0F0F0;
  --schiefer:#8AA3B8;
  --hover-blau:#7A93A8;
  --surface-platzhalter:#F5F5F5;
  /* the stage light: ONE gradient, worn by the hero card and the closing block (§8 rule 3).
     It paints no area — the surface underneath stays exactly wolke — it only lifts one. */
  --stage-light:radial-gradient(ellipse at center,
    rgba(138,163,184,0.18) 0%,
    rgba(20,17,15,0.03) 45%,
    transparent 72%);
  --ink-65:rgba(20,17,15,.65);
  --ink-40:rgba(20,17,15,.4);
  --ink-16:rgba(20,17,15,.16); /* fold labels: below reading weight on purpose, see §3 second layer */
  --ink-8:rgba(20,17,15,.08);  /* the only edge the video frames carry */
  --dim-60:rgba(0,0,0,0.6);
  --dim-35:rgba(0,0,0,0.35);
  --dim-32:rgba(20,17,15,0.32);
  /* voice on the black tiles (prints section + footer) */
  --hell-90:rgba(255,255,255,0.9);
  --hell-80:rgba(255,255,255,0.8);
  --hell-60:rgba(255,255,255,0.6);
  --hell-45:rgba(255,255,255,0.45);
  --hell-16:rgba(255,255,255,0.16);
  --hairline:rgba(0,0,0,0.07);
  --r-12:12px;--r-8:8px;--r-7:7px;--r-6:6px;--r-pill:980px;
  --tb-h:52px; /* topbar lock (the 32px control row sets the line) — also drives the grid-row collapse */
  /* the studio frame, notch-aware. max(...) means devices without a cutout keep the
     exact 10px and nothing shifts; only where the system reports an inset does the
     frame grow. --tile-w is the resulting tile width and is the single source for
     every formula that used to hardcode "100vw - 20px" (hero card, tech pack
     expansion, prints raster) — otherwise those would compute against a tile that
     is wider than the one actually drawn. */
  --frame:10px;
  --frame-t:max(var(--frame),env(safe-area-inset-top));
  --frame-r:max(var(--frame),env(safe-area-inset-right));
  --frame-b:max(var(--frame),env(safe-area-inset-bottom));
  --frame-l:max(var(--frame),env(safe-area-inset-left));
  --frame-x:max(var(--frame),env(safe-area-inset-left),env(safe-area-inset-right)); /* symmetric, for the centred glass bar */
  --tile-w:calc(100vw - var(--frame-l) - var(--frame-r));
  --wolke-glas:rgba(250,250,250,0.72); /* Wolke at 72% for the glass navbar */
  /* measuring stage (section 4): 1:1 studio dimension-line values (interaction-handler.js / configurator-utils.js) */
  --mess-halo:rgba(0,0,0,0.16);        /* 3px halo under the measurement line */
  --mess-strich:rgba(255,255,255,0.75); /* 1px dashed line on top */
  --mess-pill-bg:rgba(30,33,39,0.2);
  --mess-pill-ring:rgba(255,255,255,0.2);
  --mess-pill-text:rgba(255,255,255,0.95);
  --mess-select:rgb(178,204,255); /* fabric.js default selection blue — the studio runs stock fabric controls */
  /* studio control chrome (section 4 tools): 1:1 configurator.scss / color_tool.scss values */
  --ctl-border:rgba(0,0,0,0.12);
  --ctl-border-hover:rgba(0,0,0,0.3);
  --ctl-check-bg:rgba(138,163,184,0.12); /* checked buttons: soft schiefer */
  --ctl-check-border:#C3D0DC;            /* --schiefer-soft in the studio */
  --ctl-dot-hover:#D0D0D0;
  --ctl-seg-active:rgba(0,0,0,0.06); /* active pill segment (color-tabs) */
  --ctl-thumb-ring:rgba(0,0,0,0.13);
  --ctl-track:rgba(0,0,0,0.14); /* unfilled wash-track part (tool.scss gradient) */
  --ctl-sub:rgba(0,0,0,0.45);            /* tool sub line + wash value */
  --lh-display:0.92; /* tight display leading, every headline (Mo 03.08.) */
  --ease:cubic-bezier(0.16,1,0.3,1);
  --breath-in:160px;   /* tile entry -> headline */
  --breath-head:96px;  /* headline block -> content */
  --breath-out:128px;  /* content -> tile end */
  --breath-in-compact:96px;   /* ~60% of --breath-in: size-rhythm tiles (guardrails/tech pack/founders/faq) */
  --breath-head-compact:58px; /* ~60% of --breath-head */
  --breath-out-compact:77px;  /* ~60% of --breath-out */
  /* Fallback nur fuer den Moment vor dem ersten measure() und fuer den Fall ohne JS.
     Im Betrieb schreibt app.js beide als PIXEL — sonst rechnet CSS dieselbe Laenge ein
     zweites Mal, und auf iOS ist `vh` die grosse Ansicht waehrend innerHeight die
     aktuelle ist. Die Differenz blieb als leere Fuge stehen (Mo 14.08., docs/decisions.md). */
  --reveal:80vh; /* phase 1: p runs 0 -> 1 */
  --hold:50vh;   /* phase 2: p stays 1, scene rests */
  --s4-reveal:0vh; /* s4 scene: growth completes exactly AT the pin (sync JS S4_REVEAL 0); the whole growth happens on approach (JS S4_START) */
  --s4-hold:40vh;   /* s4 scene: pinned rest while tools fade + demo runs (sync JS S4_HOLD) */
  /* Kopfluft der Hero, fest statt prozentual (Mo 20.08.). Der Wert ist so gesetzt, dass
     die Headline dort steht, wo sie im 15-Zoll-Layout stand — dem Schirm, auf dem die
     Sektion entworfen wurde. */
  --hero-head-top:135px;
  --card-w:min(calc(var(--tile-w)*0.76),1080px);
  --card-h:calc(var(--card-w)*0.5806); /* 929/1600 */
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --sans:"Inter",system-ui,-apple-system,sans-serif;
}


/* ================= base (studio layout: 10px frame, 10px gap, r12) ================= */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html,body{
  overflow-x:clip;
  overscroll-behavior-y:none; /* studio rule: no rubber-band, no scroll chaining */
}
body{
  margin:0;
  overflow:hidden; /* the window never scrolls; content scrolls below the topbar */
  font-family:var(--sans);
  background:var(--fuge);
  color:var(--schwarz);
  line-height:1.5;
  letter-spacing:-0.03em; /* brand book: -3% on all Inter text (deliberate deviation from the studio's -0.02em); mono labels override with their positive tracking */
  -webkit-font-smoothing:antialiased;
}
.page{
  /* dvh again (Mo 15.08.). svh froze the frame and with it the toolbar jolt, but it left a
     ~90px band of page background under the last tile — and the answer to that is not a
     shorter frame, it is showing the reader that the page continues. Below 900px the hero is
     no longer a viewport stage at all (see the block near .hero-wrap), so nothing there is
     laid out against the window height and the toolbar has nothing left to move. */
  height:100dvh;
  position:relative; /* Bezug fuer die Topbar-Ebene */
  display:grid;
  /* EINE Zeile, konstant (Mo 26.08.). Bis dahin kollabierten Topbar-Zeile und Fuge linear
     mit --hp — und damit wuchs der Scrollport WAEHREND der Finger zog: gemessen bei 390x844
     von 762 auf 824px, also 62px auf 610px Hero-Strecke. Der Inhalt lief dem Finger um 10 %
     davon, und genau dort, wo --hp die 1 erreicht, endete der Versatz abrupt — das war der
     Ruck am Uebergang zum Configurator. Dazu ein Grid-Reflow des ganzen Seitencontainers
     pro Scroll-Frame statt eines Transforms.
     Jetzt liegt die Topbar als eigene Ebene UEBER dem Scrollbereich, und die 62px, die sie
     belegt, stecken als Polster im ersten Kachel-Wrapper. Sie scrollen damit weg wie jeder
     andere Inhalt, statt unter dem Finger zu verschwinden. Der Scrollport ist konstant, es
     gibt keinen Reflow, und --hp bleibt eine reine lineare Funktion der Scroll-Position
     auf jeder Breite (CLAUDE.md §2/§3) — nur bewegt es jetzt ausschliesslich Transforms. */
  grid-template-rows:minmax(0,1fr);
  padding:var(--frame-t) var(--frame-r) var(--frame-b) var(--frame-l);
}
/* Die Topbar sitzt im Rahmen, nicht in einer Grid-Zeile: sie darf keine Hoehe mehr belegen,
   die sich beim Scrollen aendert. */
.topbar{
  position:absolute;z-index:5;
  top:var(--frame-t);left:var(--frame-l);right:var(--frame-r);
}
/* Die 62px, die die Topbar verdeckt, als Polster im ersten Wrapper — dadurch beginnt die
   Hero-Kachel exakt dort, wo sie vorher begann, und das Polster scrollt normal weg. */
.hero-wrap{padding-top:calc(var(--tb-h) + 10px)}
.scroll{
  min-height:0; /* let the 1fr cell shrink */
  border-radius:var(--r-12); /* clipped edges stay rounded while tiles scroll past */
  overflow-y:auto;overflow-x:hidden;
  overscroll-behavior:none;
  scrollbar-width:none; /* studio: hidden scrollbar */
  display:flex;flex-direction:column;gap:10px;
}
.scroll::-webkit-scrollbar{width:0}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
:focus-visible{outline:2px solid var(--schiefer);outline-offset:2px;border-radius:var(--r-8)}

.tile{background:var(--wolke);border-radius:var(--r-12)}

/* mono label */
.ph-label{color:var(--dim-35)}
.label{
  font-family:var(--mono);
  font-size:12px;font-weight:400;
  text-transform:uppercase;letter-spacing:.12em;
}

/* ================= topbar (studio tile at rest, slides out with the hero reveal) ================= */
.topbar{
  height:var(--tb-h);
  padding:10px 24px; /* same logo inset as the glassbar */
  display:flex;align-items:center;
  transform:translateY(calc(var(--hp,0)*(var(--tb-h) + 20px)*-1)); /* up and out, linear with p */
  will-change:transform;
}
.topbar.out{visibility:hidden} /* fully off-screen: drop out of the tab order */
.topbar-inner{
  position:relative; /* centering layer for the menu */
  width:100%;max-width:1080px;margin-inline:auto; /* logo + CTA sit on the 1080 content column, not at the screen edge */
  display:flex;align-items:center;
}
/* centered section menu (both nav states): orientation copy first, navigation second.
   transform-free centering; the layer ignores pointer events except on the links */
.nav-menu{
  position:absolute;left:0;right:0;
  display:flex;justify-content:center;gap:28px;
  pointer-events:none;
}
.nav-menu a{
  pointer-events:auto;
  padding:6px 2px;
  font-size:13px;font-weight:500;line-height:1.2;
  color:var(--dim-60);
  transition:color 0.15s ease;
}
.nav-menu a:hover{color:var(--hover-blau)}
/* Under 900px the three section anchors are off, and that is the decision, not an open
   task (Mo 15.08.). The NAVBAR itself is unaffected and runs in both states on a phone
   exactly as it does on a desktop — logo, Log in, Start Free. What goes is only the
   Configurator/Tech Pack/Pricing row: the page is one linear argument, a phone reads it
   by scrolling, and the CTA is present in both navbar states anyway. No burger. */
@media (max-width:899px){.nav-menu{display:none}}
/* Navbar-Uebergabe auf dem Telefon (Mo 15.08.): dieselbe Bewegung wie am Desktop, aber
   ZEITgesteuert statt scrollgesteuert. Am Desktop laeuft --hp linear mit der Hero-Szene;
   mobil gibt es keine Szene, und als die Uebergabe hier an der Scrollstrecke hing, wurde
   der Scroll-Container waehrend des Ziehens 62px hoeher — die Seite lief dem Finger davon
   (gemessen: bei 160px Scroll war der Inhalt 222px gewandert). Jetzt kippt --hp an EINEM
   Punkt und Zeile, Fuge und Leiste animieren in 340ms. Der Versatz bleibt, aber er ist
   eine einmalige Bewegung an einer definierten Stelle statt eines Widerstands ueber die
   ganze Strecke. */
@media (max-width:899px){
  /* --hp bekommt hier bewusst KEINEN Uebergang: es laeuft linear mit dem Scrollen wie am
     Desktop, und ein Uebergang auf einem scrollgetriebenen Wert liefe dem Finger hinterher
     (§3). Die Glasleiste ist ein Zustand und darf einen haben — siehe unten. */
}
.topbar .logo,.glassbar .logo{height:24px;width:auto;opacity:.85}
/* glass navbar: returns once section 2 is reached; content scrolls under it.
   floating bar like apple's local nav: content-column width, detached from the edges */
.glassbar{
  position:fixed;top:var(--frame-t);left:0;right:0;z-index:40;
  width:min(1080px,calc(100vw - 2*var(--frame-x))); /* the 1080 content column; tile width on narrow screens. Centred, so it clears the LARGER of the two side insets */
  margin-inline:auto;
  height:var(--tb-h);
  padding:10px 24px;
  display:flex;align-items:center;
  background:var(--wolke-glas);
  -webkit-backdrop-filter:saturate(180%) blur(20px); /* apple's exact nav glass: saturate makes it lively, not milky */
  backdrop-filter:saturate(180%) blur(20px);
  border:1px solid var(--hairline); /* free-floating: hairline all around, no shadow (page rule: no shadows) */
  border-radius:var(--r-12);
  transform:translateY(calc(-100% - 10px)); /* clears its own 10px top offset */
  visibility:hidden;
  transition:transform 0.4s var(--ease),visibility 0s 0.4s;
  will-change:transform;
}
.glassbar.in{transform:none;visibility:visible;transition:transform 0.4s var(--ease)}
/* Auf dem Telefon faehrt sie eine Spur ruhiger ein (Mo 15.08.): 0,62s statt 0,4s, dieselbe
   Kurve. Sie haengt an einem Zustand, nicht an der Scrollstrecke — ein Uebergang ist hier
   also erlaubt, anders als bei --hp, das linear mit dem Finger laeuft (§3). */
@media (max-width:899px){
  .glassbar{transition:transform .62s var(--ease),visibility 0s .62s}
  .glassbar.in{transition:transform .62s var(--ease)}
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glassbar{background:var(--wolke)} /* solid fallback without glass */
}
@media (prefers-reduced-transparency:reduce){
  .glassbar{background:var(--wolke);-webkit-backdrop-filter:none;backdrop-filter:none} /* honor the user preference */
}
/* quiet companion to the nav CTA: returning users go straight to the studio */
.nav-login{
  margin-left:auto;
  padding:6px 10px;
  font-size:13px;font-weight:500;line-height:1.2;
  color:var(--dim-60);
  transition:color 0.15s ease;
}
.nav-login:hover{color:var(--hover-blau)}
.topbar-cta{
  margin-left:12px;
  display:inline-flex;align-items:center;
  padding:6px 13px; /* symmetric: no plus icon (deliberate deviation from the studio Create button) */
  border:0;border-radius:var(--r-7);
  text-decoration:none; /* colour, hover and timing come from .btn */
}
.topbar-cta span{font-family:var(--sans);font-size:13px;font-weight:500;line-height:1.2}
/* ghost variant (resting tile nav): outline only, fills on hover (Mo 02.08.) */
.topbar-cta--ghost{ /* not a .btn: carries no fill of its own */
  background:transparent;color:var(--schwarz);
  border:1px solid var(--schwarz);
  padding:5px 12px; /* border compensates to the filled sibling's box */
  transition:background 0.15s ease,color 0.15s ease;
}
.topbar-cta--ghost:hover{background:var(--schwarz);color:var(--weiss)}

/* ================= hero: scroll reserve + sticky stage ================= */
.hero-wrap{
  /* PURE PIN, identical in build to .s4-wrap (Mo 14.08.): one scrollport plus the scene
     reserve, and the tile inside sticks as exactly one scrollport. CONSTANT — nothing here
     may change while scrolling: this box sets the page length, and a length that moves
     under the finger is what made the handover feel like it hung (Mo 09.08.).
     The --peek that used to shorten this box during the hold is gone; it produced a joint
     that travelled with the scroll instead of staying at 10px (docs/decisions.md). */
  flex-shrink:0;
  /* + die 62px des Topbar-Polsters (Mo 26.08.): das Polster liegt seit dem Navbar-Umbau
     IN diesem Wrapper, und mit border-box wuerde es sonst von der Szenenhoehe abgehen —
     die gepinnte Kachel waere 62px kuerzer als der Scrollport und liesse unten eine Luecke. */
  height:calc(100% + var(--reveal) + var(--hold) + var(--tb-h) + 10px); /* scrollport + scene reserve */
}
.hero{
  position:sticky;top:0; /* pins to the top of the scroll area */
  height:calc(100% - var(--reveal) - var(--hold)); /* = exactly one scrollport, like .s4 */
  overflow:hidden;
  display:flex;flex-direction:column;
  align-items:center;
  text-align:center;
  padding:48px 24px 0;
}
/* Der Textblock haengt an einer FESTEN Kopfluft, nicht mehr an Prozenten der Buehne
   (Mo 20.08.). Die Buehne, ihr Pin und die ganze Karten-Choreografie bleiben unangetastet
   — es war nie der Pin, der die Headline wandern liess, sondern DIESE Zeile: `top:8%` plus
   `justify-content:center` zwischen 8 % und 0.62 x Kartenhoehe zentrierte den Block in einer
   Flaeche, die mit dem Schirm waechst. Gemessen lief der Weg von der Kachelkante zur
   Headline damit auf 73 / 135 / 187 / 381px bei 1280 / 1512 / 1920 / 2560.
   --hero-head-top ist der Abstand ab der Polsterkante der Kachel; zusammen mit den 48px
   Kachelpolster ergibt er den Wert, den das 15-Zoll-Layout hatte. */
.hero-head{
  position:absolute;left:24px;right:24px;
  top:var(--hero-head-top);
  bottom:auto;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
  padding:0;
  will-change:transform,opacity;
  z-index:2;
}
/* fold labels (Mo 13.08.): ONE labelled fold edge per side, not three columns. The
   container itself turns vertical, so the three terms sit one after another in the normal
   text flow and read as a single thin line running down the hero edge.
   nowrap is load-bearing: without it a long run wraps onto a second "line", which in
   vertical-rl means a second COLUMN — exactly the three-column look this replaces.
   Flex, not inline flow: in vertical-rl the flex main axis runs top to bottom, so the three
   terms stack down the edge and `gap` spaces them vertically. That also makes the gap
   RESPONSIVE — it gives way on short viewports before anything can be cut off.
   Both sides are pinned to the same 40px and the same 50% centre line; the two lines differ
   only in length, because the right-hand copy is shorter. That is the copy, not the layout.
   Decorative, so aria-hidden and out of the tab order; --ink-16 keeps them under reading
   weight, which is the whole point — they may only surface on a second look.
   z1: below headline (z2) and card (z3). */
.hero-falz{
  position:absolute;top:50%;
  display:flex;align-items:center;
  gap:clamp(16px,6vh,48px);
  writing-mode:vertical-rl;
  white-space:nowrap; /* load-bearing: a wrap in vertical-rl means a second COLUMN */
  font-family:var(--mono);
  font-size:10px;font-weight:400;
  text-transform:uppercase;letter-spacing:.22em;
  color:var(--ink-16);
  opacity:var(--falz-p,1); /* scroll-driven, written by app.js apply() — see the hero scene */
  z-index:1;
  pointer-events:none;user-select:none;-webkit-user-select:none;
}
.hero-falz--l{left:40px;transform:translateY(-50%) rotate(180deg)}
.hero-falz--r{right:40px;transform:translateY(-50%)}
@media (max-width:1099px){.hero-falz{display:none}} /* no room beside the card, and no room to be quiet */
/* and no room DOWN the edge either: the left line needs ~670px, so below this height the
   responsive gap has nothing left to give and words would be cut off. Measured, not guessed
   — clipping starts just under 760px of viewport height (docs/reference.md). */
@media (max-height:767px){.hero-falz{display:none}}
h1{
  font-size:clamp(44px,5.5vw,84px);
  font-weight:600;line-height:var(--lh-display);
}
h1 .line-mask,.guardrails h2 .line-mask,.autonomy h2 .line-mask,.founders h2 .line-mask,.faq h2 .line-mask,.closing h2 .line-mask,.process h2 .line-mask,.s2-head .line-mask,.s3-head .line-mask,.s4-head .line-mask,.pricing-head .line-mask{display:block;overflow:hidden;padding:.15em 0;margin:-.15em 0} /* symmetric bleed: with --lh-display 0.92 the ink juts ~.145em past the box on BOTH sides (bugfix 03.08.) */
h1 .line,.guardrails h2 .line,.autonomy h2 .line,.founders h2 .line,.faq h2 .line,.closing h2 .line,.process h2 .line,.s2-head .line,.s3-head .line,.s4-head .line,.pricing-head .line{display:block}
.hero-sub{
  font-size:clamp(17px,1.4vw,20px);font-weight:400;color:var(--dim-60);
  max-width:760px;margin-top:24px;margin-inline:auto; /* one line for the 03.08. subline on desktop */
}

/* hero CTA pill */
/* ================= .btn — THE page's filled CTA (Mo 13.08.) =================
   ONE definition of the look: flat schwarz, wolke label, schiefer on hover, 150ms.
   No gradient, no glow, no shadow, and no local override anywhere — a CTA that wants to
   look filled wears this class and nothing else touches its colour.
   Geometry stays with the context class (.hero-cta, .topbar-cta, ...): the navbar button
   is deliberately smaller than a body CTA, so size is the one thing that is NOT shared.
   The two GHOSTS on the page are not .btn and never take it: .topbar-cta--ghost in the
   resting tile nav, and .autonomy-cta, which §4 requires to be ghost because the glass
   navbar carries the filled Start Free in that same field of view. */
.btn{
  background:var(--schwarz);
  color:var(--wolke);
  transition:background 0.15s ease;
}
.btn:hover{background:var(--schiefer)}
.btn:active{transform:scale(0.98)}
/* ONE shape for every body CTA (hero, s4, autonomy, closing): the studio "Generate
   Tech Pack" form. Colour comes from .btn — this rule only carries the box. */
.hero-cta,.s4-cta,.autonomy-cta,.closing-cta{
  display:inline-flex;align-items:center;justify-content:center;
  padding:9px 22px;
  border-radius:var(--r-8); /* studio button shape (Generate Tech Pack), not the pill */
  font-size:15px;font-weight:600;
}
.autonomy-cta:active{transform:scale(0.98)} /* the ghost is not a .btn, so it needs its own */
/* the sub-to-CTA distance, the same at both ends of the page */
.hero-cta,.closing-cta{margin-top:40px}

/* quiet secondary (apple primary+secondary pair): a fine text link, not a second button */
.hero-more{
  display:inline-flex;align-items:center;gap:4px;
  /* it opens the demo modal now, so it is a <button>; these four lines only strip the
     UA chrome — size, colour, spacing and the entrance are untouched (Mo 07.08.) */
  background:none;border:0;padding:0;cursor:pointer;
  font-family:inherit;
  margin-top:16px;
  font-size:11px;font-weight:400; /* fine + half the presence (Mo 04.08.) */
  /* --dim-60 is the lowest rung on the dimmed-text ladder that cleared AA over the
     background field (5.36:1; --dim-32 measured 2.05:1). --ink-65 would add 0.02 for a
     darker tone, so it stays at the step that is just enough (Mo 05.08.). The field it
     was measured against is gone (13.08.) — over plain wolke the value only improves.
     The chevron inherits it: its stroke is currentColor. */
  color:var(--dim-60);
  transition:color 0.15s ease;
}
.hero-more:hover{color:var(--hover-blau)}
.hero-more svg{display:block;width:10px;height:10px}

/* configurator preview card: clipped by the tile edge, revealed on scroll */
.stage-slot{
  margin-top:auto; /* card sits flush with the tile edge; JS offsets it down */
  width:min(76%,1080px);
  will-change:transform;
  flex-shrink:0;
}
/* ONE frame shape, used by the hero card, the tech pack card and the video modal — the
   modal is meant to look exactly like the hero card in its end state, so it must not
   restate these values and it follows every change made here (CLAUDE.md, video modal).
   Mo 13.08.: the joint-grey bezel is gone. One hairline, r14, the video fills the frame.
   The outer box is unchanged — border-box plus aspect-ratio means dropping the 12px
   padding moves the video, not the card, so the hero scene still measures the same
   geometry. */
.stage-video,.vmodal-stage,.s2-media{
  position:relative;
  width:100%;
  aspect-ratio:1600/929; /* card ratio; the current 16:9 clip is cover-cropped a touch (see CLAUDE.md) */
  border:1px solid var(--ink-8);
  border-radius:14px;
  overflow:hidden;
  display:flex;align-items:center;justify-content:center;
}
.stage-video video,.vmodal-stage video,.s2-media video{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;
  background:var(--surface-platzhalter);
}
/* the stage light (Mo 13.08.) behind the hero card, centred a touch above its top edge so
   the card reads as lit from above rather than pasted on. z-index -1 keeps it behind the
   card inside .stage-slot's own stacking context. */
.stage-slot::before{
  content:'';position:absolute;z-index:-1;
  left:50%;top:-3%;
  width:120%;height:80%;
  transform:translate(-50%,-50%);
  background:var(--stage-light);
  pointer-events:none;
}
.stage-slot{position:relative;z-index:3} /* card slides over text and dim layer */
@media (max-width:899px){
  :root{--card-w:min(calc(var(--tile-w)*0.92),1080px)}
  .stage-slot{width:min(92%,1080px)}
  .hero-head{bottom:calc(0.69*var(--card-h))}
}

/* ================= hero on a phone: a tile, not a stage (Mo 15.08.) =================
   A pin needs a scroll reserve UNDER the tile, and that reserve is exactly the space the
   next section was supposed to occupy — so a pinned hero can never show what follows it.
   Below 900px the pin is therefore gone: the tile is as tall as its content, the guardrails
   tile follows it across the page's own 10px joint, and the reader sees at a glance that it
   continues. It also ends the whole viewport-height question — nothing here is measured
   against the window any more, so the address bar has nothing left to move. The card grows
   as it comes up the screen instead, the same gesture the garment makes in the configurator.
   Desktop keeps the stage unchanged. */
@media (max-width:899px){
  .hero-wrap{height:auto}
  .hero{
    position:static;
    height:auto;
    padding:var(--breath-in-compact) 24px var(--breath-out-compact);
  }
  .hero-head{position:static;margin:0 auto;padding:0}
  /* the run between the last copy line and the card, in the open instead of closed by JS */
  .stage-slot{
    margin-top:32px; /* Mo 15.08.: was 56, the card belongs closer to the line above it */
    width:min(100%,1080px); /* Mo 15.08.: was 92% — the card takes the tile's full content width */
    /* grown from the TOP edge: app.js reads this rectangle every frame to work out how far
       the card has risen, and a centre origin would move the very edge it is reading */
    transform-origin:50% 0;
  }
}

/* Hero-Subline mobil zweizeilig (Mo 26.08.): der Mittelsatz faellt weg, die beiden
   uebrigen stehen untereinander. Ein Element, drei Spans — der Desktop rendert weiterhin
   denselben Satz aus derselben Quelle, es gibt keinen zweiten Textblock, der auseinander-
   laufen koennte. Die Spans sind am Desktop inline und damit wirkungslos. */
@media (max-width:899px){
  .hero-sub .sub-mid{display:none}
  .hero-sub > span{display:block}
  /* Guardrails: der Rueckbezug-Satz faellt mobil weg (Mo 26.08.), damit der Kopf nicht
     ueber vier Zeilen laeuft. Dieselbe Bauweise wie bei der Hero-Subline — ein Element,
     ein Span darin, kein zweiter Textblock, der auseinanderlaufen koennte. Der Span ist
     am Desktop inline und damit wirkungslos. */
  .guardrails-sub .sub-lead{display:none}
}

/* ================= mobile Entrances (Mo 26.08.) =================
   Ersetzen die scrollgebundenen Skalierungen von Hero-Karte und Kleidungsstueck. Einmalig,
   beim ersten Sichtbarwerden, danach nie wieder — app.js setzt .is-in und trennt den
   Observer. Nur Transform und Opacity, also keine Layout-Arbeit im Scroll.
   pointer:coarse zusaetzlich zur Breite: ein schmales Desktop-Fenster soll die Buehne
   behalten, ein Tablet im Querformat den Entrance bekommen. */
@media (max-width:899px), (pointer:coarse){
  .mob-entrance{opacity:0;transform:translateY(16px)}
  .mob-entrance.is-in{
    opacity:1;transform:none;
    transition:opacity .4s ease-out, transform .4s ease-out;
  }
}
@media (prefers-reduced-motion:reduce){
  /* Inhalte sofort sichtbar, kein Uebergang. */
  .mob-entrance,.mob-entrance.is-in{opacity:1;transform:none;transition:none}
}

/* ================= Expand-Knopf auf den Videokarten (Mo 26.08.) =================
   Oeffnet das Modal mit demselben Video an derselben Stelle. Er liegt IN der Karte, die
   selbst Play/Pause schaltet — der Tap darf also nie durchschlagen (app.js stoppt die
   Weitergabe). 40x40 Trefferflaeche, Glas wie die Navbar, Radius 8. */
.card-expand{
  position:absolute;right:12px;bottom:12px;z-index:4;
  width:40px;height:40px;
  display:flex;align-items:center;justify-content:center;
  padding:0;border:0;border-radius:var(--r-8);
  background:rgba(250,250,250,.92);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  color:var(--schwarz);
  cursor:pointer;
  opacity:0;visibility:hidden;
  transition:opacity .2s var(--ease), visibility 0s .2s;
}
.card-expand svg{display:block;width:18px;height:18px}
/* Maus: erscheint beim Ueberfahren der KARTE, nicht des Knopfes — sonst muesste man ihn
   treffen, um zu sehen, dass es ihn gibt. Tastatur: immer sichtbar, sobald er Fokus hat. */
.stage-video:hover .card-expand,
.s2-media:hover .card-expand,
.card-expand:focus-visible{opacity:1;visibility:visible;transition:opacity .2s var(--ease)}
/* Touch: kein Hover zum Aufdecken, also uebernimmt app.js die Sichtbarkeit — wie die
   iOS-Videosteuerung (Mo 26.08.). .is-shown wird beim Sektionseintritt und bei jedem Tap
   gesetzt und nach 2,5s wieder entfernt; solange das Video pausiert, bleibt es stehen.
   Die Regel steht NACH der Hover-Regel, damit sie auf Touch gewinnt. */
@media (pointer:coarse){
  .card-expand{opacity:0;visibility:hidden;transition:opacity .25s ease-out, visibility 0s .25s}
  .card-expand.is-shown{opacity:1;visibility:visible;transition:opacity .25s ease-out}
}

/* ================= ticker: a flat band between hero and guardrails (Mo 06.08.) —
   the studio's capability vocabulary drifting by. Deliberately casual: no targets, no
   hover, no entrance. DOCUMENTED EXCEPTION to §4's one-thing-moves rule: this is the
   page's only perpetual motion, explicitly ordered. The mask lives on an INNER wrapper:
   on the tile itself it would fade the wolke fill into the joint at both edges. */
.ticker{
  flex-shrink:0;
  padding:14px 0; /* flat: a band, not a section — no breath tokens on purpose */
  /* Schiefer als FLAECHE — die einzige der Seite und ein bewusster Bruch von §3 (Mo 15.08.).
     Der Anlass ist gemessen: hinter Autonomy laufen 2571px = 2,8 Bildschirme hell am Stueck,
     genauso lang wie der Auftakt vor der schwarzen Guardrails. Autonomy selbst kann nicht
     dunkel werden (Founders steht direkt darueber), FAQ traegt zu viel Fliesstext, und
     Pricing haette dann eine schwarze Karte auf schwarzem Grund. Das Band ist der einzige
     freie Hebel — 42px hoch, ohne Nachbarschaftskollision, und es trennt genau dort, wo das
     Argument endet und das Angebot beginnt. Die Farbe ist nicht dekorativ gewaehlt: es ist
     der Ton des Kleidungsstuecks. */
  background:var(--schiefer);
}
.ticker-clip{
  overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
}
/* N identical lists ride one track and the shift is exactly ONE list, so the loop is
   seamless by construction. N is not fixed: two lists only span the viewport on narrow
   screens — on desktop the track ran out of content mid-cycle and left a hole at the
   right edge (352px at 1440, 1472px at 2560 — Mo 09.08.). The scene measures the list
   and clones until the track spans two viewports, then publishes the shift and the
   duration here. Duration follows the list width, so the speed stays put no matter how
   many copies it took. Fallbacks keep the band running if the scene never gets to it.
   transform only, GPU-composited, no JS in the loop itself. */
.ticker-track{
  display:flex;width:max-content;
  animation:ticker-run var(--ticker-dur,40s) linear infinite;
  will-change:transform;
}
.ticker.is-off .ticker-track{animation-play-state:paused} /* IO: no work off screen */
.ticker-list{list-style:none;display:flex}
.ticker-list li{
  display:flex;align-items:center;
  font-family:var(--mono);font-size:10px;font-weight:500;
  text-transform:uppercase;letter-spacing:0.12em;
  white-space:nowrap;
  /* Wolke auf dem Schieferband (Mo 15.08., ausdrueckliche Entscheidung gegen die Messung).
     Gemessen sind es 2,51:1 — unter der AA-Schwelle von 4,5:1 fuer Text dieser Groesse.
     Schwarz laege bei 7,17:1. Die Zeile ist dekorativ, wiederholt keine Information, die
     nirgends sonst steht, und traegt kein Klickziel; sie ist damit der einzige Text der
     Seite unter AA. Wer den Kontrast heben will, ohne die helle Schrift aufzugeben, muss
     den Bandton verdunkeln — bei #FAFAFA-Schrift braucht es rund #5A6E80. */
  color:var(--wolke);
}
/* der Trennpunkt folgt der Schrift; Schiefer auf Schiefer waere unsichtbar */
.ticker-list li::after{content:'·';color:var(--hell-45);margin:0 20px}
@keyframes ticker-run{to{transform:translate3d(calc(-1 * var(--ticker-shift,50%)),0,0)}}
@media (max-width:899px){.ticker-list li{font-size:9px}}
@media (prefers-reduced-motion:reduce){
  .ticker-track{animation:none;width:auto}
  .ticker-list{flex-wrap:wrap;justify-content:center;row-gap:8px;padding-inline:24px}
  .ticker-list[aria-hidden]{display:none} /* the loop copy has no job when nothing loops */
  .ticker-clip{-webkit-mask-image:none;mask-image:none} /* nothing runs off the edge */
}

/* ================= guardrails: the safety claim, pure typography (apple claim style) ================= */
.guardrails{
  flex-shrink:0;
  /* Kompakte Kachel, aber mit dem Boden ihrer Gruppe (Mo 20.08.). Bis dahin lief sie
     beidseitig auf --breath-in-compact und trug damit als einzige kompakte Sektion 20px
     mehr Luft unten als Tech Pack, Founders, FAQ und Closing — gemessen 97px gegen 76/77.
     Die Begruendung „reine Typo-Sektion, der Weissraum IST die Sektion" trug nicht: das
     Closing ist ebenfalls reine Typografie, sogar ohne Eyebrow, und stand immer auf 96/77. */
  padding:var(--breath-in-compact) 24px var(--breath-out-compact);
  /* Zweite benannte Ausnahme der Schwarz-Regel (Mo 15.08.), Begruendung ist gemessen: der
     Auftakt der Seite lief 2,9 Bildschirme hell am Stueck (Desktop 5,1), die Kacheln waren
     dabei nicht mehr auseinanderzuhalten. Schwarz hier kuerzt den Lauf auf 1,9 und macht aus
     der einzigen Sektion ohne Medium und ohne Handlung eine echte Zaesur statt einer
     weiteren weissen Flaeche. Naechste dunkle Kachel ist Prints, dazwischen Tech Pack —
     die Regel „nie zwei schwarze in Folge" bleibt gewahrt. Toene 1:1 von Founders. */
  background:var(--schwarz);
  display:flex;flex-direction:column;align-items:center;text-align:center;
}
/* section eyebrow: one voice everywhere (double selector beats the later .micro-label color) */
.guardrails .guardrails-eyebrow,.s2 .s2-eyebrow,.s3 .s3-eyebrow,.s4 .s4-eyebrow,
.autonomy .autonomy-eyebrow,.founders .founders-eyebrow,.pricing .pricing-eyebrow,
.process .process-eyebrow{
  font-size:12px;color:var(--schiefer);
  margin-bottom:16px;
}
.guardrails .guardrails-eyebrow{margin-bottom:14px} /* compact size-rhythm tile (Mo 04.08.) — was 24px */
.s2 .s2-eyebrow,.founders .founders-eyebrow{margin-bottom:10px} /* compact size-rhythm tile (Mo 04.08.) — was shared 16px */
.guardrails h2{
  font-size:clamp(40px,4.6vw,64px);
  font-weight:600;line-height:var(--lh-display); /* one line (Mo 03.08.): the claim runs through */
  color:var(--wolke);
}
.guardrails-sub{
  font-size:16px;font-weight:400;color:var(--hell-60); /* Wolke-Stimme wie Founders, seit die Kachel schwarz ist (Mo 15.08.) */
  margin-top:14px; /* compact size-rhythm tile (Mo 04.08.) — was 24px; sentence-per-line via br */
}

/* ================= section 2: built while you design ================= */
.s2{
  flex-shrink:0;
  /* Mittig gesetzt nach der Vorlage Autonomy (Mo 20.08.): beidseitig --breath-in-compact.
     Die Kachel bleibt kompakt, nur der Boden waechst von 77 auf 96 — vorher stand der
     Inhalt 24px zu hoch. */
  padding:var(--breath-in-compact) 24px;
}
.s2-inner{max-width:1080px;margin-inline:auto}
.s2-head{text-align:center}
.s2-head h2{
  font-size:40px;font-weight:600;line-height:var(--lh-display);
}
.s2-sub{
  font-size:16px;font-weight:400;color:var(--dim-60);
  margin-top:10px; /* compact size-rhythm tile (Mo 04.08.) — was 16px */
}
/* one proof medium: simultaneity, not sequence. The click-to-enlarge is gone (Mo 12.08.)
   — the medium sits in the hero's own frame now and its click belongs to play/pause.
   Shape and video placement come from the shared frame rule near .stage-video, so the
   only thing left here is where the block sits. */
.s2-proof{
  margin-top:var(--breath-head-compact); /* compact size-rhythm tile (Mo 04.08.) */
  width:100%;
}
/* mono micro label: the one pattern for spec-level micro copy */
.micro-label{
  font-family:var(--mono);font-size:10px;font-weight:400;
  text-transform:uppercase;letter-spacing:.12em;
  color:var(--dim-32);
}
@media (max-width:899px){
  :root{--breath-in:96px;--breath-head:64px;--breath-out:80px;--breath-in-compact:58px;--breath-head-compact:38px;--breath-out-compact:48px}
}

/* ================= section 3: print visualization =================
   Mo 13.08.: the rail is gone. ONE card in the size it always had, the four techniques
   switch under it. Head, card and switch row sit in one wrapper of exactly the card's
   width, so their left edges cannot drift apart — the headline indents WITH the card
   instead of being aligned to a second, independent inset. The lens is untouched. */
.s3{
  flex-shrink:0;
  padding:var(--breath-in) 0; /* mittig nach der Vorlage Autonomy (Mo 20.08.); der Wrapper traegt den seitlichen Einzug */
  overflow:hidden;
  background:var(--schwarz); /* dark tile (Mo 02.08.); the render cards stay white */
  /* --s3-left is no longer an edge, only the basis of the card width. Kept so the card
     keeps EXACTLY the size it had while it was the dominant card of the rail. */
  --s3-left:max(24px,calc((var(--tile-w) - 1080px)/4 + 24px));
  --s3-card-w0:calc(var(--tile-w) - 2*var(--s3-left));
  --s3-card-w:calc(var(--s3-card-w0)*0.8); /* Mo 03.08.; unchanged by the rebuild */
}
.s3-wrap{
  width:var(--s3-card-w);
  margin-inline:auto; /* centred: this is what pushes the head in to the card's edge */
}
.s3-head{
  /* --breath-head-compact, not --breath-head: a large media card needs the shorter
     distance or it reads as adrift under the sub. Same call Mo made for the pricing card
     on 09.08.; the section itself stays airy in --breath-in / --breath-out (§2). */
  margin:0 0 var(--breath-head-compact);
  text-align:left;
}
.s3-title{
  font-size:40px;font-weight:600;line-height:var(--lh-display);
  color:var(--wolke);
}
.s3-sub{
  font-size:16px;font-weight:400;color:var(--hell-60); /* the page-wide subline (Mo 13.08., was 19px) */
  margin-top:16px;
}
.s3-card{
  position:relative;
  width:100%;
  /* 0.53125 = the rail's 0.425/0.8 — the card keeps its exact former proportion, which
     is also why the mobile override below only has to change the WIDTH. */
  height:calc(var(--s3-card-w)*0.53125);
  background:var(--surface-platzhalter); /* only visible until the fabric render loads */
  border:1px solid var(--hairline);
  border-radius:var(--r-12);
  overflow:hidden;
  margin:0;
  cursor:none; /* the lens IS the cursor (Mo 03.08.: no zoom icon inside the lens) */
}
/* the four renders are stacked, not swapped in src: a src swap drops the decoded frame
   and flashes the placeholder, and it would break the lens mid-move */
.s3-shot{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;
  -webkit-user-drag:none;user-select:none;
  opacity:0;
  transition:opacity 0.32s var(--ease);
}
.s3-shot.is-on{opacity:1}
.s3-card.is-ph .s3-shot{display:none}
.s3-card .ph-label{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
}
/* switch row: the four techniques left, the constant spec right */
.s3-bar{
  margin-top:24px;
  display:flex;align-items:center;justify-content:space-between;gap:24px;
}
.s3-chips{display:flex;gap:12px;flex-wrap:wrap}
/* Same shape as the configurator's .s4-chip (r8, 14/16, 13px/500, active 600, 0.98 on
   press) but a separate rule: this one lives on the black tile and its selected state is
   INVERTED — solid wolke with black type (Mo 13.08.), where the studio chip fills soft
   schiefer. Sharing one rule would mean overriding four properties right after setting
   them, and .s4-chip must not be touched by this rebuild. */
.s3-chip{
  border:1px solid var(--hell-16);
  border-radius:var(--r-8);
  padding:14px 16px;
  background:transparent;color:var(--hell-90);
  font-family:var(--sans);font-size:13px;font-weight:500;
  text-align:center;cursor:pointer;
  transition:background-color 0.2s ease,border-color 0.2s ease,color 0.2s ease;
}
.s3-chip:hover{border-color:var(--hell-45)}
.s3-chip:active{transform:scale(0.98);transition:transform 0.06s ease}
.s3-chip.is-on{
  background:var(--wolke);color:var(--schwarz);
  border-color:var(--wolke);
  font-weight:600;
}
.s3-specline{text-align:right;color:var(--hell-45)}
@media (max-width:899px){
  /* mobile: the card takes the full content width instead of 80 % — 25 % more render at
     the same proportion (322x171 instead of 258x137 at 390px). The height follows the
     width, so there is exactly one value to change. */
  .s3{--s3-card-w:var(--s3-card-w0)}
  .s3-wrap{width:auto;margin-inline:24px}
  .s3-bar{flex-direction:column;align-items:stretch;gap:16px}
  .s3-chips{display:grid;grid-template-columns:1fr 1fr}
  .s3-specline{text-align:center}
}
/* lens: round, cursor-bound, shows 2.6x of the visible card. 180px with a mouse — the
   value the section was built on — and 110px under a finger, where the card is only 171px
   tall and the old circle stood taller than the thing it magnified (Mo 14.08.). app.js
   reads the size off the element, so this rule is the only place it exists. */
.lens{
  position:absolute;left:0;top:0;
  width:var(--lens-d);height:var(--lens-d);border-radius:50%;
  border:1px solid rgba(0,0,0,0.12);
  overflow:hidden;                 /* the circle clips the copy inside it */
  pointer-events:none;
  visibility:hidden;opacity:0;
  transition:opacity .18s ease;
  /* placed by transform, not by left/top: a phone fires pointermove faster than it
     paints, and moving a box by its layout position pays for layout on every one */
  z-index:5;
}
/* the magnified copy. Absolutely positioned, so its origin is the circle's padding box —
   the same origin a background-position had, which is why the geometry is unchanged. */
.lens-img{position:absolute;left:0;top:0;max-width:none;transform-origin:0 0}
:root{--lens-d:180px}
/* guarded: a touch device that also reports :hover would leave the circle standing after
   a tap, and on touch its whole point is that it goes away with the finger */
@media (hover:hover){.s3-card:hover .lens{visibility:visible;opacity:1}}
/* touch: press and drag. The card owns the gesture from the first frame — touch-action is
   settled when a gesture BEGINS, so it cannot be switched on inside pointerdown; a swipe
   that starts on the render therefore inspects instead of scrolling, and the page is
   scrolled from anywhere around it. The lens lives exactly as long as the finger does,
   which is also what dismisses it. */
@media (pointer:coarse){
  :root{--lens-d:110px}
  /* No fade under a finger. It has to be gone the moment the finger is, and a circle
     that is still fading is a circle that is still painted — on iOS that left a ghost
     ring standing on the render (Mo 14.08.). visibility takes it out of the paint. */
  .lens{transition:none}
  .s3-card{touch-action:none;-webkit-touch-callout:none}
  /* if the render is missing there is nothing to magnify, and a card that can neither
     magnify nor be scrolled past would be a dead spot in the page */
  .s3-card.is-ph{touch-action:auto}
}
.s3-card.is-lensing .lens{visibility:visible;opacity:1}
.s3-card.is-ph .lens{display:none}

@media (prefers-reduced-motion:reduce){
  .faq-plus,.lens,.s3-shot,.s4-frame,.s4-corner,.s4-overlay,.s4-seg-ind,.s4-seg-btn{transition:none}
  .s4-dot.swatch-pop{animation:none}
}

/* ================= section 4: placement measuring stage (pinned scroll scene, hero mechanics) ================= */
.s4-wrap{
  flex-shrink:0;
  height:calc(100% + var(--s4-reveal) + var(--s4-hold)); /* scrollport + scene reserve */
}
/* one-way resistance: once the show has played and the visitor passed below,
   the scroll reserve collapses (JS adds .passed with scrollTop compensation) —
   scrolling back UP is reserve-free and smooth (Mo 02.08.) */
.s4-wrap.passed{height:100%}
.s4-wrap.passed .s4{height:100%}
.s4{
  position:sticky;top:0;
  height:calc(100% - var(--s4-reveal) - var(--s4-hold)); /* = exactly one scrollport */
  overflow:hidden;
  display:flex;flex-direction:column;
  padding:calc(var(--tb-h) + 34px) 24px 24px; /* header clears the floating glassbar while pinned */
  /* Wieviel Schirm ueber der Referenzbreite steht. Unter 1440px ist er 0, und damit
     rechnet jede Formel darunter exakt wie vorher (Mo 19.08.). */
  --s4-wide:max(0px, 100vw - 1440px);
}
.s4-inner{max-width:1080px;margin-inline:auto}
.s4-head{text-align:center}
.s4-head h2{font-size:40px;font-weight:600;line-height:var(--lh-display)}
.s4-sub{font-size:16px;font-weight:400;color:var(--dim-60);margin-top:16px}
/* mini-configurator inside the pinned scene: garment center, tool pairs flanking
   (shoulder offset set by JS), spec line below, everything fits one scrollport */
.s4-config{
  position:relative;
  flex:1;min-height:0;
  /* Der Rahmen waechst mit dem Schirm, aber nur so schnell, wie die Flanken es brauchen
     (Mo 19.08.). Fest auf 1240px waren bei 2560px 611px Rand bis zum ersten Bedienelement,
     waehrend die Buehne dazwischen einfror. */
  width:100%;max-width:min(1240px + var(--s4-wide) * 0.6, 1760px);
  /* Der Zug ist GEMESSEN, nicht gesetzt (Mo 19.08.): app.js schreibt --s4-pull gegen die
     LETZTE Headline-Zeile, damit die Kapuzenspitze auf jeder Breite bei den dokumentierten
     30 % sitzt — dieselbe Rechnung, die unter 1160px schon lief. Die festen -97px waren
     fuer eine Fenstergroesse gerechnet: die Buehne ist oben verankert, also rutscht die
     Kapuzenspitze mit wachsender Buehne nach unten, und ab 1920px verdeckte das
     Kleidungsstueck die Subline zu 100 % statt sie zu kreuzen. 97px bleibt der
     Rueckfallwert, damit vor dem ersten Messen nichts springt. */
  margin:calc(-1 * var(--s4-pull, 97px)) auto 0;
  padding-inline:24px;
  display:grid;
  grid-template-columns:minmax(0,1fr) auto minmax(0,1fr); /* minmax(0,..): no min-content floor — flanks stay EQUAL, stage stays centered (bugfix 03.08.) */
  grid-template-rows:minmax(0,1fr) auto;
  column-gap:0;row-gap:16px;
}
.s4-tools{position:relative;z-index:2;display:flex;flex-direction:column;gap:40px;justify-content:flex-start}
/* the source canvas carries a wide transparent margin around the garment:
   the tool groups dock into that empty zone so they visibly belong to the product */
.s4-tools-left{grid-column:1;grid-row:1;justify-self:end;margin-right:24px}
.s4-tools-right{grid-column:3;grid-row:1;justify-self:start;margin-left:24px}
/* the stage IS the studio canvas: source format 1600x2000; height-driven so the
   full scene fits one scrollport, scaled by JS during the reveal */
.s4-stage{
  grid-column:2;grid-row:1;
  position:relative;
  /* +15% (Mo 03.08.): top stays anchored, the garment grows downward.
     Der Deckel haengt seit 19.08. an BEIDEN Achsen. 943px war reine Hoehenlogik: ab
     1920px lieferten 1920 und 2560 exakt dieselben 754x943, das Kleidungsstueck fror
     ein, waehrend die Sektion weiterwuchs — bei 2560px blieben 377px leeres Band
     darunter stehen. fitStage() kuerzt weiterhin, wenn die Buendchen sonst die
     Kachel verlassen wuerden; dieser Deckel ist nur die Obergrenze. */
  height:min(943px + var(--s4-wide) * 0.28, 1240px, 115%);
  aspect-ratio:1600/2000;
  align-self:start;justify-self:center; /* top-anchored: the hood lands exactly at mid-headline */
  will-change:transform;
  isolation:isolate; /* blend modes stay inside the stage */
  --s4-color:var(--schiefer); /* default garment color: slate blue */
}
.s4-lay{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
/* garment composite, exactly the studio stack: color silhouette + main (multiply) + light */
.s4-garment{position:absolute;inset:0;isolation:isolate;pointer-events:none}
.s4-color{
  background:var(--s4-color);
  -webkit-mask:center/100% 100% no-repeat;
  mask:center/100% 100% no-repeat; /* mask image per fit, set by JS */
}
.s4-main{mix-blend-mode:multiply}
/* wash: snow layer, screen blend, opacity = slider (background position: under the print) */
.s4-washlay{mix-blend-mode:screen;opacity:0}
/* eyelets/drawstrings: foreground layers, tinted with the garment color (studio multiply tint);
   toggle fades in the studio redraw rhythm instead of popping */
.s4-overlay{opacity:0;transition:opacity 0.15s ease-in}
.s4-overlay.on{opacity:1}
.s4-overlay .s4-fill{
  position:absolute;inset:0;
  background:var(--s4-color);
  -webkit-mask:center/100% 100% no-repeat;
  mask:center/100% 100% no-repeat; /* mask image per fit, set by JS */
}
.s4-overlay img{position:absolute;inset:0;width:100%;height:100%;mix-blend-mode:multiply}
.s4-art{position:absolute;cursor:grab;touch-action:none}
.s4-art.dragging{cursor:grabbing}
.s4-art img{width:100%;height:auto;pointer-events:none;-webkit-user-drag:none;user-select:none}
/* studio selection state: the stock fabric frame, visible only while engaged
   (hover / focus / tap) — idle stays clean */
.s4-frame{
  position:absolute;
  border:1px solid var(--mess-select);
  pointer-events:none;
}
.s4-corner{
  position:absolute;width:24px;height:24px; /* touch hit area around the 12px control */
  transform:translate(-50%,-50%);
  display:flex;align-items:center;justify-content:center;
  pointer-events:none;
  touch-action:none;
}
.s4-corner::after{
  content:'';width:12px;height:12px;
  border:1px solid var(--mess-select); /* fabric transparentCorners: outline only */
}
.s4-corner[data-c=tl],.s4-corner[data-c=br]{cursor:nwse-resize}
.s4-corner[data-c=tr],.s4-corner[data-c=bl]{cursor:nesw-resize}
.s4-frame,.s4-corner{opacity:0;transition:opacity 0.18s ease}
.s4-stage.selected .s4-frame,.s4-stage.selected .s4-corner{opacity:1}
.s4-stage.selected .s4-corner{pointer-events:auto}
/* studio measurement line: 3px halo under a 1px dashed white line (dash 6/4) */
.s4-line,.s4-endcap{position:absolute;pointer-events:none;background:var(--mess-halo)}
.s4-line-v{width:3px}
.s4-line-v::after{
  content:'';position:absolute;left:1px;top:0;bottom:0;width:1px;
  background:repeating-linear-gradient(180deg,var(--mess-strich) 0 6px,transparent 6px 10px);
}
.s4-line-h{height:3px}
.s4-line-h::after{
  content:'';position:absolute;top:1px;left:0;right:0;height:1px;
  background:repeating-linear-gradient(90deg,var(--mess-strich) 0 6px,transparent 6px 10px);
}
.s4-endcap{width:10px;height:3px}
.s4-endcap::after{content:'';position:absolute;top:1px;left:0;right:0;height:1px;background:var(--mess-strich)}
/* studio pill label: Inter 600 14px on translucent ink */
.s4-pill{
  position:absolute;pointer-events:none;white-space:nowrap;
  font-family:var(--sans);font-size:14px;font-weight:600;line-height:1.2;
  color:var(--mess-pill-text);
  background:var(--mess-pill-bg);
  border:0.5px solid var(--mess-pill-ring);
  border-radius:8px;
  padding:3px 6px;
}
/* momentum CTA as the closing tool of the right column (hero-CTA sizing) */
/* .s4-cta shape lives in the shared body-CTA rule near .hero-cta */
.s4-tool-label{font-size:13px;margin-bottom:12px}
.s4-tool-label strong{font-weight:600}
.s4-dots{display:flex;gap:8px}
.s4-dot{ /* studio preselect-color 1:1 */
  width:36px;height:36px;border-radius:50%;
  border:none;padding:0;cursor:pointer;flex-shrink:0;
  transition:box-shadow 0.2s ease,transform 0.2s ease;
}
.s4-dot:hover{box-shadow:0 0 0 0.5px var(--ctl-dot-hover);transform:scale(1.04)}
.s4-dot:active{transform:scale(0.95);transition:transform 0.06s ease}
.s4-dot.active{box-shadow:0 0 0 2px var(--weiss),0 0 0 3.5px var(--schwarz)}
.s4-dot.swatch-pop{animation:swatch-pop 0.25s ease-out} /* studio 1:1 */
@keyframes swatch-pop{0%{transform:scale(0.95)}100%{transform:scale(1)}}
.s4-washrow{display:flex;align-items:center;width:212px} /* = dots row width: 5x36 + 4x8 */

/* ── Pantone tool (Mo 14.08.) ─────────────────────────────────────────────
   The studio's colour-comparison box (tools/color_tool.scss .color-comparison-box),
   scaled from the sidebar to this column. Structure, behaviour and the Delta-E
   matching are 1:1 from the product; only the sizes shrink, because 212px is the
   column's set width (the dots row, which the wash slider already matches).
   Studio -> here: min-height 72->58, padding 14/16 -> 11/10, value 15px/700 ->
   12px/500 (the page carries no heavier mono), label 10 -> 9px.
   --pan-ink is the studio's --pill-text: each half flips its type to black or
   white by the luminance of the colour it is filled with. */
.s4-pan{position:relative;width:212px}
.s4-pan-box{
  display:flex;
  border-radius:var(--r-8);
  overflow:hidden;
  min-height:58px;
}
.s4-pan-col{
  flex:1;min-width:0;
  /* Die Spalte IST das Klickziel: seit 27.08. ist sie ein <label>, das seinen Input
     umschliesst — ein Tipp irgendwo in der Haelfte fokussiert das Feld, ohne eine Zeile JS.
     Vorher stand hier "the whole half is the hit area, see app.js" und der Code dazu
     existierte nicht; gemessen war die Trefferflaeche die des Feldes selbst, 109x14. */
  cursor:text;
  display:flex;flex-direction:column;justify-content:space-between;
  padding:11px 10px;
  min-height:44px;          /* Apple HIG; die Kachel gibt mobil ohnehin 46px vor */
  transition:background-color 0.2s ease;
}
.s4-pan-label{
  font-family:var(--mono);font-size:9px;font-weight:400;
  text-transform:uppercase;letter-spacing:0.06em;
  /* 0.70, not the studio's 0.55: measured on the slate half the label came out
     at 2.66:1 and missed AA for UI text. At 0.70 it reads 3.5:1 and is still
     clearly quieter than the value above it. */
  color:var(--pan-ink,var(--dim-60));opacity:0.70;
  line-height:1;white-space:nowrap;
}
.s4-pan-value{
  width:100%;min-width:0;
  background:transparent;border:none;outline:none;padding:0;
  font-family:var(--mono);font-size:12px;font-weight:500;
  letter-spacing:0;                     /* mono sets its own; the body -0.03em would crowd it */
  color:var(--pan-ink,var(--schwarz));
  line-height:1.2;cursor:text;
  flex:1;                   /* fuellt die Spalte unter dem Label — die Flaeche gehoert dem Feld */
}
.s4-pan-value::placeholder{color:var(--pan-ink,var(--dim-35));opacity:0.35}
.s4-pan-value:focus-visible{
  /* the ring flips with the fill, like the type does: a schiefer ring on a
     schiefer half is invisible, and the page-wide focus rule is schiefer */
  outline:2px solid var(--pan-ink,var(--schiefer));outline-offset:2px;border-radius:2px;
}
/* dropdown: same component as the studio's .tcx-dropdown, one step smaller.
   z-index 6 clears the measuring overlay (.s4-frame/.s4-line sit at 4-5). */
.s4-pan-drop{
  display:none;position:absolute;left:0;right:0;top:100%;
  margin-top:4px;max-height:184px;overflow-y:auto;
  background:var(--wolke);
  border:1px solid var(--hairline);
  border-radius:var(--r-8);
  z-index:6;
}
.s4-pan-drop.open{display:block}
.s4-pan-item{
  display:flex;align-items:center;gap:8px;
  padding:7px 10px;cursor:pointer;
  border-bottom:1px solid rgba(0,0,0,0.04);
}
.s4-pan-item:last-child{border-bottom:none}
.s4-pan-item:hover,.s4-pan-item.on{background:rgba(0,0,0,0.035)}
.s4-pan-sw{
  width:14px;height:14px;border-radius:3px;flex-shrink:0;
  border:1px solid var(--hairline);
}
.s4-pan-code{
  font-family:var(--mono);font-size:10px;font-weight:500;
  color:var(--schwarz);white-space:nowrap;flex-shrink:0;
}
.s4-pan-name{
  font-size:10px;color:var(--dim-35);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;
}
.s4-wash{ /* studio wash-slider optics (3px, filled track via --wash-fill; drop shadow omitted — page shadow rule); stretches to the dots-row width (Mo 02.08., statt Studio-100px) */
  -webkit-appearance:none;appearance:none;
  flex:1;height:3px;
  background:linear-gradient(to right,
    var(--schwarz) 0%,
    var(--schwarz) var(--wash-fill,0%),
    var(--ctl-track) var(--wash-fill,0%),
    var(--ctl-track) 100%);
  border-radius:2px;outline:none;
}
.s4-wash::-webkit-slider-thumb{
  -webkit-appearance:none;
  width:14px;height:14px;border-radius:50%;
  background:var(--wolke);
  border:1px solid var(--ctl-thumb-ring);
  cursor:pointer;
}
.s4-wash::-moz-range-thumb{
  width:14px;height:14px;border-radius:50%;
  background:var(--wolke);
  border:1px solid var(--ctl-thumb-ring);
  cursor:pointer;
}
/* fit: the studio pill segmented control (color-tabs optic; the active pill SLIDES —
   deliberate apple-style deviation, the studio snaps) */
.s4-seg{
  position:relative;
  display:flex;
  padding:4px;
  border:1px solid var(--hairline);
  border-radius:var(--r-pill);
}
.s4-seg-ind{
  position:absolute;top:4px;bottom:4px;
  background:var(--ctl-seg-active);
  border-radius:var(--r-pill);
  transition:left 0.2s var(--ease),width 0.2s var(--ease);
  pointer-events:none;
}
.s4-seg-btn{
  position:relative;z-index:1;
  padding:9px 14px;
  font-family:var(--mono);font-size:9px;font-weight:500;
  text-transform:uppercase;letter-spacing:0.03em;
  color:var(--dim-35);
  background:transparent;border:none;border-radius:var(--r-pill);
  cursor:pointer;text-align:center;
  transition:color 0.2s ease,font-weight 0.2s ease;
}
.s4-seg-btn:hover{color:var(--dim-60)}
.s4-seg-btn.active{color:var(--schwarz);font-weight:700}
/* details: studio select-table chips (checkbox-button optic, content width) */
.s4-chips{display:flex;gap:12px}
.s4-chip{
  border:1px solid var(--ctl-border);
  border-radius:var(--r-8);
  padding:14px 16px;
  background:var(--wolke);color:var(--schwarz);
  font-family:var(--sans);font-size:13px;font-weight:500;
  text-align:center;cursor:pointer;
  transition:all 0.2s ease;
}
.s4-chip:hover{border-color:var(--ctl-border-hover)}
.s4-chip:active{transform:scale(0.98);transition:transform 0.06s ease}
.s4-chip.active{font-weight:600;background:var(--ctl-check-bg);border-color:var(--ctl-check-border)}
/* below the flanking breakpoint the scene unpins and stacks statically */
@media (max-width:1159px){
  .s4-wrap{height:auto}
  /* Kopfluft eine Stufe kleiner als der luftige Standard (Mo 14.08.): ueber einer
     dreizeiligen Headline, in die das Kleidungsstueck hineinragt, liest die volle
     --breath-in als leeres Band. Unten bleibt sie voll — dort steht nichts drin. */
  .s4{position:static;height:auto;padding:var(--breath-in-compact) 24px var(--breath-out)}
  /* Stacked, the STAGE comes first and the tools follow underneath (Mo 14.08.). That is
     what makes the desktop pull possible here at all: with the tools first it dropped the
     colour row straight onto the headline (mobile round 05.08.), which is why it used to
     be reset to a positive margin. Now the garment rises over the head the same way it
     does above 1160px, and every control sits below it. */
  .s4-config{display:flex;flex-direction:column;align-items:center;gap:24px;margin-top:0}
  .s4-stage{
    order:0;
    height:auto;width:min(620px,100%);
    /* The pull is MEASURED, not a constant: how far the garment has to rise depends on how
       many lines the headline wraps to, and that changes with the screen. A fixed value that
       lands mid-headline on a 420px phone puts the hood above the eyebrow on a 768px tablet
       (both measured, docs/decisions.md). app.js writes --s4-pull; 0 until it has. */
    margin-top:calc(-1 * var(--s4-pull, 0px));
  }
  /* ONE column, ONE edge, ONE width (Mo 14.08.). Before this the six tools kept their
     desktop widths (212/212/258/212/202/151) and each row centred itself, so the six
     labels started at six different x — 99, 99, 76, 99, 104, 129. Nothing sat on an axis,
     and that, not the controls themselves, is what read as untidy. The width is the FIT
     control's, because it is the widest and nothing may be forced to grow past its
     natural size. app.js measures that control and writes --s4-col; until it has, the
     column falls back to the full width, which is what it was doing before anyway. */
  .s4-tools{
    flex-direction:column;flex-wrap:nowrap;align-items:stretch;
    width:min(var(--s4-col, 100%), 100%);
    margin-inline:auto;gap:28px;padding-top:0 !important;
  }
  .s4-tools-left{order:1}
  .s4-tools-right{order:2}
  .s4-config{gap:28px} /* the seam between the two groups reads as one more tool gap */
  /* the desktop column widths are what broke the axis — off with all of them */
  .s4-pan,.s4-washrow{width:auto}
  /* The swatches spread across the column instead of huddling at its left edge (Mo 14.08.).
     The 8px gap goes down to 6 as a MINIMUM, not as the spacing: with the row's own gap kept
     at 8 the five 36px dots need 212px, and the column is 204 at 320px — the last swatch was
     clipped by the tile. At 6 they fit exactly there, and everywhere wider space-between hands
     out what is left (19px at 258). */
  .s4-dots{justify-content:space-between;gap:6px}
  .s4-seg{width:100%}
  .s4-chips>*{flex:1;min-width:0} /* two equal cards instead of two content-width ones */
  .s4-cta{width:100%;justify-content:center}
  /* The converter and the detail cards carry the SAME height (Mo 14.08.). The 58px floor
     on the box is a desktop value — in a 212px column it needed the extra air — and it
     made the converter sit a head taller than the cards further down. The card is the
     shorter of the two and the one that sets the measure: 14px padding, a 13px line, 14px
     padding. One token, both users, so they cannot drift apart; off the floor alone the
     box lands at 45px and would miss by one. */
  /* Under 375px the column can no longer hold Fit at its own width: the tile's content
     box is 244px at 360 and 204px at 320, against the 258px the control needs. There the
     four segments divide what there is and drop their side padding — measured, the longest
     label (REGULAR, 47px) still clears 48px per segment at 320. Above 374 nothing here
     applies, so the measured width stays the width Mo picked. */
  @media (max-width:374px){
    .s4-seg-btn{flex:1 1 0;min-width:0;padding-inline:0}
  }
  :root{--s4-ctl-h:46px}
  .s4-pan-box{min-height:var(--s4-ctl-h)}
  .s4-chip{min-height:var(--s4-ctl-h)}
}
/* ================= autonomy: what this changes — pure typography, airy, plain wolke
   (Mo 05.08.: the tinted floor is gone again; the tile takes its fill from .tile like
   every other light section, so there is no second source for it). ================= */
.autonomy{
  flex-shrink:0;
  /* Die VORLAGE fuer die mittige Setzung (Mo 20.08.): beidseitig --breath-in, der Inhalt
     sitzt in der Mitte der Kachel. Tech Pack, Prints, Process und Pricing folgen ihr.
     Der Zwischenschritt auf --breath-out am selben Tag ist damit zurueckgenommen. */
  padding:var(--breath-in) 24px;
  display:flex;flex-direction:column;align-items:center;text-align:center;
}
.autonomy h2{font-size:40px;font-weight:600;line-height:var(--lh-display)}
.autonomy-sub{font-size:16px;font-weight:400;color:var(--dim-60);max-width:720px;margin-top:16px}
/* GHOST, not filled: the glass navbar carries the one filled Start Free in this very
   field of view, and §3 allows exactly one. Same outline language as the tile-nav
   ghost, at body-CTA size. 40px to the sub is the hero's sub-to-CTA distance. */
.autonomy-cta{
  margin-top:40px;
  background:transparent;color:var(--schwarz);
  border:1px solid var(--schwarz);
  padding:8px 21px; /* the border compensates back to the filled sibling's box */
  transition:background 0.2s ease,color 0.2s ease;
}
.autonomy-cta:hover{background:var(--schwarz);color:var(--weiss)}

/* ================= process: the old way and the new one — two chains side by side.
   The whole argument is CHAIN LENGTH, so both columns start on the same top line and
   every step tile has the same height: the right chain simply stops earlier. No icons,
   no arrows, no colour coding beyond the one schiefer accent (Mo 05.08.). ================= */
.process{
  flex-shrink:0;
  padding:var(--breath-in) 24px; /* mittig nach der Vorlage Autonomy (Mo 20.08.) */
  display:flex;flex-direction:column;align-items:center;text-align:center;
  --step-stagger:90ms; /* one source for the chain build-up rhythm */
}
.process h2{font-size:40px;font-weight:600;line-height:var(--lh-display)}
.process-sub{font-size:16px;font-weight:400;color:var(--dim-60);max-width:720px;margin-top:16px}
/* horizontal build (Mo 05.08./06.08.): both chains run LEFT TO RIGHT and sit stacked,
   WITHOUT above WITH, sharing one left edge — and since 06.08. sharing the SAME FIVE
   STATIONS (Design · Tech pack · Sample · Review · Production). The only difference is
   the detour: the upper Sample->Review span is TWICE the pitch, so the loop has room
   and the upper line visibly ends further right — the time claim is readable at rest,
   with no animation. Node x positions derive from the two pitch tokens below and
   nothing else; connectors and traces read the same numbers. */
.process{
  /* pitch: fixed on desktop, fluid below — the longer chain spans 5 pitch units */
  --slot:min(140px,calc((100vw - 170px)/5));
  --slot-loop:calc(2*var(--slot)); /* the upper Sample->Review detour span */
  --node:10px; /* node circle diameter; the rail line sits at --node/2 */
}
.process-grid{
  margin-top:var(--breath-head);
  display:flex;flex-direction:column;align-items:flex-start;gap:48px;
  width:fit-content;max-width:100%;margin-inline:auto;
  text-align:left; /* the section centres its copy; the diagram is left-set */
}
.process-label{margin-bottom:22px}
/* the top label is the discarded path and steps back; the lower one carries full ink */
.process-col--without .process-label{color:var(--dim-60)}
.process-col--with .process-label{color:var(--schwarz)}
.process-chain{position:relative;list-style:none;height:36px}
.process-col--without .process-chain{width:calc(3*var(--slot) + var(--slot-loop) + var(--node))} /* ends visibly later */
.process-col--with .process-chain{width:calc(4*var(--slot) + var(--node))}
/* node position (--x) and entrance index (--i): the single positioning inputs */
.process-step:nth-child(1){--i:0;--x:0px}
.process-step:nth-child(2){--i:1;--x:var(--slot)}
.process-step:nth-child(3){--i:2;--x:calc(2*var(--slot))}
.process-step:nth-child(4){--i:3;--x:calc(3*var(--slot))}
.process-step:nth-child(5){--i:4;--x:calc(4*var(--slot))}
/* the upper detour pushes Review and Production right by the loop span */
.process-col--without .process-step:nth-child(4){--x:calc(2*var(--slot) + var(--slot-loop))}
.process-col--without .process-step:nth-child(5){--x:calc(3*var(--slot) + var(--slot-loop))}
/* a step is a node on the line: 10px circle, mono label hanging under it */
.process-step{
  position:absolute;top:0;z-index:1; /* circles cover the trace where it passes through a node */
  left:calc(var(--node)/2 + var(--x));
  /* centring WITHOUT transform: a zero-width flex column centres circle and label on
     the left position itself. A translateX(-50%) here gets wiped by the entrance
     (sub-in ends on transform:none) and shifted every node by half its label width —
     and offsetLeft IS the node centre this way, no correction terms anywhere */
  width:0;
  display:flex;flex-direction:column;align-items:center;gap:9px;
  white-space:nowrap;
  font-family:var(--mono);font-size:9px;font-weight:500;
  text-transform:uppercase;letter-spacing:0.12em;
  color:var(--schwarz);
  transition:color 0.3s var(--ease);
}
.process-step::before{
  content:'';width:var(--node);height:var(--node);border-radius:50%;
  background:var(--wolke); /* opaque: the centre-to-centre lines pass UNDER the nodes */
  border:1px solid var(--ctl-border);
  transition:border-color 0.3s var(--ease);
}
/* connector: node centre to node centre, exactly one pitch long. border-top on zero
   height keeps it 1px; left:50% is the node centre of the translated step box */
.process-step:not(:last-of-type)::after{
  content:'';position:absolute;left:0;top:calc(var(--node)/2); /* left:0 = the node centre (zero-width box) */
  width:var(--slot);height:0;
  border-top:1px solid var(--ctl-border);
  transform:translateY(-0.5px);
  z-index:-1; /* under the circle fill */
}
/* the upper detour segment spans the loop width — and is PERMANENTLY drawn heavier,
   so the rest state already says where the time goes (Mo 06.08.) */
.process-col--without .process-step:nth-child(3)::after{width:var(--slot-loop);border-top-color:var(--ctl-border-hover)}
/* the one accent: the kept path is drawn in schiefer */
.process-col--with .process-step::after{border-top-color:var(--schiefer)}
/* the automated tech pack IS the difference: permanently accented via node contour +
   the studio's soft schiefer fill. Contour, not label — schiefer type at 9px reads
   2.51:1 on wolke and misses AA, so the ink stays black */
.process-step--auto::before{border-color:var(--schiefer);background:var(--ctl-check-bg)}
/* 390px round: at the fluid pitch the labels collide (AUTOMATED TECH PACK alone is
   ~120px wide against a ~44px pitch), so phones keep only DESIGN, SAMPLE and
   PRODUCTION — the stations stay as circles, their names step back (decision per
   brief). The sample tally keeps its own font-size and stays visible everywhere */
@media (max-width:479px){
  .process-step:nth-child(2),.process-step:nth-child(4){font-size:0}
}
/* the sample tally lives AT the Sample station (Mo 06.08.): a small mono figure above
   the node, fading in on first arrival and re-fading softly on every increment — no
   counting steps, no scale, just the number becoming present */
.process-samplecount{
  position:absolute;top:-22px;left:0;transform:translateX(-50%);
  font-size:9px; /* explicit: stays visible when the phone round zeroes sibling labels */
  color:var(--dim-60);
  opacity:0;
}
.process-samplecount.pop{animation:samplecount-in 0.3s var(--ease) forwards}
@keyframes samplecount-in{from{opacity:0}to{opacity:1}}

/* ---- the loop layer (JS builds it; geometry measured from the nodes, one source) ----
   Two moving dots, one per chain: the lower one arrives in ~3s and then sits still
   while the upper one is still looping. The silence below IS the argument. */
.process-rail{position:absolute;inset:0;pointer-events:none}
.process-col--without .process-rail{color:var(--schwarz)}
.process-col--with .process-rail{color:var(--schiefer)}
/* trace: 1px over the connector run, density carried by opacity alone — a segment
   walked three times ends up visibly heavier than one walked once */
.process-trace{
  position:absolute;left:0;top:0;height:0;
  border-top:1px solid currentColor;
  transform:translateY(-0.5px);
  opacity:0;
}
/* the dot moves on transform ONLY (§4: transform/opacity): the first build animated
   style.top and forced layout every frame — that was the visible jank.
   Apple-like optics (Mo 06.08., explicit override of the page's no-glow rule for THIS
   dot alone): bright core, a small specular light, and a soft radial halo — all in
   currentColor, no second hue, no filter (gradients composite cheaply) */
.process-dot{
  position:absolute;left:0;top:0;z-index:2;
  width:8px;height:8px;border-radius:50%;
  background:currentColor;
  opacity:0;
  will-change:transform;
}
.process-dot::before{ /* halo */
  content:'';position:absolute;inset:-11px;border-radius:50%;
  background:radial-gradient(circle,currentColor 0%,transparent 62%);
  opacity:0.38;
}
.process-dot::after{ /* specular: what makes it read as a lit sphere, not a disc */
  content:'';position:absolute;inset:1px 3.5px 3.5px 1px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%,rgba(255,255,255,0.9),rgba(255,255,255,0) 60%);
}
/* comet tail: a handful of past positions, shrinking and fading — pure transform/opacity */
.process-tail{
  position:absolute;left:0;top:0;z-index:1;
  width:6px;height:6px;border-radius:50%;
  background:currentColor;
  opacity:0;
  will-change:transform;
}
/* arrival ripple: one soft ring, once per landing (apple's success beat) */
.process-ripple{
  position:absolute;left:0;top:0;z-index:1;
  width:8px;height:8px;border-radius:50%;
  border:1px solid currentColor;
  opacity:0;
}
.process-ripple.go{animation:process-ripple 0.7s var(--ease) forwards}
@keyframes process-ripple{
  from{opacity:0.55;transform:var(--rip-t) scale(1)}
  to{opacity:0;transform:var(--rip-t) scale(4.5)}
}
.process-col--without .process-dot{--dot-a:0.55} /* black, held back */
.process-col--with .process-dot{--dot-a:1}
.process.is-running .process-dot{opacity:var(--dot-a)}
/* stations light up as the dot passes them — quiet progress the eye can keep */
.process-step.is-past::before{border-color:var(--schiefer);background:var(--ctl-check-bg)}
/* the one state change on arrival: the accepted path's last node takes the accent */
.process-step.is-done::before{border-color:var(--schiefer)}
/* end of cycle: everything the loop drew fades out together, then it starts over */
.process.is-fading .process-dot,.process.is-fading .process-trace,.process.is-fading .process-tail,
.process.is-fading .process-samplecount{
  opacity:0;transition:opacity 0.6s var(--ease);
}
.process.is-fading .process-step{transition:color 0.6s var(--ease)}
.process.is-fading .process-step::before{transition:border-color 0.6s var(--ease)}

/* ================= founders: authority + proof — centered typo, cloud tile, verified 3-stat row (Mo 04.08.) ================= */
.founders{
  flex-shrink:0;
  padding:var(--breath-in-compact) 24px var(--breath-out-compact); /* compact size-rhythm tile (Mo 04.08.) */
  background:var(--schwarz); /* deliberate exception to the black rule (Mo 04.08.) — see CLAUDE.md §2 */
  display:flex;flex-direction:column;align-items:center;text-align:center;
}
.founders h2{font-size:40px;font-weight:600;line-height:var(--lh-display);color:var(--wolke)} /* standard headline size, page-wide (Mo 04.08.) — was 32px */
.founders-sub{font-size:16px;font-weight:400;color:var(--hell-60);max-width:720px;margin-top:10px} /* two lines via br; 720px keeps the long first sentence a readable measure (matches FAQ answer width); margin-top compact (Mo 04.08.) — was 16px */
/* proof row: three verified numbers, receipts for the page's claims — every one true (§5). Data speaks mono (§3). */
.founders-stats{display:grid;grid-template-columns:repeat(2,auto);justify-content:center;gap:22px 56px;margin:29px 0 0} /* two verified stats since the brands figure left (Mo 06.08.); margin-top compact (Mo 04.08.) — was 48px */
.founders-stat{display:flex;flex-direction:column;align-items:center;gap:9px;margin:0}
.founders-stat-num{font-family:var(--mono);font-size:38px;font-weight:500;line-height:1;letter-spacing:-0.01em;color:var(--wolke)}
.founders-stat-label{margin:0;font-family:var(--mono);font-size:10px;font-weight:500;text-transform:uppercase;letter-spacing:0.12em;color:var(--hell-45)}
@media (max-width:600px){.founders-stats{grid-template-columns:1fr;justify-items:center;gap:24px}}
@media (max-width:600px){
  .footer-base{flex-wrap:wrap;gap:10px 18px} /* copyright may drop below the links */
} /* phones/small tablets: stack, centered */

/* ================= pricing: the conversion section — every lever TRUE (§5).
   ONE dark card, centred (Mo 09.08.): the launch-price card was removed, its price
   moved under the card as a single quiet line. The card keeps its old width instead
   of stretching — a lone card at full section width reads as a banner, not an offer.
   Real urgency only: the beta really ends; no countdowns, no fake scarcity. ================= */
.pricing{flex-shrink:0;padding:var(--breath-in) 24px} /* mittig nach der Vorlage Autonomy (Mo 20.08.) */
.pricing-inner{max-width:1080px;margin-inline:auto;display:flex;flex-direction:column;align-items:center}
.pricing-head{text-align:center}
.pricing-sub{font-size:16px;font-weight:400;color:var(--dim-60);margin-top:16px} /* the page-wide subline, no special case */
.pricing-head h2{font-size:40px;font-weight:600;line-height:var(--lh-display)}
.pricing-grid{
  margin-top:var(--breath-head-compact); /* was --breath-head: 96px left the card adrift under the sub (Mo 09.08.) */
  display:grid;grid-template-columns:minmax(0,620px); /* wider so the lines stop wrapping (Mo 09.08.) */
  justify-content:center;
  width:100%;
}
/* every vertical step inside the card sits on the 8px grid, and the steps climb:
   8 inside a group, 24 between groups, 32 across a block boundary (Mo 09.08.) */
.pricing-card{
  padding:40px 48px; /* sides grew with the card so the text keeps its margin */
  border-radius:var(--r-12); /* no shadow — page rule */
  display:flex;flex-direction:column;align-items:stretch;text-align:left;
}
.pricing-card--beta{
  background:var(--schwarz);
  color:var(--wolke); /* the lines carry the offer, so they get the full voice (Mo 09.08.) */
}
.pricing-tag{color:var(--schiefer)}
/* the price is a sentence now, not a figure: same face, size and weight as the line
   below it, so nothing on the card shouts except the button (Mo 09.08.) */
.pricing-price{font-size:15px;margin-top:16px}
.pricing-per{font-size:15px;margin-top:8px}
/* three labelled groups replace the dashed rules (Mo 09.08.): the gap BETWEEN groups
   is more than twice the gap inside one, so the structure reads without a single line. */
.pricing-groups{margin-top:32px;display:flex;flex-direction:column;gap:24px} /* 32 = block boundary (action -> detail), 24 = between groups */
.pricing-group-label{color:var(--hell-60)} /* dimmed against the wolke lines, but readable: 7.20:1 */
.pricing-lines{
  list-style:none;
  margin-top:8px;
  display:flex;flex-direction:column;gap:8px;
  font-size:14px;
}
.pricing-lines li{display:flex;align-items:flex-start;gap:10px}
/* the ticks carry the only accent on the cards; the paths are stroke:currentColor,
   so setting color HERE hits the icon alone and leaves the line's text untouched.
   Same schiefer as every other accent on the page — no second tone (Mo 05.08.) */
.pricing-lines svg{flex-shrink:0;margin-top:3px;color:var(--schiefer)}
.pricing-cta{
  display:flex;align-items:center;justify-content:center;
  width:100%;
  padding:16px 28px;
  margin-top:32px; /* block boundary: header block ends, action block starts */
  background:var(--wolke);color:var(--schwarz); /* inverted on the dark card, like the reference */
  border-radius:var(--r-8);
  font-size:15px;font-weight:600;
  transition:background 0.2s ease,color 0.2s ease;
}
.pricing-cta:hover{background:var(--hover-blau);color:var(--weiss)}
.pricing-cta:active{transform:scale(0.98)}
.pricing-trial{margin-top:16px;text-align:center;color:var(--hell-45)}
/* what happens after the beta, outside the card: the card sells today, this line is
   the honest footnote. Wraps to two lines on narrow screens by design. */
@media (max-width:899px){
  .pricing-grid{grid-template-columns:minmax(0,1fr)} /* full width minus the section's 24px */
  /* the 48px sides are sized for the 620px card; on a 320px card they would eat a third
     of the line. Back to 32 — still clear of the edge, still on the grid. */
  .pricing-card{padding:40px 32px}
}

/* ================= faq: questions? answers. (apple pattern, left headline, big rows) ================= */
.faq{flex-shrink:0;padding:var(--breath-in-compact) 24px var(--breath-out-compact)} /* compact size-rhythm tile (Mo 04.08.) */
.faq-inner{max-width:1080px;margin-inline:auto}
.faq h2{font-size:40px;font-weight:600;line-height:var(--lh-display);text-align:left}
.faq-list{margin-top:var(--breath-head-compact)} /* compact size-rhythm tile (Mo 04.08.) — was var(--breath-head) */
.faq-q{border-bottom:1px solid var(--ctl-border)}
.faq-q:first-child{border-top:1px solid var(--ctl-border)}
.faq-q summary{
  list-style:none;
  cursor:pointer;
  padding:28px 2px;
  font-size:21px;font-weight:600;color:var(--schwarz);
  display:flex;align-items:center;justify-content:space-between;gap:24px;
}
.faq-q summary::-webkit-details-marker{display:none}
.faq-plus{flex-shrink:0;color:var(--schiefer);transition:transform 0.18s ease}
.faq-q[open] .faq-plus{transform:rotate(45deg)} /* plus becomes a quiet x when open */
.faq-q p{font-size:16px;color:var(--dim-60);max-width:720px;padding:0 2px 28px}
@media (max-width:899px){
  .faq-q summary{font-size:17px;padding:20px 2px}
  .faq-q p{padding-bottom:20px}
}

/* ================= closing: the page's last word — pure typography, airy, cloud tile.
   Same build as autonomy (centred, no media, no card), but the CTA is FILLED: it is
   the page's closing handover, so it carries the hero's own button 1:1. That puts a
   second filled button in the field of view of the glass navbar — the second
   documented exception to the one-CTA rule after pricing (§3). ================= */
.closing{
  position:relative;overflow:hidden; /* the stage light is clipped by this tile, never by a joint */
  flex-shrink:0;
  /* Kompakt auf JEDER Breite (Mo 15.08.). Der Schluss ist reine Typografie wie Guardrails
     und traegt praktisch gleich viel Inhalt — 157px gegen 153px am Desktop —, stand aber im
     luftigen Rhythmus und war damit 100px hoeher als die Kachel mit demselben Aufbau: 445 zu
     345, und mit 65 % der hoechste Luftanteil der Seite. Mos Beobachtung dahinter ist die
     eigentliche Regel: er ist der einzige luftige Abschnitt OHNE Eyebrow, traegt also ein
     Element weniger im Kopf und braucht darum auch weniger Kopfluft. 445 auf 330. */
  padding:var(--breath-in-compact) 24px var(--breath-out-compact);
  display:flex;flex-direction:column;align-items:center;text-align:center;
}
/* the same light, second host: it sits behind the headline+CTA block so the tile does not
   read as empty now that the field is gone. Centred on the headline, not on the tile — the
   copy block starts at the breath padding, so the tile centre would sit below the text. */
.closing::before{
  content:'';position:absolute;z-index:0;
  left:50%;top:var(--breath-in);
  width:min(1100px,86%);height:70%;
  transform:translate(-50%,-35%);
  background:var(--stage-light);
  pointer-events:none;
}
.closing>*{position:relative;z-index:1} /* copy over the stage light (it sits at z0) */
.closing h2{font-size:40px;font-weight:600;line-height:var(--lh-display)} /* section standard, page-wide */
.closing-sub{font-size:16px;font-weight:400;color:var(--dim-60);max-width:720px;margin-top:16px} /* §3 pairing: 16px sub under a 40px headline */

/* ================= footer: the studio footer strip on the page's one black tile ================= */
/* Four centred rows, in this order: signature strip, copyright + legal on one baseline,
   address, socials. The 07.08. rebuild gave the address its own voice — Inter against
   mono, larger, fenced off by a hairline — to build hierarchy without touching colour.
   That is reverted (Mo 11.08.): the address now speaks the same 9px mono as everything
   else, sits last instead of first, and the hairline is gone with it. The footer is one
   quiet block again, ordered by grouping alone. The socials close it (Mo 11.08.). The centred axis is kept — a left-aligned
   variant was built and rejected: it was the only left-set element on a page that is
   centred throughout, and the break read as an accident. */
.footer{
  flex-shrink:0;
  background:var(--schwarz); /* the single dark tile of the page (Mo 02.08.) */
  padding:48px 24px 40px;
  display:block;
}
/* On a phone the footer read as a tall dark block. Both the padding and the row gaps come
   down (Mo 15.08., against Apple's mobile footer as the reference): 241px to 132. Desktop
   keeps 48/40 and its own 20/18/19 rhythm. */
@media (max-width:899px){
  .footer{padding:28px 24px 24px}
}
.footer-inner{max-width:1080px;margin-inline:auto;text-align:center}
/* The address, now the footer's LAST line and no longer its first (Mo 11.08.). It gave up
   the separate voice it used to have: same mono, same 9px, same tracking and same
   --hell-45 as every other label down here, so the footer speaks in exactly one register.
   The Inter-against-mono contrast and the 17px size that carried it are both gone, and the
   hairline that used to fence it off went with them — with the line at the bottom there is
   nothing left to divide. */
.footer-contact{
  display:inline-block;
  font-family:var(--mono);font-size:9px;font-weight:500;letter-spacing:0.12em;
  text-transform:uppercase;white-space:normal;color:var(--hell-45);
  margin-top:18px; /* same step as the socials below it, so the bottom rows keep one rhythm */
  transition:color 0.15s ease;
}
.footer-contact:hover{color:var(--schiefer)} /* the page's one accent — the only colour the footer ever shows (Mo 07.08.) */
/* ROW 1 — the signature: SILLQ, the care symbols, the motto. Measured at 4.53:1, the
   symbols sit exactly on the text tone; they were never the weak spot. */
.footer-strip{display:flex;align-items:center;justify-content:center;gap:14px;color:var(--hell-45)}
/* ROWS 2-4 — copyright and legal on ONE baseline (they were two rows), then the address,
   then the socials as the last line. All three steps share the same 18-20px rhythm. */
.footer-base{display:flex;align-items:center;justify-content:center;gap:18px;margin-top:20px}
.footer-socials{margin-top:18px}
.footer-brand{
  font-family:var(--mono);font-size:9px;font-weight:500;letter-spacing:0.12em; /* 500 statt Studio-600: auf Schwarz rendert 600 heller — gleiche Stimme wie der Rest (Mo 02.08.) */
  white-space:nowrap;color:var(--hell-45);
  transition:color 0.15s ease;
}
.footer-brand:hover{color:var(--hell-80)}
.footer-care{display:flex;align-items:center;gap:8px}
/* 13px, nicht 16 (Mo 15.08.): gemessen stand die 16px-Box gegen eine Versalhoehe von
   6,57px — Faktor 2,44, und genau so las sich die Reihe auch, die Zeichen schlugen den
   Namen. Die Glyphe fuellt rund 73 % ihrer Box, bei 13px sind das ~9,5px Zeichenhoehe und
   damit Faktor 1,45 zur Versalhoehe: eine Spur groesser als die Buchstaben, wie es ein
   Zeichen neben Versalien sein soll. Die HTML-Attribute tragen dieselbe Zahl, damit vor
   dem Stylesheet nichts springt. */
.footer-care img{display:block;width:13px;height:13px;opacity:0.45;filter:invert(1)} /* studio glyphs are dark; inverted for the black tile */
.footer-divider{width:1px;height:12px;background:var(--hell-16)}
/* one shared row rule for both link groups */
.footer-links,.footer-socials{display:flex;align-items:center;justify-content:center;gap:18px}
/* the copyright year shares the legal links' chrome voice — one pattern, one source */
.footer-legal,.footer-copy{
  font-family:var(--mono);font-size:9px;font-weight:500;letter-spacing:0.12em;
  text-transform:uppercase;color:var(--hell-45);
  transition:color 0.15s ease;
}
.footer-legal:hover{color:var(--hell-80)}
.footer-social{
  display:inline-flex;align-items:center;
  color:var(--hell-45);
  transition:color 0.15s ease;
}
.footer-social:hover{color:var(--hell-80)}

/* ================= touch round (mobile round 05.08.) =================
   Coarse pointers get the 44px minimum hit area of the Apple HIG. Implemented as a
   transparent, centered ::after — no visual, no size and no spacing changes, so the
   1:1 studio dimensions (color dots 36px, topbar 52px) stay exactly as specified.
   Adjacent targets in a row may overlap slightly; a tap then lands on the nearer
   neighbour, which still beats a 14px target. */
@media (pointer:coarse){
  .nav-menu a,.nav-login,.topbar-cta,.logo-link,.hero-cta,.hero-more,
  .s4-dot,.s4-seg-btn,.s4-chip,.s4-cta,.autonomy-cta,.closing-cta,.pricing-cta,
  .vmodal-x,.vmodal-toggle,
  .footer-social{position:relative}
  /* .s4-art is already position:absolute — it takes the hit area but NOT the
     position:relative above, which would tear it off the garment. */
  .nav-menu a::after,.nav-login::after,.topbar-cta::after,.logo-link::after,.hero-cta::after,.hero-more::after,
  .s4-dot::after,.s4-seg-btn::after,.s4-chip::after,.s4-cta::after,.autonomy-cta::after,.closing-cta::after,.pricing-cta::after,
  .vmodal-x::after,.vmodal-toggle::after,.s4-art::after,
  .footer-social::after{
    content:'';position:absolute;left:50%;top:50%;
    width:max(100%,44px);height:max(100%,44px);
    transform:translate(-50%,-50%);
  }
  /* a range input generates no ::after at all. Padding grows the hit box instead, and
     background-clip keeps the 3px studio track painted at its real size. The negative
     margin gives the added height back to the LAYOUT, so the tool row stays 3px tall
     and nothing below it shifts (it grew to 45px without this). */
  .s4-wash{height:45px;padding-block:21px;background-clip:content-box;margin-block:-21px}
  /* An <input> cannot carry a ::after, so the shared hit-area rule above cannot
     reach the two Pantone fields. The coloured half IS the target instead: it is
     58px tall and focuses its input on tap (app.js). Only the dropdown rows need
     growing — 14px swatch + 2x7px is 28px, eight short of the minimum. */
  .s4-pan-item{padding-block:15px}
  /* the rows are further apart since the rebuild, but the 44px hit areas of the strip,
     the baseline and the socials would still meet — the gaps carry the fix */
  /* The stacked TEXT rows down here give up their 44px box (Mo 15.08., decided against
     Apple's own mobile footer, which sets these lines at plain line pitch). It is the same
     reason the rule already exempts inline links in copy: a 44px area on a 14px line steals
     the taps of the lines above and below, and forcing it apart is what made the footer
     read as a tall dark block. The rows now sit at 12px, the way a paragraph of links does.
     The SOCIAL ICONS keep their 44px — they are icons, not a reading line, and their row
     has 16px of clearance above it so the box does not reach into the line before it. */
  .footer-socials{gap:28px}
  .footer-base{margin-top:12px}
  .footer-contact{margin-top:12px}
  .footer-socials{margin-top:16px}
  /* the print chips are the section's only navigation. 14px padding on a 13px line
     already clears 44px, so they need no pseudo hit area — measured, not assumed. */
  .s3-chips{gap:10px}
}

/* ================= video modal: the uncut demo (Mo 07.08.) =================
   Not a lightbox: the backdrop is FULLY OPAQUE wolke, so the page simply gives way and
   the video sits in the hero's own frame. No dimming, no shadow, no second edge — the
   only accent is schiefer on the played part of the scrubber. */
.vmodal{
  position:fixed;inset:0;z-index:60;
  background:var(--wolke); /* 100% opaque on purpose: the page behind disappears */
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:24px;
}
.vmodal[hidden]{display:none}
.vmodal-inner{
  /* EXACTLY the hero card's end-state width. --hero-end-w is published by app.js from the
     same calculation the scroll scene uses (app.js heroEndWidth), so the two cannot drift;
     the fallback restates it in CSS only for the moment before the first measure.
     Second term: if head and foot would not fit at that width, the frame scales down
     proportionally — the aspect ratio is held by .vmodal-stage, so only the size gives. */
  --vmodal-chrome:84px; /* foot row + margin + the sheet's padding (the head row is gone) */
  /* Zuerst in vh als Rueckfall fuer Browser ohne svh, direkt danach dieselbe Rechnung in
     svh (Mo 26.08.). svh ist die KLEINE Ansicht — die mit ausgefahrenen iOS-Leisten; mit vh
     rechnete das Modal dort bis zu 90px hoeher, als sichtbar ist, und sprang beim Ein- und
     Ausblenden der Leisten. Wer svh nicht kennt, ueberliest die zweite Zeile. */
  width:min(
    var(--hero-end-w,min(calc(0.9*var(--tile-w)),calc(0.9*(100vh - var(--frame-t) - var(--frame-b))*1600/929))),
    calc((100vh - var(--vmodal-chrome))*1600/929),
    calc(100vw - 48px)
  );
  width:min(
    var(--hero-end-w,min(calc(0.9*var(--tile-w)),calc(0.9*(100svh - var(--frame-t) - var(--frame-b))*1600/929))),
    calc((100svh - var(--vmodal-chrome))*1600/929),
    calc(100vw - 48px)
  );
  display:flex;flex-direction:column;
}
.vmodal-foot{margin-top:14px;text-align:center;color:var(--dim-60)}
/* the video surface: cursor is the only affordance, and it hides with the bar */
/* the only affordance on either video surface is the cursor — same rule as the tech pack
   medium (CLAUDE.md §4): no play button, no overlay chrome, no hint text. */
.stage-video,.vmodal-stage,.s2-media{cursor:pointer}
.vmodal-stage video{background:var(--surface-platzhalter)}
.vmodal-stage.idle{cursor:none}
/* transport cue: one soft pulse per play/pause, then gone. Black disc at reduced opacity
   so the glyph survives on bright and dark frames alike. Never clickable. */
/* one cue for both video surfaces (Mo 09.08.): the modal and the hero card share the
   symbol, the pulse and the timing — one pattern, one source (CLAUDE.md §6.3). */
.vmodal-cue,.hero-cue,.s2-cue{
  position:absolute;left:50%;top:50%;z-index:3;
  width:64px;height:64px;margin:-32px 0 0 -32px;
  border-radius:50%;
  background:rgba(20,17,15,0.55);
  color:var(--wolke);
  display:flex;align-items:center;justify-content:center;
  opacity:0;pointer-events:none;
}
.vmodal-cue svg,.hero-cue svg,.s2-cue svg{display:none}
.vmodal-cue[data-cue="play"] .vmodal-cue-play,
.vmodal-cue[data-cue="pause"] .vmodal-cue-pause,
.hero-cue[data-cue="play"] .vmodal-cue-play,
.hero-cue[data-cue="pause"] .vmodal-cue-pause,
.s2-cue[data-cue="play"] .vmodal-cue-play,
.s2-cue[data-cue="pause"] .vmodal-cue-pause{display:block}
.vmodal-cue.go,.hero-cue.go,.s2-cue.go{animation:video-cue 0.5s linear forwards}
@keyframes video-cue{
  0%{opacity:0.95;transform:scale(0.84)}
  55%{opacity:0.8;transform:scale(1.10)}
  100%{opacity:0;transform:scale(1.30)}
}
/* ONE control bar along the bottom edge of the video (Mo 08.08.). No scrim, no gradient,
   no shadow: the transport sits in its own opaque pills, so nothing is smeared across
   the footage. Everything here hides together — bar, pills and progress line. */
.vmodal-bar{
  position:absolute;left:12px;right:12px;bottom:12px;z-index:4;
  padding:0 4px 4px;
  opacity:1;
  transition:opacity 0.2s var(--ease);
}
.vmodal-stage.idle .vmodal-bar{opacity:0;pointer-events:none}
/* scrubber: a 16px hit strip carrying a 3px track that grows on stage hover */
.vmodal-scrub{
  position:relative;
  height:16px;
  display:flex;align-items:center;
  cursor:pointer;
}
.vmodal-track{
  position:relative;display:block;width:100%;height:3px;
  background:rgba(250,250,250,0.35);
  border-radius:var(--r-pill);
  transition:height 0.18s var(--ease),background-color 0.18s var(--ease);
}
.vmodal-fill{
  position:absolute;left:0;top:0;bottom:0;
  width:var(--vp,0%);
  background:var(--schiefer);
  border-radius:var(--r-pill);
}
.vmodal-knob{
  position:absolute;left:var(--vp,0%);top:50%;
  width:12px;height:12px;margin:-6px 0 0 -6px;
  border-radius:50%;
  background:var(--schiefer);
  opacity:0;
  transition:opacity 0.18s var(--ease);
}
/* touch: the scrubber grows its own hit box and pulls the growth back out with negative
   margins, so the bar keeps its height and the track stays exactly where it was. Where the
   44px box runs under the transport row, the row wins the touch — it paints later.
   Must sit AFTER the .vmodal-scrub base rule: same specificity, later wins (Mo 09.08.). */
@media (pointer:coarse){.vmodal-scrub{height:44px;margin-block:-14px}}
.vmodal-stage:hover .vmodal-track,.vmodal-scrub:focus-visible .vmodal-track{height:5px;background:rgba(250,250,250,0.5)}
.vmodal-stage:hover .vmodal-knob,.vmodal-scrub:focus-visible .vmodal-knob,.vmodal-scrub.dragging .vmodal-knob{opacity:1}
/* transport row: play/pause as a disc, the running time as a pill — both OPAQUE, so the
   controls read as objects on the footage instead of as a translucent haze. The right
   side stays empty: no volume, no fullscreen, no settings, no captions. */
.vmodal-row{display:flex;align-items:center;gap:8px;margin-top:6px}
.vmodal-toggle{
  display:flex;align-items:center;justify-content:center;
  width:38px;height:38px;padding:0;
  /* youtube-style translucency, but not below the legibility floor: over pure white
     footage the fill must render at <=116 grey for wolke text to clear AA, which puts
     the alpha at >=0.60. 0.62 keeps a small margin and still shows the video through. */
  background:rgba(20,17,15,0.62);color:var(--wolke);
  border:0;border-radius:50%;
  cursor:pointer;
}
.vmodal-toggle svg{display:block}
.vmodal-toggle .vmodal-ico-play{display:none;margin-left:2px} /* optical centre of a triangle */
.vmodal-stage.paused .vmodal-toggle .vmodal-ico-play{display:block}
.vmodal-stage.paused .vmodal-toggle .vmodal-ico-pause{display:none}
.vmodal-time{
  display:flex;align-items:center;
  height:38px;padding:0 18px;
  background:rgba(20,17,15,0.62);
  border-radius:var(--r-pill);
  font-family:var(--mono);font-size:12px;font-weight:400;letter-spacing:0.06em;
  color:var(--wolke);
  font-variant-numeric:tabular-nums;
}
/* close: the studio's hairline square, never a filled button */
.vmodal-x{
  position:absolute;top:24px;right:24px;z-index:4;
  width:38px;height:38px;
  display:flex;align-items:center;justify-content:center;
  background:transparent;color:var(--schwarz);
  border:1px solid var(--ctl-border);
  border-radius:var(--r-8);
  cursor:pointer;
  transition:border-color 0.15s ease,background-color 0.15s ease;
}
.vmodal-x:hover{border-color:var(--schwarz);background:rgba(20,17,15,0.04)}
/* open: the wolke sheet fades in while the frame settles from 97% to 100% */
.vmodal.opening{animation:vmodal-in 0.3s var(--ease) forwards}
.vmodal.opening .vmodal-inner{animation:vmodal-scale 0.3s var(--ease) forwards}
.vmodal.closing{animation:vmodal-in 0.3s var(--ease) reverse forwards}
.vmodal.closing .vmodal-inner{animation:vmodal-scale 0.3s var(--ease) reverse forwards}
@keyframes vmodal-in{from{opacity:0}to{opacity:1}}
@keyframes vmodal-scale{from{transform:scale(0.97)}to{transform:none}}
@media (max-width:699px){
  .vmodal{padding:16px}
  .vmodal-inner{width:min(100%,calc((100vh - var(--vmodal-chrome))*1600/929))}
  .vmodal-inner{width:min(100%,calc((100svh - var(--vmodal-chrome))*1600/929))}
  .vmodal-x{top:16px;right:16px}
  /* Bedienelemente skalieren mit dem Rahmen (Mo 14.08.). Auf dem Desktop ist der Rahmen
     ~1230px breit und ein 38px-Knopf nimmt 3% davon ein; hier ist er ~380px breit und
     derselbe Knopf nimmt 10%. Die Groessen sind deshalb eine Stufe kleiner — die
     44px-Trefferflaeche bleibt, sie kommt aus der ::after-Regel weiter oben. */
  .vmodal-bar{left:10px;right:10px;bottom:10px;padding:0 2px 2px}
  .vmodal-row{gap:6px;margin-top:5px}
  .vmodal-toggle{width:30px;height:30px}
  .vmodal-toggle svg{width:13px;height:13px}
  .vmodal-time{height:30px;padding:0 12px;font-size:10px;letter-spacing:0.05em}
  /* Die Leiste versteckt sich hier wie auf dem Desktop: sichtbar bei Beruehrung und
     solange pausiert, sonst nach IDLE_MS weg. Vorher zwang eine Regel an dieser Stelle
     sie dauerhaft sichtbar ("nothing ever hides") — auf dem kleinen Rahmen verdeckte das
     dauerhaft ein Zehntel des Videos. Griff und Spur bleiben grob, das ist die
     Touch-Groesse; nur das Verstecken kehrt zurueck. */
  .vmodal-track{height:5px;background:rgba(250,250,250,0.5)}
  .vmodal-knob{opacity:1}
  .vmodal-stage.idle{cursor:pointer}
}
@media (prefers-reduced-motion:reduce){
  .vmodal.opening,.vmodal.closing,
  .vmodal.opening .vmodal-inner,.vmodal.closing .vmodal-inner{animation:none}
  .vmodal-cue.go,.hero-cue.go,.s2-cue.go{animation:none;opacity:0} /* no scaling pulse; the state is read from the bar */
  .vmodal-track,.vmodal-knob{transition:none}
}

/* ================= legal pages: imprint / privacy / terms ================= */
/* ONE component for all three (§6.3). It began as a page-scoped <style> in imprint.html,
   was copied into privacy.html, and by the third page the duplication had to end — the
   blocks had already drifted apart in line-height and section gap. Two structures share
   these rules: the imprint's dl/dt/dd and the prose pages' section/h2/p. Nothing here
   invents a value; every one is an existing token. */
.legal{flex-shrink:0;padding:var(--breath-in-compact) 24px var(--breath-out-compact)}
.legal-inner{max-width:1080px;margin-inline:auto}
.legal h1{font-size:40px;font-weight:600;line-height:var(--lh-display)} /* section standard, not the hero display size */
.legal-note{font-size:16px;color:var(--dim-60);max-width:720px;margin-top:24px}
.legal-body{margin-top:var(--breath-head-compact);max-width:720px;display:flex;flex-direction:column;gap:38px}
/* spacing model, three rules and no exceptions list: every child of a section sits 12px
   below its predecessor, the label supplies its own 10px instead, and a flush wrapper
   closes the gap entirely. Works for paragraphs, lists and nested blocks alike. */
.legal-sec{display:flex;flex-direction:column}
.legal-sec > * + *{margin-top:12px}
/* the page-wide .micro-label sits at --dim-32 and measures 3.2:1, which misses AA; the
   legal pages have to clear it everywhere, so their label runs one rung darker at 5.7:1 */
.legal-label{font-family:var(--mono);font-size:10px;font-weight:500;text-transform:uppercase;letter-spacing:.12em;color:var(--dim-60);margin:0 0 10px}
.legal-sec > .legal-label + *{margin-top:0} /* the label already carried the step */
.legal-p{font-size:16px;font-weight:400;line-height:1.6;color:var(--schwarz)} /* no margin:0 here — the global reset covers it, and repeating it would out-order the spacing rule above */
/* flush — consecutive lines of ONE block: a name and its role, a street and its city.
   line-height 1.5 is the exact 24px box the legal graphics were generated on, so artwork
   lines and text lines share a single pitch and the block sets like an address. Usable on
   a whole section (imprint rows) or on a wrapper inside one (the party block in terms). */
.legal-flush{display:flex;flex-direction:column}
.legal-flush > * + *{margin-top:0}
.legal-flush .legal-p{line-height:1.5}
/* hover carries the brand blue on the RULE, not on the type: --hover-blau measures 3.06:1
   on wolke and would drop link text under AA the moment the cursor lands on it */
.legal-body a{text-decoration:underline;text-underline-offset:3px;
  transition:text-decoration-color .15s ease,text-decoration-thickness .15s ease}
.legal-body a:hover{text-decoration-color:var(--hover-blau);text-decoration-thickness:2px}
/* the page-wide focus ring is schiefer at 2.5:1 and misses the 3:1 WCAG 1.4.11 asks of a
   focus indicator; on these pages it goes to schwarz */
.legal-body a:focus-visible{outline-color:var(--schwarz)}
/* enumerated points: no bullets, the leading phrase carries the eye */
.legal-list{list-style:none;display:flex;flex-direction:column;gap:10px}
.legal-list li{font-size:16px;line-height:1.6;color:var(--schwarz)}
.legal-list b{font-weight:600}
/* company name, managing director and address are artwork, never markup (docs/decisions.md).
   Heights are locked to the 24px line box the graphics were generated on. */
.legal-art{display:block;width:auto;max-width:100%}
.legal-art--line{height:24px}
.legal-art--block{height:48px}
.legal-stamp{margin-top:8px;font-family:var(--mono);font-size:10px;font-weight:500;
  text-transform:uppercase;letter-spacing:.12em;color:var(--dim-60)}

/* ================= entrance (load, once) ================= */
@media (prefers-reduced-motion:no-preference){
  /* masked line reveal */
  h1 .line{transform:translateY(110%);animation:line-in .6s var(--ease) forwards}
  h1 .line-mask:nth-child(2) .line{animation-delay:.08s}
  /* the gesture lands first (both lines, ~680ms), THEN sub + cta rise as an
     identical pair (same animation, 80ms apart) — one vocabulary, sequenced */
  .hero-sub{opacity:0;animation:sub-in .3s var(--ease) .7s forwards}
  .hero-cta{opacity:0;animation:sub-in .3s var(--ease) .78s forwards}
  .hero-more{opacity:0;animation:sub-in .3s var(--ease) .86s forwards}
  /* video card: the configurator's garment entrance */
  .stage-video{opacity:0;animation:garment-entrance .7s var(--ease) .4s forwards}
  /* pricing: reveal -> sub -> card, the page grammar (§3 Motion). The card used to
     come at .5s, which was fine while the head had no subline; with one it would
     overtake its own sub, so both now sit on the documented 700/840ms beats. */
  .pricing-head .line{transform:translateY(110%)}
  .pricing.in .pricing-head .line{animation:line-in .6s var(--ease) forwards}
  .pricing-sub{opacity:0}
  .pricing.in .pricing-sub{animation:sub-in .3s var(--ease) .7s forwards}
  .pricing-grid{opacity:0}
  .pricing.in .pricing-grid{animation:garment-entrance .7s var(--ease) .84s both}
  /* process: reveal -> sub -> the two chains build top-down, left first. The right one
     starts later on purpose, so the length difference happens in front of the eye
     instead of being there from the start. */
  .process .line{transform:translateY(110%)}
  .process.in .line{animation:line-in .6s var(--ease) forwards}
  .process-sub,.process-label,.process-step{opacity:0}
  .process.in .process-sub{animation:sub-in .3s var(--ease) .7s forwards}
  .process-col--without{--chain-start:840ms}
  .process-col--with{--chain-start:1200ms}
  /* --i comes from the shared node-index rule in the layout block (one source) */
  .process.in .process-label{animation:sub-in .3s var(--ease) var(--chain-start) forwards}
  .process.in .process-step{animation:sub-in .3s var(--ease) calc(var(--chain-start) + var(--i)*var(--step-stagger)) forwards}
  /* founders: reveal -> quiet pair */
  .founders .line{transform:translateY(110%)}
  .founders.in .line{animation:line-in .6s var(--ease) forwards}
  .founders-sub,.founders-stats{opacity:0}
  .founders.in .founders-sub{animation:sub-in .3s var(--ease) .7s forwards}
  .founders.in .founders-stats{animation:sub-in .3s var(--ease) .84s forwards}
  /* faq: reveal -> the list rises as one quiet unit */
  .faq .line{transform:translateY(110%)}
  .faq.in .line{animation:line-in .6s var(--ease) forwards}
  .faq-list{opacity:0}
  .faq.in .faq-list{animation:sub-in .3s var(--ease) .7s forwards}
  /* autonomy: same grammar as guardrails — reveal, then the sub */
  .autonomy .line{transform:translateY(110%)}
  .autonomy.in .line{animation:line-in .6s var(--ease) forwards}
  .autonomy-sub,.autonomy-cta{opacity:0}
  .autonomy.in .autonomy-sub{animation:sub-in .3s var(--ease) .7s forwards}
  .autonomy.in .autonomy-cta{animation:sub-in .3s var(--ease) .84s forwards}
  /* closing: reveal, then sub and CTA as the quiet pair */
  .closing .line{transform:translateY(110%)}
  .closing.in .line{animation:line-in .6s var(--ease) forwards}
  .closing-sub,.closing-cta{opacity:0}
  .closing.in .closing-sub{animation:sub-in .3s var(--ease) .7s forwards}
  .closing.in .closing-cta{animation:sub-in .3s var(--ease) .84s forwards}
  /* guardrails: the claim reveals like the hero (two lines), sub follows */
  .guardrails .line{transform:translateY(110%)}
  .guardrails.in .line{animation:line-in .6s var(--ease) forwards}
  .guardrails-sub{opacity:0}
  .guardrails.in .guardrails-sub{animation:sub-in .3s var(--ease) .7s forwards}
  /* s2: signature reveal -> product entrance (garment) -> quiet pair */
  .s2-head .line{transform:translateY(110%)}
  .s2.in .s2-head .line{animation:line-in .6s var(--ease) forwards}
  .s2-sub{opacity:0}
  .s2.in .s2-sub{animation:sub-in .3s var(--ease) .7s forwards}
  .s2-media{opacity:0}
  .s2.in .s2-media{animation:garment-entrance .7s var(--ease) .5s both}
  /* s3: same grammar, left-aligned — reveal, gallery entrance as ONE unit, quiet pair */
  .s3-head .line{transform:translateY(110%)}
  .s3.in .s3-head .line{animation:line-in .6s var(--ease) forwards}
  .s3-sub{opacity:0}
  .s3.in .s3-sub{animation:sub-in .3s var(--ease) .7s forwards}
  .s3-card{opacity:0}
  .s3.in .s3-card{animation:garment-entrance .7s var(--ease) .5s both}
  .s3-bar{opacity:0}
  .s3.in .s3-bar{animation:sub-in .3s var(--ease) 1.2s forwards} /* chips and spec enter as ONE unit — they are one row now */
  /* s4 header: the hero's masked line reveal, softened (0.8s) */
  .s4-head .line{transform:translateY(110%)}
  .s4.in .s4-head .line{animation:line-in .8s var(--ease) forwards}
  .s4-sub{opacity:0}
  .s4.in .s4-sub{animation:sub-in .5s var(--ease) .85s forwards} /* after the 0.8s reveal lands (same sequencing as the hero) */
  /* s4 scene: stage fades in softly on entry; the growth itself is scroll-driven (JS scale).
     at p=1 the .ready class hands over: tools and spec fade in as smooth transitions */
  .s4-stage{opacity:0;transition:opacity 0.8s var(--ease)}
  .s4.in .s4-stage{opacity:1}
  .s4-tools{opacity:0;transform:translateY(12px);transition:opacity 0.7s var(--ease),transform 0.7s var(--ease)}
  .s4-tools-right{transition-delay:120ms}
  .s4-config.ready .s4-tools{opacity:1;transform:none}
  .s4-config:not(.ready) .s4-tools,.s4-config:not(.ready) .s4-art{pointer-events:none}
  /* the print (and its measurement) appears only once the garment is full size,
     fading in softly at the collar before it places itself to 120.0 */
  .s4-art,.s4-line,.s4-endcap,.s4-pill{opacity:0;transition:opacity 0.5s var(--ease)}
  .s4-config.show .s4-art,.s4-config.show .s4-line,.s4-config.show .s4-endcap,.s4-config.show .s4-pill{opacity:1}
}
@keyframes line-in{to{transform:none}}
@keyframes sub-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes garment-entrance{from{opacity:0;transform:translateY(22px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}


.wrap-sentence{display:none} /* the mobile-only sentence break; the media block below turns it on */

/* Headlines on phones (Mo 14.08.). Two defects, both page-wide, both measured before
   they were fixed. FIRST, the size never moved: every section headline stood at a flat
   40px from a 320px screen to a 430px one, so the same words met a line box that grew by
   a third while the type grew by nothing — on a 390 that is 40px on 322px of text, half
   again the ratio the desktop ever reaches. It scales with the screen now and lands back
   on the full 40px at 445px, so nothing between here and the desktop changes character.
   SECOND, nothing steered the break, so the browser filled line by line and left over
   whatever was left over: 'it.' on 14% of the width, 'later.' on 27%, 'floor.' on 32%.
   A closing line at a quarter width is what reads as unclean. text-wrap:balance spreads
   the lines evenly instead; browsers without it fall back to exactly today's behaviour.
   Line counts per width are in docs/reference.md. Above 900px not one pixel moves.
   IT HAS TO SIT LAST. The eleven per-section 40px declarations carry the same
   specificity as the shared selector here, and a media query adds none — placed up
   with the other shared headline rules the size was simply overwritten further down
   (balance came through, the size did not, which is the confusing half-effect). Last
   in the cascade beats it without a single !important. */
/* The hero gets its OWN breakpoint, and it is not 899px. Its clamp only bottoms out at
   800px, so between 800 and 899 it is still growing on its own and an override there
   would have shrunk it — and left a 5px step at exactly 900. 458px is where 9.6vw
   reaches the 44px the old rule held below 800, so the two curves meet without a seam. */
@media (max-width:458px){
  h1{font-size:clamp(30px,9.6vw,44px)} /* keeps the hero's two authored lines whole down to 320px — below 402px it used to break into four */
}
@media (max-width:899px){
  .guardrails h2,.s2-head h2,.s3-title,.s4-head h2,.autonomy h2,.process h2,
  .founders h2,.pricing-head h2,.faq h2,.closing h2{
    font-size:clamp(30px,9vw,40px);
    text-wrap:balance;
  }
  /* balance optimises for equal line LENGTHS, not for sentence boundaries, and at 360px
     it picked 'Drop it / anywhere. It's / already measured.' — worse than what it
     replaced. The configurator headline is the page's only one made of two full
     sentences, so it gets the break placed by hand; every other headline balances
     cleanly on its own and must not carry this. */
  .wrap-sentence{display:inline}
}

/* ================= reduced motion: no pin, no reserve, static ================= */
@media (prefers-reduced-motion:reduce){
  .hero-wrap{height:auto}
  .hero{
    position:static;
    height:auto;min-height:100%;
    padding-bottom:32px;
    justify-content:center;
  }
  .hero-head{position:static;margin:48px auto 0}
  .stage-slot{margin-top:64px}
  .s4-wrap{height:auto}
  .s4{position:static;height:auto;padding:var(--breath-in) 24px var(--breath-out)}
  .s4-stage{height:auto;width:min(620px,100%)}
  /* end states shown directly: navbar stays the static tile */
  .topbar{transform:none;visibility:visible}
  .glassbar{display:none}
}

/* ================= Umbruchverhalten, seitenweit (Mo 26.08.) =================
   Gemessen vor dem Eingriff: 5 Widows, alle unter 768px — "calendar." und "does."
   (Autonomy, 390), "production-checked." (Guardrails, 430), "weeks." (Process, 430),
   "good." (Pricing, 430). Ab 768px war die Seite bereits sauber. Gemessen wurde der
   TATSAECHLICHE Umbruch ueber Range-Rechtecke je Wort, und je SEGMENT zwischen den
   <br> — sonst faellt "...three weeks." durch, weil danach noch ein Satz folgt.

   Beide Eigenschaften sind progressive enhancement: wer sie nicht kennt, bekommt exakt
   das heutige Verhalten. Deshalb kein Fallback und kein !important.

   DIESER BLOCK STEHT AM ENDE DER DATEI, und das ist tragend — aus demselben Grund wie
   der 899px-Block darueber: die elf Sektionsregeln tragen dieselbe Spezifitaet, und ein
   frueher platzierter Block waere von ihnen ueberschrieben worden. */

/* 1. Headlines: Zeilen gleich lang statt erste voll, letzte fast leer. */
h1,
.guardrails h2, .s2-head h2, .s3-title, .s4-head h2, .autonomy h2, .process h2,
.founders h2, .pricing-head h2, .faq h2, .closing h2, .vmodal h2 {
  text-wrap:balance;
}

/* 2. Sublines und Fliesstext: `pretty` zieht bei Bedarf ein Wort in die vorletzte Zeile
      herunter, ohne die uebrigen Zeilen anzufassen — genau das Mittel gegen Widows im
      Absatzsatz. Auf Headlines waere es das falsche: dort will man `balance`. */
.hero-sub, .guardrails-sub, .s2-sub, .s3-sub, .s4-sub, .process-sub,
.founders-sub, .autonomy-sub, .pricing-sub, .closing-sub,
.faq-q p, .s3-spec, .legal-inner p {
  text-wrap:pretty;
}

/* 3. Lesebreiten in ch statt allein in px: die Zeilenlaenge bleibt damit ueber alle
      Breiten in einem lesbaren Band, und die Umbruchpunkte werden vorhersehbar.
      Die Werte sind AUFGEMESSEN, nicht geraten — sie entsprechen der Breite, die jede
      Subline am Desktop heute schon hat (720px bei 16px Inter sind rund 82ch), gekappt
      auf das Band, in dem keine Zeile mehr allein steht. Wo bisher 720px stand, ist die
      neue Grenze bewusst nicht kleiner: `min()` nimmt, was zuerst greift, und am Desktop
      bleibt damit alles auf den Pixel wie es war. */
/* Die Grenzen stehen dort, wo sie heute stehen — nur zusaetzlich in ch ausgedrueckt.
   Ein Versuch mit den 60ch aus der Vorgabe ist GEMESSEN verworfen worden: 60ch sind bei
   16px Inter rund 606px, und damit rutschten Guardrails, Founders und Autonomy am Desktop
   auf je eine Zeile mehr — die Kacheln wuchsen um 24px, die Seite um 72px. Das ist eine
   Layoutverschiebung, und die war ausgeschlossen. Die Widows sind ohnehin ohne die
   Verengung weg (balance, pretty und die gebundenen Wortpaare reichen); die ch-Angabe
   bleibt als obere Schranke fuer den Fall, dass jemand die Schriftgroesse hochsetzt —
   dann greift sie vor der Pixelgrenze und haelt die Zeile lesbar. */
.hero-sub{max-width:min(760px,72ch)}
.s2-sub, .s4-sub, .pricing-sub{max-width:min(720px,72ch);margin-inline:auto}
.process-sub, .autonomy-sub, .founders-sub, .closing-sub{max-width:min(720px,72ch)}

/* 4. Feste Wortpaare stehen im Markup mit &nbsp; (Zahl plus Einheit, "three weeks"),
      damit sie nie ueber eine Zeilengrenze reissen. Nichts davon gehoert ins CSS. */

