/* ZAPPWERK — Werkprüfstand. Hand-formatted. Zero dependencies, no build step.
   Comments here are constraints, not narration. Binding source: docs/KONTRAKT.md §2 Physics. */

/* ---------------------------------------------------------------------------
   §4 type — six pinned files, self-hosted, font-display: block (no fallback
   flash on the sign). Axis ranges match the shipped VF instances exactly. */

@font-face {
  font-family: "Anybody";
  src: url("./fonts/anybody-vf.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 50% 150%;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Saira";
  src: url("./fonts/saira-vf.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 50% 125%;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Archivo";
  src: url("./fonts/archivo-vf.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Martian Mono";
  src: url("./fonts/martian-mono-vf.woff2") format("woff2");
  font-weight: 100 800;
  font-stretch: 75% 112.5%;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("./fonts/plex-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("./fonts/plex-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: block;
}

/* ---------------------------------------------------------------------------
   §3 palette — house tokens (--carbon, --paper, --ash, --line, --volt) are
   declared once, in site/tokens.css, and only consumed here. Accent is
   runtime-switched by bench.js via --accent; the three named presets are
   pre-declared here so JS only ever swaps which token --accent points at,
   never a raw hex value. */

:root {
  --iron: #141416;

  --accent-volt: var(--volt);
  --accent-arc: #DAF4FF;
  --accent-mono: var(--paper);
  --accent: var(--accent-volt);
}

/* ---------------------------------------------------------------------------
   base — page commits to dark (§3). No smooth-easing transitions anywhere
   in this file: state changes are instant or, where a transition exists,
   stepped. KONTRAKT §2 CUT — a channel changes completely or not at all —
   applies to the whole bench, not only the sign. */

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

html {
  background: var(--carbon);
  color-scheme: dark;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--carbon);
  color: var(--paper);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  overflow-x: hidden;
}

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

/* ---------------------------------------------------------------------------
   layout rig — stage fills available height, panel docks after it in normal
   flow (desktop). Mobile media query below turns the panel into a fixed
   overlay sheet instead. */

.zw-rig {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* ---------------------------------------------------------------------------
   §7 stage — the sign centered on carbon, generous void around it.
   overflow: hidden is the horizontal-overflow guard: it clips any skew
   overhang from the sign so the page never gains a scrollbar at ≥320px. */

.zw-stage {
  flex: 1 1 auto;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 42vh;
  padding: 3rem 1.25rem;
  background: var(--carbon);
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   §4/§6 the sign. Root (rootEl passed to createEngine) is .zw-stage; .zw-sign
   and svg.zw-fx are its two children per the DOM contract confirmed against
   engine.js. Slant is skewX on this container per §4/§6 verbatim. Font-size
   via clamp: fills ~80vw up to a cap, never forces horizontal overflow. */

.zw-sign {
  margin: 0;
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0;
  max-width: 100%;
  color: var(--paper);
  text-transform: uppercase;
  letter-spacing: -0.01em;
  line-height: 1;
  white-space: nowrap;
  font-weight: 900;
  font-size: clamp(2.5rem, 13vw, 12rem);
  transform: skewX(var(--slant, -10deg));
}

.zw-sign[data-font="anybody"] {
  font-family: "Anybody", sans-serif;
  font-variation-settings: "wdth" 150, "wght" 900;
}

.zw-sign[data-font="saira"] {
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 125, "wght" 900;
}

.zw-sign[data-font="archivo"] {
  font-family: "Archivo", sans-serif;
  font-variation-settings: "wdth" 125, "wght" 900;
}

/* word-gap bench toggle: ZAPPWERK -> ZAPP WERK. .zw-sign's only children are
   the 8 letters (svg.zw-fx lives in .zw-stage, not here), so nth-child(5)
   is unambiguously the W. */

.zw-sign.is-gapped .zw-letter:nth-child(5) {
  margin-inline-start: 0.28em;
}

/* §6/§7 FELD take: engine.js builds the feTurbulence/feDisplacementMap
   filter (id="zw-field-filter") inside svg.zw-fx but never references it —
   applying a filter is a presentation decision, so the hook lives here,
   keyed off data-take (bench.js sets this on rootEl/.zw-stage). Without
   this rule FELD is inert: the filter exists but nothing ever wears it.
   Scoped to no-preference: engine.js already forces displacement scale to
   0 under reduced motion, but skipping the filter outright avoids any
   SVG-filter rasterization side effect on the still sign (KONTRAKT §2 STILL,
   the poster test), same reasoning as the discharge-look rule below. */

@media (prefers-reduced-motion: no-preference) {
  [data-take="field"] .zw-sign {
    filter: url(#zw-field-filter);
  }
}

/* reduced-motion underline pulse. engine.js sets --flash on rootEl
   (.zw-stage) specifically for the reduced-motion path (and separately,
   independently, on each letter for the normal-motion path, which this
   rule does not read). .zw-sign has no --flash of its own, so it inherits
   rootEl's value through the normal custom-property cascade. Opacity is
   forced to 0 outside prefers-reduced-motion: full motion uses the stroke
   discharge look instead, never both at once. No movement, ever — only
   opacity, keyed directly off the engine's value, no transition. */

.zw-sign::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.14em;
  height: 2px;
  background: var(--accent);
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .zw-sign::after {
    opacity: var(--flash, 0);
  }
}

/* letters: engine hooks only. --jx/--jy are written by engine.js per
   letter; fallbacks here are what render before the engine attaches (or
   if it never does). margin-inline is the tight-tracking control since
   these are flex items, not one text run — letter-spacing has no effect
   between separate boxes. */

.zw-letter {
  display: inline-block;
  margin-inline: -0.015em;
  transform: translate3d(var(--jx, 0), var(--jy, 0), 0);
  will-change: transform;
}

/* discharge look: hard switch on the data-state hook, no transition —
   KONTRAKT §2 CUT, no crossfade between states. Scoped to
   no-preference: engine.js sets data-state="discharge" the same way
   regardless of reduced motion, so without this guard the stroke look
   would fire alongside the underline pulse under reduced motion — §9
   gate 6 requires underline pulse ONLY in that mode, one discharge
   signal, not two. */

@media (prefers-reduced-motion: no-preference) {
  [data-state="discharge"] .zw-letter {
    color: transparent;
    -webkit-text-stroke: 1.5px var(--accent);
  }

  /* chroma split: engine.js only wires the red/blue channel split into the
     FELD SVG filter (feColorMatrix/feOffset/feBlend) — TAKT and RUHE have
     no filter pipeline to carry it, so the same chroma-on-discharge moment
     gets a CSS text-shadow split instead. :not([data-take="field"]) keeps
     the two forms mutually exclusive: never a filter chroma and a shadow
     chroma at once on the same discharge. */
  [data-chroma="1"][data-state="discharge"]:not([data-take="field"]) .zw-letter {
    text-shadow:
      -1px 0 rgba(255, 40, 70, 0.85),
      1px 0 rgba(60, 150, 255, 0.85);
  }
}

/* fx overlay: engine writes only its contents (defs/filter/arc polyline).
   Sized to the full stage so the arc's vertical lift never clips against
   an svg-local viewport edge; overflow: visible is the SVG-spec default
   override (root <svg> defaults to overflow: hidden otherwise). */

.zw-fx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.zw-arc-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1px;
}

/* ---------------------------------------------------------------------------
   §7 panel — machine-plate: iron surface, hairline borders, bilingual DIN
   labels in Saira, readouts in Martian Mono. Accent is reserved for exactly
   three panel surfaces: the active segment, lit lamps, the discharge
   button — KONTRAKT §2 STRIKE, one accent, total. Nowhere else touches
   --accent (not focus rings, not hover states, not borders). */

.zw-panel {
  flex: 0 0 auto;
  background: var(--iron);
  border-top: 1px solid var(--line);
  color: var(--paper);
}

.zw-panel[data-panel="0"] {
  display: none;
}

.zw-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--line);
}

.zw-plate-title {
  margin: 0;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.85rem;
  color: var(--paper);
}

.zw-engine-note {
  margin: 0;
  font-family: "Martian Mono", "IBM Plex Mono", monospace;
  font-size: 0.68rem;
  letter-spacing: 0.03em;
  color: var(--ash);
}

.zw-sheet-toggle {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--paper);
  cursor: pointer;
}

.zw-chevron::before {
  content: "▴";
}

.zw-panel[data-sheet="expanded"] .zw-chevron::before {
  content: "▾";
}

.zw-sheet-toggle:focus-visible,
.zw-seg-input:focus-visible + .zw-seg-label,
.zw-check input:focus-visible,
.zw-discharge-btn:focus-visible,
.zw-util-btn:focus-visible,
.zw-reveal-tab:focus-visible,
input[type="range"]:focus-visible {
  outline: 2px solid var(--paper);
  outline-offset: 2px;
}

.zw-panel-body {
  margin: 0;
  padding: 1rem;
  border: 0;
  max-height: 70vh;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  align-items: start;
  gap: 0.75rem;
}

.zw-field {
  min-width: 0;
  border: 1px solid var(--line);
  margin: 0;
  padding: 0.7rem 0.8rem;
}

.zw-field legend {
  padding: 0 0.35rem;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.68rem;
  color: var(--ash);
}

.zw-field input[type="range"] {
  display: block;
  width: 100%;
  margin: 0.4rem 0 0;
}

.zw-field output {
  display: block;
  margin-top: 0.3rem;
  font-family: "Martian Mono", "IBM Plex Mono", monospace;
  font-size: 0.75rem;
  color: var(--paper);
}

.zw-field-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.85rem;
}

