/* Every colour literal lives in :root or the prefers-color-scheme override below —
   tool/website.test.mjs enforces it. The accent is the site's work blue and the one
   logo.svg repeats (it cannot read this file); the app's seed is this same
   --color-accent (test/theme/duimstok_tokens_test.dart pins it). The amber pair is
   the app's tertiary and appears exactly where the app uses it — the open-questions
   chip. The deep pair is the one dark surface left on the site: the promise strip. */

:root {
  --color-bg: #ffffff;
  --color-fg: #33475b;
  --color-heading: #16324f;
  --color-muted: #5c6b7a;
  --color-accent: #1c6dd0;
  --color-on-accent: #ffffff;
  --color-line: #cfdae4;
  --color-line-soft: #e3eaf1;
  --color-panel: #f4f7fa;
  --color-panel-2: #f8fafc;
  --color-amber: #8a6a16;
  --color-amber-soft: #fdf0d5;
  --color-amber-ink: #4a3b12;
  --color-record: #d1414c;

  /* the one dark surface left on the site */
  --color-deep: #16324f;
  --color-on-deep: #dbe6f0;
  --color-green: #237a33;
  --color-on-green: #ffffff;
  --color-green-bright: #7fd694;

  /* The signature pad is paper in both colour schemes: canvas 2D resolves neither
     currentColor nor a var() against the element, so offerte.js leaves strokeStyle at
     the spec default (opaque black) and the surface has to stay light even on a phone
     in dark mode. --color-signature-ink records that default here, in the one place
     colours live, so the pair can be contrast-checked. Deliberately not overridden in
     the prefers-color-scheme: dark block below. */
  --color-signature-surface: #fafafb;
  --color-signature-ink: #000000;

  --shadow-phone: 0 1px 3px rgba(22, 50, 79, 0.09), 0 18px 44px rgba(22, 50, 79, 0.13);

  --step--2: 0.6875rem;
  --step--1: clamp(0.8rem, 0.78rem + 0.1vw, 0.875rem);
  --step-text: clamp(0.9375rem, 0.9rem + 0.2vw, 1rem);
  --step-0: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  --step-1: clamp(1.15rem, 1.08rem + 0.35vw, 1.375rem);
  --step-2: clamp(1.6rem, 1.35rem + 1.2vw, 2.4rem);
  --step-3: clamp(2.2rem, 1.5rem + 2.6vw, 3.4rem);
  --measure: 34rem;
  --pad: clamp(1.15rem, 0.8rem + 1.9vw, 2.5rem);
}

/* The landing page carries class="brand" on <html> and keeps its white-and-blue
   whatever the visitor's operating system says: it is the one page whose job is to
   show what the product looks like, and half the audience runs a dark phone. Every
   other page here is something you read — the policy, the FAQ, a quote a customer
   opens at night — and those do follow the visitor. */
@media (prefers-color-scheme: dark) {
  :root:not(.brand) {
    --color-bg: #121214;
    --color-fg: #d6dde5;
    --color-heading: #f2f2f7;
    --color-muted: #a3adb8;
    --color-accent: #4ca3ff;
    --color-on-accent: #06121f;
    --color-line: #3a3a3c;
    --color-line-soft: #2c2c2e;
    --color-panel: #1c1c1e;
    --color-panel-2: #161618;
    --color-amber: #e7c173;
    --color-amber-soft: #4a3b12;
    --color-amber-ink: #fdecc8;
    --color-deep: #1c1c1e;
    --color-on-deep: #f2f2f7;
    --color-green: #7fd694;
    --color-on-green: #06210f;
  }
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

/* the tape is fixed at 22px whatever the motion preference — every anchor target
   must clear it, or its eyebrow lands hidden behind the balk */
section[id] {
  scroll-margin-top: 3rem;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
  font-size: var(--step-0);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1,
h2,
h3 {
  margin: 0;
  text-wrap: balance;
  font-weight: 700;
  letter-spacing: -0.03em;
}

p {
  margin: 0;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: 6px;
}

.shell {
  max-width: 72rem;
  margin: 0 auto;
  padding-inline: var(--pad);
}

.eyebrow {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0;
}

/* ── the tape: a measuring tape as the scroll indicator ─────────────────── */

.tape {
  position: fixed;
  inset: 0 0 auto 0;
  height: 22px;
  z-index: 40;
  background: var(--color-accent);
  border-bottom: 1px solid color-mix(in srgb, var(--color-accent) 65%, black);
  overflow: hidden;
}

.tape .ticks {
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(to right, color-mix(in srgb, var(--color-on-accent) 45%, transparent) 0 1px, transparent 1px 100%),
    repeating-linear-gradient(to right, color-mix(in srgb, var(--color-on-accent) 45%, transparent) 0 1px, transparent 1px 100%);
  background-size:
    10% 11px,
    2% 6px;
  background-position:
    bottom left,
    bottom left;
  background-repeat: repeat-x;
}

.tape .fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: color-mix(in srgb, var(--color-on-accent) 22%, transparent);
  border-right: 2px solid var(--color-on-accent);
}

.tape-cm {
  position: absolute;
  inset: 0 0.55rem 0 auto;
  display: flex;
  align-items: center;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--color-on-accent);
}

