/* =========================================================================
   Buzzbelt — marketing site

   The token block below is the site's copy of `ui/theme/Tokens.kt`. The same
   rule applies here as in the app: a hardcoded colour, radius or spacing
   value outside :root is a bug, not a shortcut. If a value changes in
   Tokens.kt it changes here, and the two stay one system.

   The app's UI rules that survive the jump to a web page:

   - Rule 4, no colour-only state. Every distinction on this page — the two
     kinds of range in the diagram, the limits versus the features, the
     primary versus the secondary button — is carried by shape, weight,
     border pattern or position as well as by hue. Load the page in
     greyscale and nothing should become ambiguous.
   - Rule 5, tokens only. See above.
   - Rule 7, no protocol vocabulary. No "node", "beacon", "relay", "mesh",
     "hop" or "packet" in anything a visitor reads. Distance is people.

   No JavaScript is loaded, so there is nothing here that depends on it.
   ========================================================================= */

/* ---- Tokens ------------------------------------------------------------ */

:root {
  color-scheme: light dark;

  /* Map ink on cool map paper. Not cream — cream reads as stationery,
     this reads as a chart. Lifted from Palette in Tokens.kt. */
  --paper: #e6eae1;
  --paper-raised: #f2f5ee;
  --ink: #101a12;
  --ink-muted: #3a453c;
  --outline: #b9c2b6;

  /* Control-flag orange. Attention only — never decorative, which on this
     page means the honest limits and nothing else. */
  --alert: #8b2a08;
  --on-alert: #e6eae1;

  /* Signal blue. Reserved for the signed-announcement material, the same
     way the app reserves it. */
  --announce: #143c6b;
  --on-announce: #e6eae1;

  /* Blaze green, for the things that are confirmed or included. */
  --confirmed: #2d561b;

  /* The inverted emphasis block — one big card that has to carry more weight
     than a bordered panel. In light it is an ink slab with paper type. In dark
     it deliberately does NOT become a paper slab: the dark theme exists
     because a tent at night and a phone at 20% battery both want black
     pixels, and a near-white block that size is exactly what it is avoiding.
     There it becomes a raised surface with a bright outline instead, which
     reads as emphatic without lighting up the room. */
  --emphasis-surface: var(--ink);
  --emphasis-ink: var(--paper);
  --emphasis-border: var(--ink);

  /* Four-point spacing scale. Nothing in between, so the rhythm survives
     future edits. */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 0.75rem;
  --space-lg: 1rem;
  --space-xl: 1.5rem;
  --space-2xl: 2rem;
  --space-3xl: 3rem;
  --space-4xl: 4.5rem;
  --space-5xl: 7rem;

  --radius-small: 4px;
  --radius-medium: 10px;
  --radius-large: 18px;

  --stroke-hairline: 1px;
  --stroke-regular: 2px;
  --stroke-heavy: 3px;
  --stroke-blaze: 6px;

  --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* The tabular voice: things read as data — counts, times, step numbers. */
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
    "Liberation Mono", monospace;

  /* Fluid type. The floor of each clamp is the size at 320px and the ceiling
     the size at 1200px, so nothing is set in viewport units alone — a page
     that shrinks its body text on a phone is unreadable in exactly the
     conditions this product is used in. */
  --type-display: clamp(2.25rem, 1.55rem + 3.5vw, 4rem);
  --type-h2: clamp(1.75rem, 1.35rem + 2vw, 2.75rem);
  --type-h3: clamp(1.15rem, 1.05rem + 0.5vw, 1.375rem);
  --type-lead: clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
  --type-body: 1.0625rem;
  --type-label: 0.8125rem;

  --measure: 34rem;      /* prose column: roughly 70 characters */
  --page-max: 72rem;
  --page-gutter: clamp(1.25rem, 5vw, 3rem);
}

/* A genuinely dark instrument panel, not a grey inversion: a tent at night
   and a phone at 20% battery both want black pixels. The accents are lifted
   rather than reused, so they hold their contrast against near-black. */
@media (prefers-color-scheme: dark) {
  :root {
    --paper: #080c09;
    --paper-raised: #141a15;
    --ink: #e2e8de;
    --ink-muted: #a8b3a6;
    --outline: #39433a;
    --alert: #ff9a6b;
    --on-alert: #080c09;
    --announce: #8fbef2;
    --on-announce: #080c09;
    --confirmed: #9bd07a;

    --emphasis-surface: var(--paper-raised);
    --emphasis-ink: var(--ink);
    --emphasis-border: var(--ink);
  }
}