.zw-field-discharge {
  grid-column: 1 / -1;
}

/* segmented control: real radios (native keyboard + a11y), visually hidden;
   the adjacent label is the visible segment. */

.zw-segmented {
  display: flex;
  flex-wrap: wrap;
  border: 1px solid var(--line);
  width: fit-content;
  max-width: 100%;
}

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

.zw-seg-label {
  display: inline-block;
  padding: 0.45rem 0.7rem;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 500;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-size: 0.72rem;
  color: var(--ash);
  border-right: 1px solid var(--line);
  cursor: pointer;
  user-select: none;
}

.zw-seg-label:last-of-type {
  border-right: 0;
}

.zw-seg-input:checked + .zw-seg-label {
  background: var(--accent);
  color: var(--carbon);
}

/* checkboxes: square, plate-consistent. Checked state uses --paper, not
   --accent — accent stays reserved for the three named surfaces. */

.zw-check {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.72rem;
  color: var(--ash);
  cursor: pointer;
}

.zw-check input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin: 0;
  background: transparent;
  border: 1px solid var(--line);
  cursor: pointer;
}

.zw-check input[type="checkbox"]:checked {
  background: var(--paper);
}

/* range inputs: flat line track, square thumb — machine-plate, not a
   friendly rounded slider. */

input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  height: 1px;
  background: var(--line);
  outline: none;
}