/* ── the top bar ────────────────────────────────────────────────────────── */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.9rem 1.5rem;
  padding-block: 2.1rem 1.4rem;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 700;
  font-size: 1.2rem;
  letter-spacing: -0.03em;
  color: var(--color-heading);
}

.wordmark svg {
  color: var(--color-accent);
}

.topbar nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 1.35rem;
  font-size: var(--step-text);
}

.topbar nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  text-decoration: none;
  font-weight: 600;
  color: var(--color-muted);
}

.topbar nav a:hover {
  color: var(--color-accent);
}

@media (max-width: 44rem) {
  .topbar nav {
    flex: 1 1 100%;
    min-width: 0;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    gap: 0 1rem;
  }

  /* zero-height, not the banned display rule — see "hidden always means hidden" */
  .topbar nav::-webkit-scrollbar {
    width: 0;
    height: 0;
  }

  .topbar nav a {
    white-space: nowrap;
  }
}

/* ── the first screen: the pitch, and nothing beside it ─────────────────── */

.hero {
  display: grid;
  gap: clamp(2rem, 5vw, 3rem);
  padding-block: clamp(1.5rem, 4vw, 3rem) clamp(2.25rem, 6vw, 4.5rem);
}

/* ── the #akkoord scene: one phone, three phases that cross-fade ────────────
   Base rules describe the readable, no-motion fallback: the three phases
   stacked in normal flow, fully legible, every amount on its end state. The
   pinned, scrubbing layout lives entirely inside the
   prefers-reduced-motion: no-preference block below, and --scene-motion is
   the one channel by which that preference reaches index.html's script —
   the same pattern as .tally's --count-from. matchMedia stays banned there. */
.scene:not([hidden]) {
  display: grid;
  gap: clamp(2rem, 5vw, 3rem);
}

.scene {
  --scene-motion: 0;
  /* end-state defaults for phase C: no dark stage, signature fully drawn,
     stamp at rest. The script overwrites these per frame, but only under .live. */
  --stage-dark: 0;
  --sig: 1;
  --stamp-scale: 1;
}

.stage:not([hidden]) {
  display: contents;
}

.phase {
  margin: 0;
  width: min(23rem, 100%);
  margin-inline: auto;
}

.phase figcaption {
  margin-bottom: 0.8rem;
  text-align: center;
}

.phase figcaption b:not([hidden]) {
  display: block;
  font-size: var(--step-1);
  color: var(--color-heading);
}

.phase figcaption span {
  font-size: var(--step--1);
  color: var(--color-muted);
}

.rec:not([hidden]) {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.55rem 0;
  font-size: var(--step--1);
  color: var(--color-muted);
}

.rec-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--color-record);
}

.scene-wave:not([hidden]) {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 2rem;
  padding: 0.6rem 0.55rem 0.2rem;
}

.scene-bar {
  flex: 1;
  border-radius: 2px;
  background: var(--color-record);
}

/* varied static heights for the readable fallback; the live block animates them */
.scene-bar:nth-child(3n + 1) {
  height: 55%;
}

.scene-bar:nth-child(3n + 2) {
  height: 85%;
}

.scene-bar:nth-child(3n) {
  height: 35%;
}

.scene-lines:not([hidden]) {
  display: grid;
  gap: 0.5rem;
  padding: 0.5rem 0.55rem 0.85rem;
}

.scene-line:not([hidden]) {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0;
  font-size: var(--step--1);
  font-style: italic;
  color: var(--color-muted);
}

.scene-line .stamp {
  margin-left: auto;
}