/* ---- Reset ------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* Honour the setting rather than animating regardless. Vestibular triggers
   are not a preference. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--type-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
p,
ul,
ol,
dl,
figure,
blockquote {
  margin: 0;
}

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

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

a {
  color: inherit;
}

/* ---- Focus, throughout -------------------------------------------------
   One visible treatment for every focusable thing on the page. A focus ring
   that only clears contrast on some backgrounds is the same bug as a state
   carried by colour: it works until the one moment somebody needs it. */

:focus-visible {
  outline: var(--stroke-heavy) solid var(--ink);
  outline-offset: 3px;
  border-radius: var(--radius-small);
}

.button:focus-visible,
.skip-link:focus-visible {
  outline-offset: 4px;
}

/* ---- Skip link --------------------------------------------------------- */

.skip-link {
  position: absolute;
  left: var(--space-lg);
  top: var(--space-lg);
  z-index: 10;
  padding: var(--space-md) var(--space-lg);
  background: var(--ink);
  color: var(--paper);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--radius-small);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: translateY(0);
}

/* ---- Layout ------------------------------------------------------------ */

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

section {
  padding-block: var(--space-4xl);
}

/* Alternating surfaces give the page a rhythm you can feel at a glance
   without reading it — the "25% test" applied to a scroll. */
.band-raised {
  background: var(--paper-raised);
  border-block: var(--stroke-hairline) solid var(--outline);
}

.measure {
  max-width: var(--measure);
}

.lead {
  font-size: var(--type-lead);
  line-height: 1.5;
  color: var(--ink-muted);
}

/* ---- Type -------------------------------------------------------------- */

h1,
h2,
h3 {
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 700;
  text-wrap: balance;
}

h1 {
  font-size: var(--type-display);
}

h2 {
  font-size: var(--type-h2);
}

h3 {
  font-size: var(--type-h3);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 600;
}

p {
  text-wrap: pretty;
}

/* The eyebrow: a section's number and name, in the tabular voice. It is the
   only place on the page that shouts, and it shouts quietly. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
  font-family: var(--font-mono);
  font-size: var(--type-label);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* The blaze: the signature mark, borrowed from the trail-blaze stripe the
   app draws down the edge of a verified announcement. */
.eyebrow::before {
  content: "";
  flex: none;
  width: var(--stroke-blaze);
  height: 1.1em;
  background: var(--ink);
  border-radius: 1px;
}

.section-intro {
  max-width: var(--measure);
  margin-bottom: var(--space-3xl);
}

.section-intro > * + * {
  margin-top: var(--space-lg);
}

/* ---- Header ------------------------------------------------------------ */

.site-header {
  border-bottom: var(--stroke-hairline) solid var(--outline);
  background: var(--paper);
}

.site-header .shell {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  padding-block: var(--space-lg);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
  /* Same 48px floor as everything else tappable, rather than the height the
     text happens to give it. */
  min-height: 48px;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-decoration: none;
}

.wordmark svg {
  display: block;
  flex: none;
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  /* 48px minimum touch target, one-handed and outdoors. The horizontal
     padding is what makes the target, so it is not reduced on narrow
     screens. */
  min-height: 48px;
  padding-inline: var(--space-md);
  color: var(--ink-muted);
  font-size: 0.9375rem;
  text-decoration: none;
  border-radius: var(--radius-small);
}

.site-nav a:hover {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* ---- Buttons ----------------------------------------------------------- */

.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
}

.button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
  min-height: 56px;
  padding: var(--space-md) var(--space-xl);
  border: var(--stroke-regular) solid var(--ink);
  border-radius: var(--radius-medium);
  background: var(--ink);
  color: var(--paper);
  font-family: inherit;
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
}

/* Primary and secondary differ in fill, not in hue, so the hierarchy holds
   in greyscale and at a quarter of this size. */
.button-secondary {
  background: transparent;
  color: var(--ink);
}

.button:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.button svg {
  flex: none;
}

.button-note {
  margin-top: var(--space-lg);
  font-size: 0.9375rem;
  color: var(--ink-muted);
}

/* ---- Hero -------------------------------------------------------------- */

.hero {
  padding-block: clamp(var(--space-3xl), 8vw, var(--space-5xl));
}

.hero h1 {
  max-width: 18ch;
}

.hero .lead {
  max-width: var(--measure);
  margin-top: var(--space-xl);
}

.hero .button-row {
  margin-top: var(--space-3xl);
}