input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  background: var(--paper);
  border: 1px solid var(--carbon);
}

input[type="range"]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  background: var(--paper);
  border: 1px solid var(--carbon);
  border-radius: 0;
}

input[type="range"]::-moz-range-track {
  height: 1px;
  background: var(--line);
}

/* lamps: DERATE / REDUCED. Off = --line, lit = --accent (one of the three
   sanctioned accent surfaces). */

.zw-lamp {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

.zw-lamp-dot {
  width: 10px;
  height: 10px;
  background: var(--line);
  border: 1px solid var(--line);
}

.zw-lamp[data-lit="1"] .zw-lamp-dot {
  background: var(--accent);
  border-color: var(--accent);
}

.zw-lamp-label {
  font-family: "Martian Mono", "IBM Plex Mono", monospace;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--ash);
}

/* discharge button — big, industrial, hard :active. No transition: the
   press is instant, a breaker snapping, not a cushioned tap. */

.zw-discharge-btn {
  width: 100%;
  padding: 1.1rem 1.25rem;
  background: var(--accent);
  color: var(--carbon);
  border: 1px solid var(--accent);
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 1rem;
  cursor: pointer;
}

.zw-discharge-btn:active {
  transform: translateY(3px);
  box-shadow: inset 0 3px 0 var(--carbon);
}

.zw-key-hint {
  margin-inline-start: 0.5rem;
  font-family: "Martian Mono", "IBM Plex Mono", monospace;
  font-size: 0.75rem;
  opacity: 0.65;
}