@media (prefers-reduced-motion: no-preference) {
  .scene {
    --scene-motion: 1;
  }

  .scene.live:not([hidden]) {
    display: block;
    min-height: 400vh;
  }

  /* top/height clear the fixed .tape, 22px whatever the motion preference.
     background dims to --color-deep as phase B leaves — --stage-dark is the
     script's one channel for it, and the caption turns light on the same number. */
  .scene.live .stage {
    position: sticky;
    top: 22px;
    height: calc(100svh - 22px);
    display: grid;
    grid-template-rows: 1fr auto;
    align-items: center;
    justify-items: center;
    gap: 1rem;
    background: color-mix(in srgb, var(--color-deep) calc(var(--stage-dark) * 100%), transparent);
    border-radius: 18px;
  }

  .scene.live .phase figcaption b {
    color: color-mix(in srgb, var(--color-on-deep) calc(var(--stage-dark) * 100%), var(--color-heading));
  }

  .scene.live .phase figcaption span {
    color: color-mix(in srgb, var(--color-on-deep) calc(var(--stage-dark) * 100%), var(--color-muted));
  }

  /* every phase shares the same grid cell — that is the cross-fade stack;
     the script owns each phase's transform and opacity from here */
  .scene.live .phase {
    grid-area: 1 / 1;
    will-change: transform, opacity;
  }

  .scene.live .scene-dots[hidden] {
    display: flex;
    grid-area: 2 / 1;
    gap: 0.4rem;
  }

  .scene-dot {
    width: 1.5rem;
    height: 3px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--color-line);
  }

  .scene-dot-fill {
    display: block;
    height: 100%;
    transform: scaleX(0);
    transform-origin: left;
    background: var(--color-accent);
  }

  .scene.live .scene-line {
    opacity: 0;
  }

  /* phase B builds itself: cards, totals and the chip start invisible and the
     script fades them in. With reduced motion, or without JavaScript, .live
     never gets added and phase B is simply a finished quote. */
  .scene.live .card,
  .scene.live .totals > div,
  .scene.live .questions {
    opacity: 0;
  }

  /* phase C answers: the option, the signature row, the stamp and the closing
     status line start invisible and the script brings them in. With reduced
     motion, or with no JavaScript, .live never lands and the customer's screen
     is simply a signed, accepted quote. */
  .scene.live .accept-option,
  .scene.live .sign,
  .scene.live .accept-stamp,
  .scene.live .accept-status.done {
    opacity: 0;
  }

  /* the signature draws itself over the path pathLength="1" normalises */
  .scene.live .sign path {
    stroke-dasharray: 1;
    stroke-dashoffset: calc(1 - var(--sig));
  }

  .scene.live {
    --sig: 0;
  }

  .rec-dot {
    animation: rec-pulse 1.6s ease-in-out infinite;
  }

  .scene-bar {
    animation: wave-bar 1.1s ease-in-out infinite alternate;
  }
}

@keyframes rec-pulse {
  from {
    opacity: 1;
  }

  to {
    opacity: 0.25;
  }
}

@keyframes wave-bar {
  from {
    transform: scaleY(0.4);
  }

  to {
    transform: scaleY(1);
  }
}

/* A 100svh stage at 320 × 568 is shorter than phase B's mock — the scale
   *property* is used, not transform: the script owns transform on .phase,
   and the two compose without either overwriting the other. */
@media (max-height: 46rem) {
  .scene.live .phase .phone {
    scale: 0.88;
    transform-origin: top center;
  }
}

@media (max-height: 38rem) {
  .scene.live .phase .phone {
    scale: 0.76;
  }
}

/* Centred, and narrower than the column it sits in: the hero carries only the
   kicker, the heading and the lede, and left-aligned text that short left a
   wide empty half beside it. */
.pitch {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-inline: auto;
  max-width: 46rem;
  gap: 1.15rem;
}

/* .pitch centres its children with align-items: center, so a plain block wrapper
   would left-align the max-width: 20ch heading inside a full-width box. The wrapper
   is the mask; the child keeps the shrink-to-fit width it has today. The
   padding/negative-margin pair gives the line-height: 1.03 heading room, so
   overflow: hidden cannot clip a descender or the <mark> stripe, without changing
   the box the pitch-fit test computes. */
.hero-line {
  width: 100%;
  overflow: hidden;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

.hero-line > * {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
  animation: hero-line-in 0.9s cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
}

.hero-line:nth-child(2) > * {
  animation-delay: 0.12s;
}

.hero-line:nth-child(3) > * {
  animation-delay: 0.24s;
}

@keyframes hero-line-in {
  from {
    transform: translateY(110%);
  }
}

.kicker {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 10%, var(--color-bg));
  border-radius: 999px;
  padding: 0.35rem 0.9rem;
}

/* the word that changes every few seconds — see index.html's ROTATING_TRADES.
   min-width reserves the longest trade so the kicker pill doesn't resize on swap */
.rot {
  display: inline-block;
  min-width: 13ch;
  text-align: center;
  transition: opacity 0.25s, transform 0.25s;
}

.rot.out {
  opacity: 0;
  transform: translateY(0.35em);
}

@media (prefers-reduced-motion: reduce) {
  .rot {
    transition: none;
  }

  .rot.out {
    transform: none;
  }
}

.pitch h1 {
  font-size: var(--step-3);
  line-height: 1.03;
  letter-spacing: -0.04em;
  color: var(--color-heading);
  max-width: 20ch;
}

/* the marker stripe under the second sentence — a highlighter, not a fill,
   so the heading keeps its own colour and contrast. Stops are percentages of
   the *font* box (ascent + descent), not of the line box, because that's what
   an inline element's background positioning area follows — 62% clears the
   x-height, 92% stays above the bottom of the line box. box-decoration-break
   gives a wrapped heading one identical stripe per line fragment instead of
   one gradient sliced across both. */
.pitch h1 mark {
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  background: linear-gradient(transparent 62%, color-mix(in srgb, var(--color-accent) 25%, var(--color-bg)) 62% 92%, transparent 92%);
  color: inherit;
}