/* The three facts that decide whether a leader reads any further. Set as
   data, in the tabular voice, because that is what they are. */
.hero-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-xl);
  margin-top: var(--space-3xl);
  padding-top: var(--space-xl);
  border-top: var(--stroke-hairline) solid var(--outline);
  font-family: var(--font-mono);
  font-size: var(--type-label);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.hero-facts li {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.hero-facts li::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  background: var(--ink-muted);
  transform: rotate(45deg);
}

/* ---- The problem ------------------------------------------------------- */

.problem-body {
  display: grid;
  gap: var(--space-3xl);
  grid-template-columns: 1fr;
}

@media (min-width: 56rem) {
  .problem-body {
    grid-template-columns: 1.05fr 0.95fr;
    gap: var(--space-4xl);
  }
}

.problem-body p + p {
  margin-top: var(--space-lg);
}

/* The list of things that do not help, each struck through by its own rule
   rather than by a colour. */
.dead-ends {
  align-self: start;
  padding: var(--space-xl);
  border: var(--stroke-hairline) solid var(--outline);
  border-radius: var(--radius-large);
  background: var(--paper);
}

.dead-ends h3 {
  margin-bottom: var(--space-lg);
}

.dead-ends li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-md);
  padding-block: var(--space-md);
  border-top: var(--stroke-hairline) solid var(--outline);
}

.dead-ends li:first-of-type {
  border-top: 0;
}

.dead-ends li::before {
  content: "";
  width: 18px;
  height: var(--stroke-heavy);
  margin-top: 0.7em;
  background: var(--ink-muted);
}

.dead-ends strong {
  font-weight: 600;
}

.dead-ends span {
  color: var(--ink-muted);
}

/* ---- How it works ------------------------------------------------------ */

.diagram {
  margin: 0;
}

.diagram-frame {
  padding: var(--space-xl) var(--space-lg);
  border: var(--stroke-hairline) solid var(--outline);
  border-radius: var(--radius-large);
  background: var(--paper);
}

/* Capped, and centred once capped. Type inside an SVG scales with the box, so
   the labels are sized as a fraction of the viewBox to stay legible on a
   320px screen — which means the box itself has to stop growing, or those
   same labels become billboards on a wide monitor. */
.diagram svg {
  display: block;
  width: 100%;
  max-width: 640px;
  height: auto;
  margin-inline: auto;
}

/* These two have to be CSS rules rather than presentation attributes on the
   SVG elements: no browser resolves var() inside a presentation attribute,
   so fill="var(--paper)" silently renders as black. */
.diagram-label {
  font-family: var(--font-mono);
}

.diagram-monogram-inverse {
  fill: var(--paper);
}

/* Everything the diagram means is written out here, in the page, for
   everyone — not tucked into a <desc> that only a screen reader reaches. */
.diagram-steps {
  counter-reset: diagram-step;
  display: grid;
  gap: var(--space-xl);
  margin-top: var(--space-3xl);
}

@media (min-width: 56rem) {
  .diagram-steps {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-2xl);
  }
}

.diagram-steps li {
  counter-increment: diagram-step;
  padding-top: var(--space-lg);
  border-top: var(--stroke-regular) solid var(--ink);
}

.diagram-steps li::before {
  content: counter(diagram-step, decimal-leading-zero);
  display: block;
  margin-bottom: var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--type-label);
  font-weight: 500;
  letter-spacing: 0.12em;
  color: var(--ink-muted);
}

.diagram-steps h3 {
  margin-bottom: var(--space-sm);
}

.diagram-steps p {
  color: var(--ink-muted);
}

/* The caveat that belongs with the diagram rather than three sections
   later: what happens when there is nobody in the middle. */
.diagram-caveat {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-lg);
  align-items: start;
  max-width: 44rem;
  margin-top: var(--space-3xl);
  padding: var(--space-xl);
  border: var(--stroke-regular) solid var(--ink);
  border-radius: var(--radius-medium);
}

.diagram-caveat::before {
  content: "";
  align-self: stretch;
  width: var(--stroke-blaze);
  background: var(--ink);
  border-radius: 1px;
}

/* ---- Features ---------------------------------------------------------- */

.feature-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}

@media (min-width: 40rem) {
  .feature-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 64rem) {
  .feature-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.feature {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-xl);
  border: var(--stroke-hairline) solid var(--outline);
  border-radius: var(--radius-large);
  background: var(--paper);
}

.feature-mark {
  display: block;
  margin-bottom: var(--space-sm);
  color: var(--ink);
}

.feature p {
  color: var(--ink-muted);
}