/* copy / hide — secondary, neutral, no accent. */

.zw-util-btn {
  padding: 0.55rem 0.85rem;
  background: transparent;
  color: var(--paper);
  border: 1px solid var(--line);
  font-family: "Martian Mono", "IBM Plex Mono", monospace;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.zw-copy-status {
  font-family: "Martian Mono", "IBM Plex Mono", monospace;
  font-size: 0.68rem;
  color: var(--ash);
}

/* plate footer */

.zw-plate-footer {
  margin: 0;
  padding: 0.6rem 1rem;
  text-align: center;
  border-top: 1px solid var(--line);
  font-family: "Martian Mono", "IBM Plex Mono", monospace;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  color: var(--ash);
}

/* reveal tab — only path back once the panel has been fully hidden
   (?panel=0 or the HIDE PANEL control). */

.zw-reveal-tab {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 20;
  padding: 0.55rem 0.85rem;
  background: var(--iron);
  color: var(--paper);
  border: 1px solid var(--line);
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.7rem;
  cursor: pointer;
}

.zw-reveal-tab[hidden] {
  display: none;
}

/* ---------------------------------------------------------------------------
   mobile — panel becomes a fixed bottom sheet, collapsible; stage keeps the
   sign visible. Usable at 320px, no horizontal scroll (§9 gate 5). */

@media (max-width: 640px) {

  .zw-panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 15;
    max-height: 82vh;
    display: flex;
    flex-direction: column;
  }

  .zw-panel-head {
    min-height: 3.25rem;
  }

  .zw-rig {
    padding-bottom: 3.25rem;
  }

  .zw-panel-body {
    max-height: 55vh;
    grid-template-columns: 1fr;
  }

  .zw-panel[data-sheet="collapsed"] .zw-panel-body,
  .zw-panel[data-sheet="collapsed"] .zw-plate-footer {
    display: none;
  }
}

/* =============================================================================
   STUDIO PAGE — site/index.html only. Everything above this line is bench
   territory (§7: keep bench styles, namespace new sections, tokens
   unchanged). "zws-" prefix throughout so nothing here can ever collide
   with a "zw-" bench class, even though the two pages never load together.
   Binding source: docs/KONTRAKT.md. KONTRAKT §2 CUT: no transition and no
   cubic-bezier appears anywhere in this section — the page has exactly one
   moving part, the strike, and it lives on a canvas rather than in CSS. */

/* page-level type override: bench's global `body` rule (§ base, above) sets
   IBM Plex Mono for the bench's own readouts. The studio page's body copy
   is Saira per §4; scoping to body.zws-page leaves the bench's unscoped
   `body` rule untouched, satisfying "do not restructure existing bench
   styles" literally — this is an addition, not an edit to that rule. */

body.zws-page {
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 100, "wght" 400;
}

.zws-wrap {
  max-width: 72rem;
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}

/* ---------------------------------------------------------------------------
   bar — ~64px, hairline bottom border, logotype left, nav right. Static (no
   position: sticky). flex-wrap is the ≥320px overflow guard: at widths too
   narrow for brand + nav on one line, nav drops to its own row instead of
   forcing a scrollbar.

   position: relative is structural, not decorative: it is the containing
   block for .zws-arc AND the terminus strike.js walks the offsetParent chain
   to. Removing it puts the field in a different coordinate space than the
   canvas it draws into. */

.zws-bar {
  position: relative;
  border-bottom: 1px solid var(--line);
}

/* strike overlay — strike.js appends this canvas; it exists only when the
   engine does. KONTRAKT §2 STILL: at rest it is cleared, so the page with the
   overlay and the page without it are the same poster. The bar's bottom
   hairline is the ground plane a strike earths into, which is why the canvas
   stops at the padding box and not at the border. */

.zws-arc {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.zws-bar-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  min-height: 64px;
  padding-block: 0.75rem;
}

.zws-brand {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  min-width: 0;
}