.pitch .lede {
  font-size: var(--step-1);
  line-height: 1.4;
  letter-spacing: -0.015em;
  color: var(--color-muted);
  max-width: 38rem;
}

.pitch-action {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  margin-top: 0.35rem;
}

/* The two calls to action and the store line, at the foot of #akkoord. They used
   to sit in the hero; a visitor who has just read the whole loop has a reason to
   press one, a visitor three seconds in has not. */
.cta-block {
  margin-top: clamp(1.8rem, 4vw, 2.6rem);
  display: grid;
  justify-items: center;
  gap: 0.9rem;
  text-align: center;
}

.cta-block .pitch-action {
  margin-top: 0;
}

.pill {
  font-size: var(--step--1);
  letter-spacing: 0.01em;
  color: var(--color-muted);
}

/* a FilledButton, straight from the app's seed. The display declaration stays
   behind :not([hidden]) so a hidden button is really hidden — an author rule
   beats the UA [hidden] default whatever its specificity. */
.btn:not([hidden]) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  min-width: 48px;
  padding: 0.75rem 1.5rem;
  border: 0;
  border-radius: 10px;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: inherit;
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: -0.005em;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s;
}

.btn:hover {
  background: color-mix(in srgb, var(--color-accent) 82%, black);
}

.btn:active {
  transform: translateY(1px);
}

.btn:disabled {
  opacity: 0.55;
  cursor: default;
}

.btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0.75rem 1.25rem;
  border: 1px solid var(--color-line);
  border-radius: 10px;
  color: var(--color-accent);
  font-weight: 600;
  text-decoration: none;
}

.btn-ghost:hover {
  border-color: var(--color-accent);
}

/* ── the promise strip, closing the call to action ──────────────────────── */

.trust {
  background: var(--color-deep);
  color: var(--color-on-deep);
}

.promises {
  list-style: none;
  margin: 0;
  padding-block: 1.05rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  font-size: var(--step-text);
  font-weight: 600;
}

.promises li {
  position: relative;
  padding-left: 1.35rem;
}

.promises li::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--color-green-bright);
  font-weight: 700;
}

/* ── the device ─────────────────────────────────────────────────────────── */

.phone {
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-line);
  border-radius: 26px;
  box-shadow: var(--shadow-phone);
  padding: 0.85rem;
}

.screen {
  display: grid;
  gap: 0.1rem;
}

.appbar {
  display: grid;
  gap: 0.1rem;
  padding: 0.35rem 0.55rem 0.85rem;
  border-bottom: 1px solid var(--color-line-soft);
}

.appbar strong {
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--color-heading);
}

.appbar span {
  font-size: var(--step--1);
  color: var(--color-muted);
}

.lines {
  display: grid;
}

/* an app card: CardThemeData — 12px radius, outlineVariant hairline, elevation 0 */
.card {
  padding: 0.8rem 0.55rem;
  border-bottom: 1px solid var(--color-line-soft);
  display: grid;
  gap: 0.15rem;
}

.line-head {
  display: flex;
  justify-content: space-between;
  gap: 0.8rem;
  font-size: var(--step-text);
}

.line-head b {
  font-weight: 600;
  color: var(--color-fg);
}

.amount {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--color-heading);
}

.qty {
  font-size: var(--step--1);
  color: var(--color-muted);
}

.source {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  margin-top: 0.35rem;
  font-size: var(--step--1);
  font-style: italic;
  color: var(--color-muted);
}

.source.standard {
  font-style: normal;
}

.play {
  font-style: normal;
  font-size: 0.65rem;
  color: var(--color-accent);
}

.stamp {
  margin-left: auto;
  font-style: normal;
  font-variant-numeric: tabular-nums;
}

/* :not([hidden]) here too, for the same reason as .btn above */
.totals:not([hidden]) {
  display: grid;
  gap: 0.1rem;
  padding: 0.75rem 0.55rem 0.5rem;
  font-size: var(--step-text);
}

.totals > div:not([hidden]) {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-variant-numeric: tabular-nums;
}

.totals > div.vat {
  font-size: var(--step--1);
  color: var(--color-muted);
}

.totals > div.grand {
  margin-top: 0.35rem;
  padding-top: 0.45rem;
  border-top: 2px solid var(--color-heading);
  font-weight: 700;
  color: var(--color-heading);
}

/* the one warm note on the page: the app's tertiary, where the app uses it. */
.questions:not([hidden]) {
  display: grid;
  gap: 0.15rem;
  margin: 0.35rem 0.2rem 0.2rem;
  padding: 0.7rem 0.8rem;
  border-radius: 12px;
  background: var(--color-amber-soft);
  color: var(--color-amber-ink);
  font-size: var(--step--1);
}

