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

/* ---------------------------------------------------------------------------
   §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 — exact tokens, do not alter. 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 {
  --carbon: #0A0A0B;
  --iron: #141416;
  --line: #2A2A2E;
  --line-bright: #3A3A40;
  --paper: #F2F0E6;
  --ash: #8A8A90;
  --volt: #E8FF00;

  --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. Covenant §2.3 quantized timing 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 (§2.6 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 —
   quantized per covenant §2.3. Verbatim rule per contract. 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 — covenant §2.5, one accent. Nowhere else in the panel 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/DESIGN.md §6 (composition, exact copy), §5 (rays),
   §8 (quality gates). No transitions anywhere in this section except the
   flare's instant class toggle below — covenant §2.3/gate 4: instant
   attack, hard cutoff, no ease, no cubic-bezier. */

/* 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 — §6.1: ~64px, hairline bottom border, logotype+rays left, nav right.
   Static (no position: sticky) for v0, per spec verbatim. flex-wrap is the
   ≥320px overflow guard (gate 5): at widths too narrow for brand + nav on
   one line, nav drops to its own row instead of forcing a scrollbar. */

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

.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 — §4: two words, word-spacing carries the ≈0.45em gap (simpler
   and more robust than splitting into two spans). wdth 140/wght 900,
   skewX(-8deg) verbatim. font-size is the cap-height control: tuned so the
   rendered cap height stays well under the ≤18px ceiling (gate 2) — the
   name is a mark, not a mural. */

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

/* nav — §4/§6.1: Saira caps, 13px, +8% tracking. --ash at rest, --paper on
   hover/focus: covenant §2.5 reserves --volt for the ray motif alone, so
   interactive nav states brighten toward --paper, never toward the accent. */

.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);
}

/* ---------------------------------------------------------------------------
   §5 ray motif — the static engraving lives entirely in index.html markup
   (9 <g class="ray-line"> groups, each a horizontal <line> rotated to its
   fixed angle). This block only paints it; rays.js only ever toggles
   .is-flare on one group at a time and rewrites that one group's transform
   for the optional 6px extension. No JS builds or removes a single line —
   the poster test (no-JS) already shows the finished fan. */

.zws-rayfan {
  overflow: visible;
  flex: 0 0 auto;
}

.ray-line line {
  stroke: var(--line-bright);
  stroke-width: 1px;
  stroke-linecap: square;
  vector-effect: non-scaling-stroke;
  opacity: 0.8;
}

.ray-line.ray--volt line {
  stroke: var(--volt);
  opacity: 0.55;
}

/* hover — covenant: opacity step only, no movement, no accent bleed beyond
   the rays themselves. One step up from each ray's own resting opacity. */

.zws-brand:hover .ray-line line,
.zws-brand:focus-within .ray-line line {
  opacity: 1;
}

.zws-brand:hover .ray-line.ray--volt line,
.zws-brand:focus-within .ray-line.ray--volt line {
  opacity: 0.8;
}

/* flare — instant attack, hard cutoff (covenant §2.3): no transition
   property at all, a straight class-driven snap. rays.js adds .is-flare,
   holds ~120ms, removes it; revert is equally instant. Placed after the
   hover rules and scoped through .zws-brand so it wins the cascade even if
   a flare lands while the logotype happens to be hovered. */

.zws-brand .ray-line.is-flare line {
  stroke: var(--volt);
  opacity: 1;
}

/* mobile ≤480px (§5/§8 gate 5): fan trims to the 5 core rays (each already
   ≤40px per the geometry in index.html); the 4 extended rays (>40px) are
   struck entirely rather than scaled, so "5 rays, max 40px" is literally
   true of what's in the DOM, not just visually implied. */

@media (max-width: 480px) {
  .ray-line.ray--ext {
    display: none;
  }

  .zws-rayfan {
    width: 56px;
    height: 60px;
  }

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

/* ---------------------------------------------------------------------------
   hero — §6.2: 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 — GRUND; bilingual
   plate-label flavor per §4, English elsewhere. */

.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);
}

/* ---------------------------------------------------------------------------
   placards — §6.3: iron surface, 1px --line borders, serial top-right in
   Martian Mono. The grid's own 1px gap on a --line background is the
   border: adjoining cards share one hairline instead of doubling it. */

.zws-placard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}

.zws-placard {
  position: relative;
  min-height: 9rem;
  padding: 1.5rem 1.25rem 1.75rem;
  background: var(--iron);
}

.zws-placard-serial {
  position: absolute;
  top: 0.9rem;
  right: 1.1rem;
  margin: 0;
  font-family: "Martian Mono", ui-monospace, monospace;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--ash);
}

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

.zws-placard-desc {
  margin: 0;
  font-family: "Saira", sans-serif;
  font-variation-settings: "wdth" 100, "wght" 400;
  font-size: 0.9rem;
  color: var(--ash);
}

/* placeholder placards (WERK 002/003, §6.3 "no link, --ash") dim the title
   too, so real-but-unlinked (WERK 001) and true placeholders read as
   visibly different states, not the same grey. */

.zws-placard--dim .zws-placard-title {
  color: var(--ash);
}

/* ---------------------------------------------------------------------------
   studio prose — §6.4, two short paragraphs, Saira body per §4. */

.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;
}

.zws-studio em {
  font-style: italic;
}

/* ---------------------------------------------------------------------------
   footer plate — §6.5: Martian Mono, --ash, small. Left = ZW plate id,
   right = KONTAKT — 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);
}