/* logotype — the sole bearer of the left side since the ray fan was struck
   (KONTRAKT §5). Sized up from 1.45rem for that reason and no other: the
   answer to a bar that lost 84px of motif is the mark carrying its own
   weight, not a replacement motif. The ≤18px cap ceiling it used to answer
   to belonged to the dead covenant and does not survive into KONTRAKT.

   Two spans, not word-spacing: strike.js needs each electrode's own
   offsetLeft/offsetWidth. .zws-w2's margin is the ≈0.45em clearance channel.

   skewX reads --lean (KONTRAKT §2 LEAN: one standing angle, one declaration
   site). font-weight/font-stretch duplicate the variation settings one level
   up because canvas ctx.font can express those and cannot express
   font-variation-settings at all — strike.js rasterises from the computed
   values, so the two declarations must always be edited together. */

.zws-logotype {
  display: inline-block;
  font-family: "Anybody", sans-serif;
  font-variation-settings: "wdth" 140, "wght" 900;
  font-weight: 900;
  font-stretch: 140%;
  font-size: 1.65rem;
  line-height: 1;
  color: var(--paper);
  text-transform: uppercase;
  letter-spacing: -0.01em;
  white-space: nowrap;
  transform: skewX(var(--lean));
  transform-origin: left center;
}

.zws-w {
  display: inline-block;
}

.zws-w2 {
  margin-left: 0.45em;
}

.zws-bl {
  display: inline-block;
  width: 0;
  height: 0;
}

/* the affordance is keyed off the attribute strike.js sets, so it appears
   only once the engine is live and can honour it */

.zws-logotype[role="button"] {
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

.zws-logotype:focus-visible {
  outline: 2px solid var(--paper);
  outline-offset: 4px;
}

/* nav — Saira caps, 13px, +8% tracking. --ash at rest, --paper on
   hover/focus: KONTRAKT §2 STRIKE reserves --volt for the discharge alone —
   one accent, total — so every interactive state on this page, the
   logotype's focus ring included, brightens toward --paper instead. */

.zws-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 13px;
}

.zws-nav a {
  color: var(--ash);
  text-decoration: none;
}

.zws-nav a:hover,
.zws-nav a:focus-visible {
  color: var(--paper);
}

.zws-nav a:focus-visible {
  outline: 2px solid var(--paper);
  outline-offset: 2px;
}

.zws-nav a + a::before {
  content: "· ";
  color: var(--ash);
}

/* the ray fan was removed here under KONTRAKT §5 — its duty passed to
   strike.js and to that engine's engraved reduced-motion plate. Nothing
   replaces it: the bar's own geometry (mark, clearance channel, hairline
   ground plane) is the composition. */

@media (max-width: 480px) {
  .zws-logotype {
    font-size: 1.45rem;
  }

  .zws-nav {
    font-size: 11px;
  }
}

/* ---------------------------------------------------------------------------
   hero — statement, not signage. H1 is the only place a company name would
   compete for attention, and it isn't the company name. */

.zws-hero {
  padding-block: clamp(4rem, 12vw, 7rem) clamp(3rem, 8vw, 5rem);
}

.zws-hero h1 {
  margin: 0 0 1.1rem;
  max-width: 18ch;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 125, "wght" 700;
  font-size: clamp(1.8rem, 1.35rem + 2vw, 2.6rem);
  line-height: 1.15;
  color: var(--paper);
}

.zws-hero p {
  margin: 0;
  max-width: 42ch;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 100, "wght" 400;
  font-size: clamp(1rem, 0.92rem + 0.4vw, 1.15rem);
  line-height: 1.6;
  color: var(--ash);
}

/* section title — shared by WERKE — WORKS and STUDIO. KONTRAKT §1: German is
   seasoning, so the one bilingual header left is a proper noun and its gloss.
   A header that was only German (GRUND) is now only English. */

.zws-section-title {
  margin: 0 0 2rem;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.85rem;
  color: var(--ash);
}

.zws-works,
.zws-studio {
  padding-block: clamp(3rem, 8vw, 5rem);
  border-top: 1px solid var(--line);
}