.questions b {
  font-size: var(--step--2);
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

/* ── light bands ────────────────────────────────────────────────────────── */

.band {
  padding-block: clamp(2.5rem, 7vw, 5.5rem);
}

.band.tight {
  padding-top: 0;
}

.band.soft,
.soft-band {
  background: var(--color-panel-2);
  border-block: 1px solid var(--color-line-soft);
}

/* Centred, like the hero and #koppelingen: every section head reads the same way
   down the page, and a 38rem measure left-aligned in a 72rem shell put half the
   band's width beside every heading. Only the head is centred — the cards, lists
   and chips below it stay left-aligned, which is how they are read. */
.band-head {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 0.7rem;
  max-width: 38rem;
  margin-inline: auto;
  margin-bottom: clamp(2rem, 4.5vw, 3.5rem);
}

.band-head .eyebrow {
  color: var(--color-accent);
}

.band-head h2 {
  font-size: var(--step-2);
  line-height: 1.08;
  color: var(--color-heading);
}

.band-head p {
  color: var(--color-muted);
  font-size: var(--step-1);
  letter-spacing: -0.012em;
}

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: clamp(1rem, 2.5vw, 1.5rem);
}

.step {
  padding: 1.4rem 1.5rem;
  border: 1px solid var(--color-line-soft);
  border-radius: 16px;
  display: grid;
  gap: 0.4rem;
  align-content: start;
}

.step .num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  margin-bottom: 0.5rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--color-accent) 12%, var(--color-bg));
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
  font-size: var(--step-text);
  font-weight: 700;
}

.step h3 {
  font-size: var(--step-1);
  color: var(--color-heading);
}

.step p {
  color: var(--color-muted);
  font-size: var(--step-text);
  line-height: 1.6;
  max-width: 21rem;
}

/* ── the customer says yes on his own phone ─────────────────────────────── */

.checklist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: 0.8rem clamp(1.5rem, 4vw, 3rem);
  font-size: var(--step-text);
  line-height: 1.6;
}

.checklist li {
  position: relative;
  padding-left: 1.6rem;
  color: var(--color-muted);
}

.checklist li::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--color-green);
  font-weight: 700;
}

/* the second screen in .screens — the same device, a different app: what the
   customer opens. Roomier than the review screen, because it holds one decision. */
.accept-mock {
  position: relative; /* the stamp's containing block */
  overflow: hidden; /* and its clip — a scaled stamp can never widen the page */
  gap: 0.7rem;
  padding: 0 0.2rem 0.3rem;
}

.accept-mock .sum {
  font-size: var(--step-2);
  font-weight: 700;
  letter-spacing: -0.035em;
  color: var(--color-heading);
}

.accept-mock .sum span {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0;
  color: var(--color-muted);
}

.accept-option {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--color-line-soft);
  border-radius: 12px;
  font-size: var(--step--1);
}

.accept-option .tick {
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 5px;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: 0.7rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.accept-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  border-radius: 10px;
  background: var(--color-green);
  color: var(--color-on-green);
  font-weight: 700;
  text-decoration: none;
}

.sign {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.7rem;
  border: 1px dashed var(--color-line);
  border-radius: 12px;
  font-size: var(--step--1);
  color: var(--color-muted);
}

.sign svg {
  color: var(--color-fg);
}

.accept-status {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--step--1);
  color: var(--color-muted);
}

.accept-stamp {
  position: absolute;
  right: 0.6rem;
  bottom: 3.2rem;
  padding: 0.15rem 0.5rem;
  border: 2px solid var(--color-green);
  border-radius: 6px;
  color: var(--color-green);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.08em;
  transform: rotate(-11deg) scale(var(--stamp-scale));
  transform-origin: center;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--color-green);
}

/* ── what is in the app today ───────────────────────────────────────────── */

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: 0 clamp(1.5rem, 4vw, 3rem);
}

.fact {
  position: relative;
  padding: 0.75rem 0 0.75rem 1.6rem;
  border-bottom: 1px solid var(--color-line-soft);
  font-size: var(--step-text);
  line-height: 1.6;
  color: var(--color-muted);
}

.fact::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--color-green);
  font-weight: 700;
}

.fact b {
  color: var(--color-fg);
  font-weight: 600;
}

/* ── the accounting packages, as a row of ticked-off tiles ──────────────── */

.koppel {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 0.5rem;
}

.koppel h2 {
  font-size: var(--step-2);
  line-height: 1.08;
  letter-spacing: -0.025em;
  color: var(--color-heading);
}

.koppel-tiles {
  margin-top: 1.1rem;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.8rem, 2vw, 1.2rem);
  width: min(38rem, 100%);
}

@media (max-width: 36rem) {
  .koppel-tiles {
    grid-template-columns: minmax(0, 1fr);
  }
}

.koppel-tile {
  padding: 1.3rem 1rem;
  border: 1px solid var(--color-line-soft);
  border-radius: 16px;
  background: var(--color-panel-2);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--color-heading);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}

/* The tick that says "this one is done". Deliberately the same accent-blue disc
   as .step .num above it, so the row reads as the ticked-off tail of the four
   steps rather than as a second, unrelated kind of badge. */