/* What the app will actually put on screen, quoted. It is set apart as
   something the phone says rather than something the page claims. */
.feature-quote {
  margin-top: auto;
  padding-top: var(--space-lg);
  border-top: var(--stroke-hairline) solid var(--outline);
  font-family: var(--font-mono);
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-muted);
}

/* ---- Setup ------------------------------------------------------------- */

.setup-steps {
  counter-reset: setup-step;
  display: grid;
  gap: 0;
  max-width: 46rem;
}

.setup-steps li {
  counter-increment: setup-step;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-lg) var(--space-xl);
  padding-block: var(--space-xl);
  border-top: var(--stroke-hairline) solid var(--outline);
}

.setup-steps li:first-child {
  border-top: 0;
  padding-top: 0;
}

.setup-steps li::before {
  content: counter(setup-step);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: var(--stroke-regular) solid var(--ink);
  border-radius: var(--radius-medium);
  font-family: var(--font-mono);
  font-size: 1.125rem;
  font-weight: 500;
  line-height: 1;
}

.setup-steps h3 {
  margin-bottom: var(--space-sm);
}

.setup-steps p {
  color: var(--ink-muted);
}

.setup-steps .step-body {
  min-width: 0;
}

/* The claim the section is built around, sized to be read across a room —
   and immediately qualified, because an unqualified timing claim is the
   kind of confident vagueness this product does not trade in. */
.setup-claim {
  max-width: 46rem;
  margin-top: var(--space-3xl);
  padding: var(--space-2xl);
  border: var(--stroke-regular) solid var(--emphasis-border);
  border-radius: var(--radius-large);
  background: var(--emphasis-surface);
  color: var(--emphasis-ink);
}

.setup-claim strong {
  display: block;
  font-size: var(--type-h2);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.setup-claim p {
  margin-top: var(--space-lg);
  /* Paper at 80% would fail contrast against ink; the full token does not,
     so the softening is done with size instead. */
  font-size: 0.9375rem;
}

/* ---- Honest limits -----------------------------------------------------
   Deliberately the loudest section on the page after the hero. It is in the
   normal flow, at full section weight, with the only use of the alert
   colour anywhere on the site. A leader is making a safety-adjacent
   decision and will trust a precise limit more than a confident vagueness —
   so this is not a disclaimer, it is a feature of the product. */

.limits {
  background: var(--paper-raised);
  border-block: var(--stroke-heavy) solid var(--alert);
}

.limits .eyebrow {
  color: var(--alert);
}

.limits .eyebrow::before {
  background: var(--alert);
}

.limit-list {
  display: grid;
  gap: var(--space-xl);
}

@media (min-width: 52rem) {
  .limit-list {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-2xl);
  }
}

.limit {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-lg);
  align-items: start;
  padding: var(--space-xl);
  border: var(--stroke-regular) solid var(--alert);
  border-radius: var(--radius-medium);
  background: var(--paper);
}

/* The blaze again, in the alert colour — the same mark the app draws down
   the edge of something that must not be skimmed. Shape as well as hue, so
   it survives greyscale. */
.limit::before {
  content: "";
  align-self: stretch;
  width: var(--stroke-blaze);
  background: var(--alert);
  border-radius: 1px;
}

.limit h3 {
  margin-bottom: var(--space-sm);
}

.limit p + p {
  margin-top: var(--space-md);
}

/* The one limit that is not a nuance. It spans the grid and is set heavier
   than the others, because a leader skimming this section at speed must not
   be able to miss it. */
.limit-gravest {
  background: var(--alert);
  border-color: var(--alert);
  color: var(--on-alert);
}

@media (min-width: 52rem) {
  .limit-gravest {
    grid-column: 1 / -1;
  }
}

.limit-gravest::before {
  background: var(--on-alert);
}

.limit-gravest h3 {
  font-size: clamp(1.375rem, 1.15rem + 1vw, 1.875rem);
}

/* ---- FAQ ---------------------------------------------------------------
   <details> and <summary>, so it opens and closes with no JavaScript and
   keeps native keyboard and screen-reader behaviour. */

.faq-list {
  max-width: 48rem;
  border-top: var(--stroke-hairline) solid var(--outline);
}

.faq-item {
  border-bottom: var(--stroke-hairline) solid var(--outline);
}

.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  min-height: 48px;
  padding-block: var(--space-lg);
  font-size: var(--type-h3);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  cursor: pointer;
  list-style: none;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

/* A plus that becomes a minus. Drawn from two bars rather than a glyph, so
   it is legible at any size and needs no icon font. */