/* ---------------------------------------------------------------------------
   clamp plates — KONTRAKT §7. Every WERK is the numbered plate of a limited
   works machine: plate geometry, the serial dominant, engraved spec lines,
   the §6 figure as the maker's mark.

   The shared-hairline sheet (1px gap on a --line grid background) is gone:
   a cut corner exposes whatever is behind the plate, and behind it was
   --line, which would fill the chamfer solid instead of opening it to the
   ground. Three discrete plates, each carrying its own hairline, gap is
   carbon. A 1px gap is not available as a substitute — plate hairline plus
   carbon plus plate hairline is a 3px seam.

   Track floor is 266px, not 240. auto-fit parks a plate exactly on the floor
   at each column-count transition, so the floor is the narrowest plate that
   still holds every engraved value on one line — otherwise every transition
   carries a dead band beneath it where one value breaks in two. The widest
   pair is STATUS / IN THE WORKS at 265.353px of plate: label 44.047 + column
   gap 14.4 + value 104.906 + the figure's 60px reserve + 40px gutters + 2px
   of hairline. Measured off nowrap metrics, not chosen; re-measure it if the
   spec's type, its gutters, or the figure's reserve move, and note that the
   longest *value* is what sets it — a longer title costs nothing here.

   §1: racing is a register, not decoration. No flag, no chevron, no speed
   line appears in this block. The plate, the number and the lean carry it. */

.zws-placard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(266px, 1fr));
  gap: 1rem;
}

/* Chamfer, one corner only: BOTTOM right, running up-and-right, truly
   parallel to the Funkenstrecke's channel rather than mirrored from it
   (rise 48 / run 20; 67.4° from horizontal — KONTRAKT §7, founder ruling
   2026-07-27). A top-right or bottom-left cut is that channel's mirror, not
   that channel; the corner moved, the angle did not. Machined, never
   rounded.

   --cut-rise is not a free number: it is the plate's foot strip measured off
   the bottom rule — bottom hairline 1px + foot padding 1.4rem + one engraved
   spec line (0.62rem × 1.4). The apex therefore lands exactly on the top of
   the last spec line's box, and the cut is as tall as the strip that line
   sits in. One spec line, not the whole dl: WERK 001 carries two rows and a
   reserved plate carries one, and the cut has to be identical across the
   row. Change the foot padding or the spec's type and --cut-rise moves with
   them. --cut-run follows from the mark's ratio. Both are consumed twice
   below, so clip and hairline cannot drift apart.

   The three straight edges are a real 1px border, sliced flush by the clip.
   The cut's own hairline cannot be a border (the clip cuts through it), so
   it is one background layer sized to the chamfer box at bottom right:
   `to bottom right` puts that box's bottom-left/top-right diagonal — the cut
   line itself — at the 50% stop, and the 1px band is laid inboard of it. */

.zws-placard {
  --cut-rise: calc(1px + 1.4rem + 0.868rem);
  --cut-run:  calc(var(--cut-rise) * 20 / 48);

  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 13rem;
  padding: 1.25rem 1.25rem 1.4rem;
  border: 1px solid var(--line);
  background-color: var(--iron);
  background-image: linear-gradient(to bottom right,
                                    transparent calc(50% - 1px),
                                    var(--line) calc(50% - 1px),
                                    var(--line) 50%,
                                    transparent 50%);
  background-origin: border-box;
  background-position: bottom right;
  background-repeat: no-repeat;
  background-size: var(--cut-run) var(--cut-rise);
  clip-path: polygon(0 0, 100% 0,
                     100% calc(100% - var(--cut-rise)),
                     calc(100% - var(--cut-run)) 100%,
                     0 100%);
}

/* the class stamped above the serial, the way a plate carries its type over
   its number. Mono, because this line is machine-set and the title is not. */

.zws-placard-werk {
  margin: 0;
  font-family: "Martian Mono", ui-monospace, monospace;
  font-size: 0.6rem;
  letter-spacing: 0.22em;
  color: var(--ash);
}

/* the serial is the loudest element on the page after the logotype, and the
   only consumer of --rosso anywhere (§7). Anybody at --lean is the mark's own
   face at the one standing angle: the speed is structural, never drawn.
   transform-origin is the baseline corner, not the centre the logotype uses,
   because here the numeral hangs off a left rail — skewing about the foot
   keeps that rail straight and leans only the head.

   font-weight duplicates the wght axis so a numeral rendered in the fallback
   face is still heavy; a spindly serial would read as body copy. */