.koppel-tile::before {
  content: '✓';
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--color-accent) 12%, var(--color-bg));
  color: var(--color-accent);
  font-size: 0.85rem;
  font-weight: 700;
}

/* ── the trades ─────────────────────────────────────────────────────────── */

.trades {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

/* Without JavaScript and under reduced motion the chips are the plain wrapped list
   they are today: the rows dissolve into their parent and the clones stay hidden.
   :not([hidden]) matters here — an author rule beats the UA [hidden] default
   regardless of specificity (see "hidden always means hidden" in
   tool/website.test.mjs), so a bare .trades-track { display: contents } would
   un-hide the clone tracks everywhere, not just while the marquee runs.
   Everything the marquee needs lives in the no-preference block below, because
   the fallback is a different layout, not just the same layout standing still. */
.trades-row,
.trades-track:not([hidden]) {
  display: contents;
}

@media (prefers-reduced-motion: no-preference) {
  .trades.seen {
    display: block;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to right, transparent, black 2.5rem, black calc(100% - 2.5rem), transparent);
    mask-image: linear-gradient(to right, transparent, black 2.5rem, black calc(100% - 2.5rem), transparent);
  }

  .trades.seen .trades-row {
    display: flex;
    gap: 0;
    width: max-content;
    animation: trades-marquee 46s linear infinite;
  }

  .trades.seen .trades-row + .trades-row {
    margin-top: 0.6rem;
    animation-direction: reverse;
  }

  .trades.seen .trades-track,
  .trades.seen .trades-track[hidden] {
    display: flex;
    gap: 0.6rem;
    padding-right: 0.6rem;
  }
}

@keyframes trades-marquee {
  to {
    transform: translateX(-50%);
  }
}

.trade {
  padding: 0.5rem 1.05rem;
  border: 1px solid var(--color-line-soft);
  border-radius: 999px;
  background: var(--color-panel);
  font-size: var(--step-text);
  font-weight: 600;
}

/* the contrast: what the evening costs, what it costs now */
.swap {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: clamp(1rem, 2.5vw, 2rem);
  align-items: stretch;
}

.swap > div {
  border: 1px solid var(--color-line-soft);
  border-radius: 18px;
  padding: 1.75rem 1.6rem;
  display: grid;
  gap: 0.5rem;
  align-content: start;
}

.swap .then {
  background: var(--color-panel);
  color: var(--color-muted);
}

.swap .now {
  background: color-mix(in srgb, var(--color-accent) 8%, var(--color-bg));
  border-color: color-mix(in srgb, var(--color-accent) 35%, transparent);
}

.swap .big {
  font-size: clamp(1.8rem, 1.2rem + 2.4vw, 2.8rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--color-heading);
}

.swap .now .big {
  color: var(--color-accent);
}

.swap .label {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-muted);
}

.swap p {
  font-size: var(--step-text);
  line-height: 1.6;
  color: var(--color-muted);
  max-width: 24rem;
}

.tally {
  --count-from: 4;
  margin-top: clamp(1rem, 2vw, 1.5rem);
  font-size: var(--step-1);
  color: var(--color-heading);
  letter-spacing: -0.015em;
}

.tally-count {
  display: inline-block;
  min-width: 3ch;
  text-align: center;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent);
}

@media (prefers-reduced-motion: no-preference) {
  .tally {
    --count-from: 240;
  }
}

/* the tradesman Duimstok was built for — one line, no photo, no surname */
.voice {
  margin: clamp(1.5rem, 3vw, 2.5rem) 0 0;
  padding-left: clamp(1rem, 2vw, 1.5rem);
  border-left: 3px solid var(--color-accent);
  display: grid;
  gap: 0.6rem;
  max-width: 34rem;
}

.voice blockquote {
  margin: 0;
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.3;
  text-wrap: balance;
  color: var(--color-heading);
}

.voice figcaption {
  font-size: var(--step-text);
  color: var(--color-muted);
}

/* ── objections: the four "ja maar"s ────────────────────────────────────── */

.objections {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, 2.5vw, 1.5rem);
}

@media (max-width: 56rem) {
  .objections {
    grid-template-columns: minmax(0, 1fr);
  }
}

.objection {
  padding: 1.4rem 1.5rem;
  border-radius: 16px;
  border-top: 3px solid var(--color-accent);
  background: var(--color-panel);
  display: grid;
  gap: 0.5rem;
  align-content: start;
}

.objection h3 {
  font-size: var(--step-1);
  color: var(--color-heading);
}

.objection p {
  color: var(--color-muted);
  font-size: var(--step-text);
  line-height: 1.6;
}

/* ── the sign-up band ───────────────────────────────────────────────────── */

.signup-band {
  background: var(--color-panel-2);
  border-block: 1px solid var(--color-line-soft);
}

/* Centred under its own centred head — the field and the button are the last
   thing on the page, and off to the left they read as an afterthought. */