.faq-marker {
  position: relative;
  flex: none;
  width: 20px;
  height: 20px;
}

.faq-marker::before,
.faq-marker::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: var(--stroke-heavy);
  margin-top: calc(var(--stroke-heavy) / -2);
  background: var(--ink);
}

.faq-marker::after {
  transform: rotate(90deg);
  transition: transform 150ms ease;
}

.faq-item[open] .faq-marker::after {
  transform: rotate(0deg);
}

.faq-answer {
  padding-bottom: var(--space-xl);
  max-width: var(--measure);
  color: var(--ink-muted);
}

.faq-answer p + p {
  margin-top: var(--space-lg);
}

/* ---- Footer ------------------------------------------------------------ */

.site-footer {
  padding-block: var(--space-3xl);
  border-top: var(--stroke-hairline) solid var(--outline);
  background: var(--paper-raised);
}

.footer-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xl);
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  padding-inline: var(--space-md);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--radius-small);
}

.footer-links a:hover {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.footer-note {
  max-width: 30rem;
  font-size: 0.9375rem;
  color: var(--ink-muted);
}

.footer-note p + p {
  margin-top: var(--space-md);
}

/* ---- Legal pages ------------------------------------------------------- */

.legal {
  padding-block: var(--space-4xl);
}

.legal-body {
  max-width: 42rem;
  /* German compound nouns run long — Auftragsverarbeitungsvertrags is 29
     characters with no hyphen anywhere in it — and on a narrow phone an
     unbroken one is wide enough to push the whole page into horizontal
     scroll, which no other content on this site ever needs. hyphens:auto
     reads the page's lang (de on datenschutz.html, en on the rest, where
     it's a no-op on already-short words) and breaks at real syllable
     boundaries; overflow-wrap is the fallback for the word it can't place. */
  hyphens: auto;
  overflow-wrap: break-word;
}

/* terms.html reuses .diagram-caveat (an unreviewed-draft notice) and
   .limit/.limit-gravest (the emergency clause) straight from index.html's
   component set rather than inventing page-specific boxes — same rule as
   everywhere else on the site, one system, not two. .diagram-caveat carries
   its own top margin already; .limit does not, because on index.html that
   spacing comes from .limit-list's grid gap, which a standalone legal clause
   has no parent grid to inherit. */
.legal-body .limit {
  margin-top: var(--space-xl);
}

.legal-body h2 {
  margin-top: var(--space-3xl);
  font-size: clamp(1.375rem, 1.2rem + 0.8vw, 1.75rem);
}

.legal-body h3 {
  margin-top: var(--space-2xl);
}

.legal-body p,
.legal-body ul {
  margin-top: var(--space-lg);
}

.legal-body ul {
  display: grid;
  gap: var(--space-md);
  padding-left: var(--space-xl);
  list-style: disc;
}

.legal-body li {
  padding-left: var(--space-xs);
}

.legal-body a {
  color: var(--ink);
  text-underline-offset: 3px;
}

.legal-updated {
  margin-top: var(--space-xl);
  font-family: var(--font-mono);
  font-size: var(--type-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Marks a value the operator has to fill in before this site is published.
   Deliberately conspicuous: an Impressum with a placeholder still in it is
   an Impressum that is missing, and §5 DDG makes that a fineable offence. */
.placeholder {
  padding: 0.1em 0.35em;
  border: var(--stroke-hairline) dashed var(--alert);
  border-radius: var(--radius-small);
  font-family: var(--font-mono);
  font-size: 0.9em;
  color: var(--alert);
}

/* ---- Utilities --------------------------------------------------------- */

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

/* ---- Print -------------------------------------------------------------
   A leader printing this is printing the setup steps and the limits to put
   in a folder, so those survive and the chrome does not. */

@media print {
  .site-header,
  .site-nav,
  .hero .button-row,
  .skip-link {
    display: none;
  }

  body {
    background: #fff;
    color: #000;
    font-size: 11pt;
  }

  section {
    padding-block: var(--space-xl);
    break-inside: avoid;
  }

  .limit,
  .setup-steps li,
  .diagram {
    break-inside: avoid;
  }

  /* A closed <details> is not opened by CSS. Browsers hide its contents through
     an internal slot, so `display: block` on the answer looks like it forces
     the FAQ to print and silently does nothing. The section that matters on
     paper is the setup steps and the limits, both of which print in full, so
     the honest thing is to not pretend otherwise here. */

  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    word-break: break-all;
  }
}