.zws-placard-serial {
  margin: 0.15rem 0 0.85rem;
  font-family: "Anybody", sans-serif;
  font-variation-settings: "wdth" 112, "wght" 800;
  font-weight: 800;
  font-size: clamp(2.9rem, 2.2rem + 3vw, 3.9rem);
  line-height: 0.86;
  letter-spacing: -0.02em;
  color: var(--rosso);
  transform: skewX(var(--lean));
  transform-origin: left bottom;
}

.zws-placard-title {
  margin: 0 0 0.9rem;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 87, "wght" 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.9rem;
  color: var(--paper);
}

/* engraved spec — label/value pairs, mono, at plate scale. margin-top: auto
   drops the block to the foot so every plate's spec sits on one line however
   its title wrapped; padding-right is the mark's clearance, since the figure
   is absolutely positioned and cannot push text out of its way. */

.zws-placard-spec {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 0.9rem;
  margin: auto 0 0;
  padding-right: 3.75rem;
  font-family: "Martian Mono", ui-monospace, monospace;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  line-height: 1.4;
  text-transform: uppercase;
}

.zws-placard-spec dt {
  color: var(--ash);
}

.zws-placard-spec dd {
  margin: 0;
  color: var(--paper);
}

/* a work that has not shipped is stamped but unpainted: its serial drops to
   --ash. This is the dim state, and it is a machine convention rather than a
   grey-out — the paint goes on when the work does. It also keeps --rosso at
   the size where its contrast on iron holds, which is the serial and nowhere
   smaller. */

.zws-placard--dim .zws-placard-serial,
.zws-placard--dim .zws-placard-title,
.zws-placard--dim .zws-placard-spec dd {
  color: var(--ash);
}

/* ---------------------------------------------------------------------------
   Signatur — KONTRAKT §6, worn as §7's maker's mark. signatur.js emits path
   geometry and class names only; every stroke value is declared here, the way
   the struck ray fan's was. Engraved, never lit: --ash held well under full
   opacity reads as a scratch in the iron. Neither --volt nor --rosso appears
   in a signature — volt is the strike's and a signature is only its
   afterimage; rosso is the plate's paint, and a mark is stamped, not painted.

   Foot of the plate, diagonally opposite the serial: the number opens the
   plate at the head, the figure closes it at the foot. The chamfer cuts that
   same corner, so `right` is a clearance and not just a gutter — it has to
   stay wider than the cut's inset at the figure's own baseline, which the
   1.25rem gutter is by a factor of three at the current --cut-rise. Every
   path point is clamped inside the 60×40 viewBox, so overflow: visible costs
   half a stroke, not a twig into the cut. Width and height override the SVG's
   own attributes to stamp size — the viewBox is unchanged and the strokes are
   non-scaling, so the figure is the same figure, smaller. */

.zws-signatur {
  position: absolute;
  right: 1.25rem;
  bottom: 1.4rem;
  width: 3rem;
  height: 2rem;
  overflow: visible;
  pointer-events: none;
}

.zws-signatur path {
  fill: none;
  stroke: var(--ash);
  stroke-width: 1px;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.zws-signatur-trunk  { opacity: 0.42; }
.zws-signatur-branch { opacity: 0.26; }
.zws-signatur-twig   { opacity: 0.15; }

/* a dim placard's figure sits at the same remove as its title */

.zws-placard--dim .zws-signatur {
  opacity: 0.55;
}

/* ---------------------------------------------------------------------------
   studio prose — two short paragraphs, Saira body. */

.zws-studio p {
  margin: 0 0 1.25rem;
  max-width: 58ch;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--paper);
}

.zws-studio p:last-child {
  margin-bottom: 0;
}

/* ---------------------------------------------------------------------------
   footer plate — Martian Mono, --ash, small. Left = ZW plate id and where the
   studio stands, right = CONTACT as inert text (no address published in v0). */

.zws-footer {
  border-top: 1px solid var(--line);
}

.zws-footer-inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  padding-block: 1.5rem;
}

.zws-footer p {
  margin: 0;
  font-family: "Martian Mono", ui-monospace, monospace;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--ash);
}