.signup {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: center;
  gap: 0.7rem 0.8rem;
  margin-top: 1.5rem;
  margin-inline: auto;
  max-width: 34rem;
}

.signup-field {
  display: grid;
  gap: 0.35rem;
  flex: 1 1 15rem;
}

.signup-field label {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--color-muted);
}

.signup-input {
  min-height: 48px;
  width: 100%;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--color-line);
  border-radius: 10px;
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: inherit;
  /* at least 16px, or iOS Safari zooms the page when the field gains focus */
  font-size: 1rem;
}

.signup-input::placeholder {
  color: var(--color-muted);
}

/* visually hidden the clip-path way — the honeypot must stay reachable to a
   bot and invisible to everyone else, without taking it out of the layout tree */
.signup-trap {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.signup-status {
  flex-basis: 100%;
  font-size: var(--step-text);
  font-weight: 600;
  color: var(--color-green);
}

.signup-note {
  margin-top: 0.8rem;
  font-size: var(--step-text);
  color: var(--color-muted);
  text-align: center;
}

/* ── footer ─────────────────────────────────────────────────────────────── */

footer {
  border-top: 1px solid var(--color-line-soft);
  padding-block: 3rem 4rem;
  display: grid;
  gap: 1.75rem;
}

.footer-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  font-size: var(--step-text);
}

.footer-links a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: var(--color-muted);
  transition: color 0.2s;
}

.footer-links a:hover {
  color: var(--color-fg);
}

.soon {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
  border: 1px solid color-mix(in srgb, var(--color-accent) 45%, transparent);
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
}

.colofon {
  color: var(--color-muted);
  font-size: var(--step-text);
  max-width: var(--measure);
}

/* ── reveal on scroll ───────────────────────────────────────────────────── */

.reveal {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity 0.6s cubic-bezier(0.2, 0.7, 0.3, 1),
    transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.reveal.seen {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }
}

/* ── document pages: privacy, account verwijderen, faq ──────────────────────
   The chrome the privacy generator (tool/build-privacy-page.mjs) emits — keep
   these class names stable or regenerate privacy.html. */

.wrap {
  max-width: 42rem;
  margin: 0 auto;
  padding: 4.5rem var(--pad);
}

.backlink {
  margin: 0 0 2.5rem;
  font-size: var(--step--1);
}

.backlink a {
  color: var(--color-muted);
  text-decoration: none;
  transition: color 0.2s;
}

.backlink a:hover {
  color: var(--color-accent);
}

.doc h1 {
  font-size: var(--step-2);
  margin: 0 0 1.75rem;
}

.doc h2 {
  font-size: var(--step-1);
  margin: 2.5rem 0 1rem;
}

.doc .eyebrow {
  color: var(--color-accent);
  margin: 3rem 0 0.5rem;
}

.doc p,
.doc li {
  color: var(--color-fg);
}

.doc ul {
  padding-left: 1.25rem;
}

.doc li {
  margin: 0.5rem 0;
}

.doc a {
  color: var(--color-accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.doc blockquote {
  margin: 0 0 2rem;
  padding: 1rem 1.25rem;
  border: 1px solid color-mix(in srgb, var(--color-amber) 45%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--color-amber-soft) 55%, var(--color-bg));
  color: var(--color-amber-ink);
}

.doc blockquote p {
  margin: 0;
  color: inherit;
}

.doc code {
  font-size: 0.9em;
  background: var(--color-panel);
  border: 1px solid var(--color-line-soft);
  border-radius: 0.25rem;
  padding: 0.1rem 0.35rem;
  overflow-wrap: anywhere;
}

.table-scroll {
  overflow-x: auto;
  margin: 0 0 2rem;
}

.doc table {
  width: 100%;
  min-width: 28rem;
  border-collapse: collapse;
  margin: 0;
  font-size: 0.95rem;
}

.doc th,
.doc td {
  text-align: left;
  vertical-align: top;
  padding: 0.5rem;
  border-bottom: 1px solid var(--color-line-soft);
}

.doc th {
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-muted);
  white-space: nowrap;
}

.doc details {
  margin: 0 0 2rem;
  color: var(--color-muted);
  font-size: var(--step-text);
}

.doc summary {
  cursor: pointer;
  color: var(--color-accent);
}

.doc details ul {
  margin-top: 0.75rem;
}

.badge {
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-amber);
  border: 1px solid var(--color-amber);
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
  white-space: nowrap;
}

/* ── the public quote page (/offerte/<token>) ────────────────────────────
   .doc table's 28rem (448px) min-width would clip, not scroll, at a 375px
   viewport (body sets overflow-x: hidden) — .doc.quote overrides win on
   specificity, not source order. */

.doc.quote table {
  min-width: 0px;
  width: 100%;
  font-size: var(--step-text);
}

.quote-logo {
  margin: 0 0 1.5rem;
}

.quote-logo img {
  max-width: 100%;
  max-height: 96px;
  height: auto;
  display: block;
}

.quote-lines th,
.quote-lines td {
  padding: 0.45rem 0.35rem;
}

.doc.quote .quote-lines thead th {
  white-space: normal;
}

.quote-lines td:nth-child(2),
.quote-lines td:nth-child(3),
.quote-lines td:nth-child(4),
.quote-lines td:nth-child(5) {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.quote-lines td:first-child {
  overflow-wrap: anywhere;
}

.doc.quote .quote-amounts th {
  text-transform: none;
  letter-spacing: normal;
  font-size: inherit;
  color: var(--color-fg);
  white-space: normal;
}

.quote-options-note {
  margin: 0 0 1rem;
  color: var(--color-muted);
}

.quote-option-choice {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  cursor: pointer;
}

.quote-option-check {
  min-height: 44px;
  min-width: 44px;
  flex: 0 0 auto;
  accent-color: var(--color-accent);
}

.quote-options-total {
  margin: 0.75rem 0 1.5rem;
  font-weight: 600;
}

.quote-status,
.quote-superseded {
  margin: 0 0 1.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--color-line);
  border-radius: 12px;
  background: var(--color-panel);
}

.quote-download {
  margin: 0 0 1.5rem;
}

.quote-respond {
  margin: 0 0 1.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--color-line);
  border-radius: 12px;
  background: var(--color-panel);
  display: grid;
  gap: 0.9rem;
}

.quote-respond h2 {
  font-size: var(--step-1);
}

.quote-respond-status:not([hidden]) {
  display: block;
}

.quote-respond-form:not([hidden]) {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
}

.quote-respond-field {
  display: grid;
  gap: 0.3rem;
  flex: 1 1 12rem;
  min-width: 0;
}

.quote-respond-field label {
  font-size: var(--step--2);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-muted);
}

.quote-respond-input {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  font: inherit;
  /* at or above 16px so iOS Safari does not zoom the page on focus */
  font-size: var(--step-0);
  color: var(--color-fg);
  background: var(--color-panel-2);
  border: 1px solid var(--color-line);
  border-radius: 999px;
  padding: 0.65rem 1rem;
}

.quote-signature {
  display: grid;
  gap: 0.3rem;
  /* Without this the flex item's automatic minimum size falls back to the canvas's
     intrinsic 600px bitmap (width: 100% against an indefinite basis is not a definite
     size, and height: auto yields no transferred size suggestion), and
     body's overflow-x: hidden clips rather than scrolls it at a 375px viewport — the
     same trap the .doc.quote table comment above already records. The 100% basis also
     gives the pad its own row in the wrapping form. */
  flex: 1 1 100%;
  min-width: 0;
}

.quote-signature-label {
  margin: 0;
  font-size: var(--step--2);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-muted);
}

.quote-signature-hint {
  margin: 0 0 0.3rem;
  font-size: var(--step--1);
  color: var(--color-muted);
}

.quote-signature-canvas {
  /* Stops the page from scrolling while a finger draws on it. */
  touch-action: none;
  width: 100%;
  max-width: 100%;
  height: auto;
  color: var(--color-signature-ink);
  background: var(--color-signature-surface);
  border: 1px solid var(--color-line);
  border-radius: 12px;
  cursor: crosshair;
}

.quote-signature-clear:not([hidden]) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: -0.005em;
  border-radius: 999px;
  padding: 0.7rem 1.35rem;
  cursor: pointer;
  color: var(--color-fg);
  background: transparent;
  border: 1px solid var(--color-line);
  transition:
    background 0.2s,
    transform 0.2s;
}

.quote-signature-clear:disabled {
  opacity: 0.55;
  cursor: default;
  transform: none;
}

.quote-accept:not([hidden]) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: -0.005em;
  border-radius: 999px;
  padding: 0.7rem 1.35rem;
  cursor: pointer;
  color: var(--color-on-accent);
  background: var(--color-accent);
  border: 0;
  transition:
    background 0.2s,
    transform 0.2s;
}

.quote-reject:not([hidden]) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: -0.005em;
  border-radius: 999px;
  padding: 0.7rem 1.35rem;
  cursor: pointer;
  color: var(--color-fg);
  background: transparent;
  border: 1px solid var(--color-line);
  transition:
    background 0.2s,
    transform 0.2s;
}

.quote-accept:disabled,
.quote-reject:disabled {
  opacity: 0.55;
  cursor: default;
  transform: none;
}

@media (max-width: 34rem) {
  /* Visually hidden with clip-path, not a display declaration, so the column
     labels stay in the accessibility tree even though the stacked layout
     below repeats them as each row's data-label. */
  .quote-lines thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .quote-lines tr {
    display: block;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--color-line-soft);
  }

  .quote-lines td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.2rem 0;
    border: 0;
  }

  .quote-lines td::before {
    content: attr(data-label);
    color: var(--color-muted);
    font-size: var(--step--1);
  }
}
