/* ==========================================================================
   3Technics — style specimen
   Kleuren en ruimtes komen altijd uit tokens.css. Typografische fijnafstelling
   per component (regelhoogte, onderstreepdikte, een enkele pixelmaat) mag hier
   inline staan — dat zijn waarden die nergens anders terugkomen en die als token
   alleen maar ruis zouden toevoegen.
   ========================================================================== */

/* --- webfonts ------------------------------------------------------------- */
/* Zelf gehoste variabele fonts, 400-700, geen cursief (de site gebruikt er geen).
   Door unicode-range haalt de browser de latin-ext-bestanden alleen op wanneer een
   pagina zo'n teken werkelijk gebruikt; Nederlandse tekst doet dat normaal niet.
   Beide families vallen onder de SIL Open Font License — zie letters/NOTITIE.md. */
@font-face {
  font-family: "Archivo";
  src: url("letters/archivo-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  src: url("letters/archivo-latin-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Source Sans 3";
  src: url("letters/source-sans-3-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Source Sans 3";
  src: url("letters/source-sans-3-latin-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- spacing utilities ---------------------------------------------------- */
/* Vervangen elf inline style="margin-top: …"-attributen. Als losse klasse is
   het voor wie later aan de site werkt makkelijker terug te vinden. */
.u-stack-3 { margin-top: var(--space-3); }
.u-stack-5 { margin-top: var(--space-5); }
.u-stack-6 { margin-top: var(--space-6); }

/* --- skip link ------------------------------------------------------------ */
/* Zeven focusstops staan voor de inhoud; dit slaat ze over. */
.skip-link {
  position: absolute;
  top: calc(-1 * var(--space-9));
  left: var(--gutter);
  z-index: calc(var(--z-header) + 1);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--border-width-strong) solid var(--color-line-contrast);
  border-radius: var(--radius-xs);
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  text-decoration: none;
}
.skip-link:focus { top: var(--space-2); }
/* Het doel van de skiplink krijgt tabindex="-1" zodat Safari de focus meeneemt;
   die focus mag geen ring tonen, want de gebruiker klikte er niet op. */
main:focus,
section[tabindex="-1"]:focus { outline: none; }

/* --- reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; color-scheme: light; }
html { scroll-padding-top: calc(var(--header-height) + var(--space-4)); }
body, h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }
ul[class] { list-style: none; padding: 0; }
img { max-width: 100%; height: auto; display: block; }  /* height:auto is required, otherwise the HTML height attribute beats aspect-ratio */
button, input, textarea, select { font: inherit; color: inherit; }
/* --- base ----------------------------------------------------------------- */
body {
  background: var(--color-page);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--leading-body);
  font-weight: var(--font-weight-regular);
}
h1, h2, h3, h4 {
  font-family: var(--font-display);
  text-wrap: balance;
}
h1 {
  font-size: var(--font-size-2xl);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-weight: var(--font-weight-bold);
}
h2 {
  font-size: var(--font-size-xl);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-weight: var(--font-weight-semibold);
}
h3 {
  font-size: var(--font-size-lg);
  line-height: var(--leading-snug);
  font-weight: var(--font-weight-semibold);
}
p { text-wrap: pretty; max-width: var(--measure); }
/* 404.html carried class="lead" while the only rule for it was scoped to the
   dark band, so that paragraph rendered as plain body text. Global now, on
   --leading-normal as huisstijl.md 2.2 specifies for a lead (1.45, not 1.25). */
.lead {
  font-size: var(--font-size-lead);
  line-height: var(--leading-normal);
  max-width: var(--measure-narrow);
}
/* Global focus style. Never remove without an equivalent replacement. */
:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: var(--focus-ring-offset);
}
.theme-dark :focus-visible { outline-color: var(--color-focus-on-dark); }
/* --- layout helpers ------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--wide { max-width: var(--container-wide); }
/* Centred, this container put the running text of a service page at 432px on a
   1440px screen while the hero above it and the form below it both start at 96px
   — a 336px jog in the middle of the page. The margin below lines its content up
   with the wide container's content edge at every width: it offsets by exactly
   the amount the wide container is inset, and the gutter does the rest.
   Checked at 320, 1000 and 1440: 20, 30.4 and 96, matching .container--wide. */
.container--narrow {
  max-width: var(--container-narrow);
  margin-inline: calc((100% - min(100%, var(--container-wide))) / 2) auto;
}
.section { padding-block: var(--space-section); }
/* Only between two plain sections. A filled band separates itself — .urgent
   carries its own border-block and the dark band its 4px green rule — and
   neither carries the .section class, so neither gets a second line on top of
   that. Worth stating: it reads as designed, but it works by accident. */
.section + .section { border-top: var(--border-width-hairline) solid var(--color-line); }
/* --- meta label ----------------------------------------------------------- */
.meta {
  font-family: var(--font-display);
  font-size: var(--font-size-meta);
  line-height: var(--leading-meta);
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
}
/* ==========================================================================
   PAGE HEADER
   ========================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--color-surface);
  /* A decorative hairline measured 1.26:1 against the paper below it, so the
     sticky bar had no visible edge while scrolling. No shadow is allowed here
     (huisstijl.md 2.5), so the line carries it: 3.26:1 against paper. */
  border-bottom: var(--border-width-hairline) solid var(--color-line-strong);
}
.site-header__inner {
  min-height: var(--header-height);
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.site-header__logo { margin-right: auto; display: flex; align-items: center; }
/* The wordmark was the smallest thing in its own header bar. Measured at 1440px
   against a bar of 77px: logo 44px (57.1%), Offerte button 48.4px (62.9%), nav
   link boxes 50.8px (66.0%) — so the brand read as subordinate to a button.
   52px puts it just above the button at 67.5% and still leaves 12.5px of air
   above and below, so the bar keeps its height and no container changes. The
   ceiling with 8px of air would be 61px, which is too much: at that size the
   mark crowds its own bar. */
.site-header__logo img { height: 42px; width: auto; }
/* Static, so the absolutely positioned panel below takes its position from
   the sticky header instead of from this button. Anchored to the button it
   started at left: -34px on a 320px viewport — the panel is 240px wide and the
   button's right edge sits at 206px — which clipped the first letter of every
   label off-screen. Overflow to the left creates no scrollbar, which is why no
   overflow check ever caught it. */
.site-nav-toggle { position: static; }
.site-nav-toggle > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target-min);
  padding-inline: var(--space-3);
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
  border: var(--border-width-strong) solid var(--color-line-contrast);
  border-radius: var(--radius-xs);
  transition: var(--transition-interactive);
}
.site-nav-toggle > summary::-webkit-details-marker { display: none; }
.site-nav-toggle > summary:hover { background: var(--color-line-contrast); color: var(--color-text-on-dark); }
.site-nav {
  display: none;
  position: absolute;
  right: var(--gutter);
  top: calc(100% + var(--space-3));
  min-width: 15rem;
  max-width: calc(100vw - 2 * var(--gutter));
  /* At 400% zoom the viewport is 320x256 and five 48px rows do not fit; the
     header is sticky, so scrolling the page cannot bring the last two into
     reach. Cap the panel and let it scroll on its own instead. */
  max-height: calc(100vh - var(--header-height) - var(--space-5));
  max-height: calc(100dvh - var(--header-height) - var(--space-5));
  overflow-y: auto;
  background: var(--color-surface);
  border: var(--border-width-hairline) solid var(--color-line-strong);
  border-radius: var(--radius-xs);
  padding: var(--space-2);
}
.site-nav-toggle[open] .site-nav { display: block; }
.site-nav a {
  display: block;
  padding: var(--space-3);
  min-height: var(--tap-target-min);
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
  color: var(--color-text);
  text-decoration: none;
  border-bottom: var(--border-width-strong) solid transparent;
  transition: var(--transition-interactive);
}
.site-nav a:hover { border-bottom-color: var(--color-line-contrast); }
.site-nav a[aria-current="page"] { border-bottom-color: var(--color-accent); }
/* Very narrow phones: keep logo, menu and the one call to action on one row. */
@media (max-width: 419px) {
  .site-header__inner { gap: var(--space-2); }
  .site-header__logo img { height: 34px; }
  .site-header .btn { padding-inline: var(--space-4); font-size: var(--font-size-sm); }
  .site-nav-toggle > summary { padding-inline: var(--space-2); }
}
@media (min-width: 840px) {
  html { scroll-padding-top: calc(var(--header-height-wide) + var(--space-4)); }
  .site-header__inner { min-height: var(--header-height-wide); }
  .site-header__logo img { height: 52px; }
}

/* THE LAID-OUT NAVIGATION
   Two reasons this block is not simply "@media (min-width: 840px)".

   The width. Measured in the browser with the loaded Archivo, the five labels
   need 956.68px of viewport to each sit on one line: logo 108.39 + button
   108.59 + nav 652.02 + gutters 55.68 + two header gaps 32. At 840px the nav is
   116.68px short and "Vellen en demonteren" and "Hakselen en afvoer" each wrap
   onto a second line — right at the breakpoint that reveals the nav. So it
   starts at the next breakpoint up, 1000px, which still leaves margin.

   The guard. This block only works where ::details-content exists. Without it
   the nav collapses to width 0 while the summary is already hidden, so there is
   no navigation at all: measured on this page, hit-testing the centre of a nav
   link returns the header container, not the link. Safari before 18.4, Firefox
   before 139 (including ESR 128) and Chrome before 131 all land there. Guarding
   only the summary rule is not enough — the rules that flatten the panel have
   to sit behind the same condition, otherwise those engines get a permanently
   expanded link row. Behind the guard they keep the dropdown, which works. */
@supports selector(::details-content) {
  @media (min-width: 1000px) {
    .site-nav-toggle > summary { display: none; }
    .site-nav-toggle::details-content { content-visibility: visible; block-size: auto; }
    .site-nav-toggle .site-nav,
    .site-nav-toggle[open] .site-nav {
      display: flex;
      position: static;
      min-width: 0;
      max-width: none;
      max-height: none;
      overflow: visible;
      border: 0;
      padding: 0;
      gap: var(--space-2);
      background: none;
    }
    .site-nav a { padding-inline: var(--space-3); display: flex; align-items: center; }
  }
}
/* ==========================================================================
   BUTTONS
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-target-min);
  padding: var(--space-3) var(--space-5);
  border: var(--border-width-strong) solid transparent;
  border-radius: var(--radius-xs);
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  font-size: 1.0625rem;              /* 17px — sits between --sm and --base */
  letter-spacing: var(--tracking-wide);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: var(--transition-interactive);
}
.btn--primary {
  background: var(--color-accent);
  color: var(--color-text-on-accent);
}
.btn--primary:hover { background: var(--color-accent-hover); }
.btn--primary:active { background: var(--color-accent-active); }
.btn--secondary {
  background: transparent;
  color: var(--color-text);
  border-color: var(--color-line-contrast);
}
.btn--secondary:hover {
  background: var(--color-line-contrast);
  color: var(--color-text-on-dark);
}
/* A second way in next to the offer form: secondary weight, so it never
   competes with the green primary button, with the WhatsApp glyph in its own
   colour so it reads as WhatsApp before the label is read. */
.btn--whatsapp {
  background: var(--color-surface);
  color: var(--color-text);
  border-color: var(--color-line-contrast);
}
.btn--whatsapp:hover { border-color: var(--color-accent); }
.btn__icon { flex: none; }
.btn--whatsapp .btn__icon { color: var(--color-whatsapp); }
/* The phone number only reaches the screen after a full scroll on a phone, so
   the header carries it. From 1000px the laid-out navigation needs the room
   (see THE LAID-OUT NAVIGATION) and a tel: link does little on a desktop. */
@media (min-width: 1000px) { .site-header__call { display: none; } }
.link {
  color: var(--color-accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: var(--transition-interactive);
}
.link:hover {
  color: var(--color-text);
  text-decoration-thickness: 2px;
}
/* ==========================================================================
   IMAGE FRAME
   Fixed ratio + cover, so photos from four different cameras line up.
   ========================================================================== */
.frame {
  margin: 0;
  max-width: var(--image-max-portrait);
}
/* THE EDGE OF A PHOTOGRAPH
   huisstijl.md 2.4 point 6 gives every photo a hairline for a stated reason:
   "zonder die lijn lopen de vele witte en lichtgrijze luchten weg in de
   papierkleur". The reason is right and the colour it named could not deliver
   it — --color-line reaches 1.26:1 against the paper, so where a photo ends in
   a blown-out sky there is no visible edge at all.

   Measured on the right-hand 3px column of each hero as it renders, after
   object-fit: cover, with the percentage of that column that is near-white and
   its average contrast against the paper:

     snoeien          96.9% near-white   1.05:1   <- no visible edge
     boomveiligheid    3.0%              6.63:1
     vellen            0%                7.42:1
     hakselen          0%                9.26:1
     klimmer (hero)    0%                3.94:1
     voor / na        21.3% / 18.5%      6.51 / 6.11
     portret           0%                1.72:1   (light blue sky)

   --color-line-strong carries it at 3.26:1 against paper and 3.59:1 against
   white — the same token and the same reasoning as the sticky header, which had
   the same invisible-boundary problem. Layout hairlines (section dividers, card
   borders) keep --color-line; this is only for the edge of a photograph, which
   is the one place the line has a job to do rather than a look to provide. */
.frame__media {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--ratio-portrait);
  object-fit: cover;
  background: var(--color-surface-quiet);
  border: var(--border-width-hairline) solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  filter: var(--image-filter);
}
.frame--tall .frame__media { aspect-ratio: var(--ratio-portrait-tall); }
.frame figcaption { margin-top: var(--space-3); }
/* Every photograph gets the same faint correction and the same hairline
   (huisstijl.md 2.4, points 5 and 6), and the frame decides the shape rather
   than the photo (point 1). Only .frame__media and .card__media were doing
   that, so four of the eight photos on the homepage went without: measured
   filter: none on the storm damage photo, on both before/after frames and on
   the portrait. Logos are deliberately absent here — they are not photographs.
   The portrait keeps its own proportions for now; huisstijl.md 2.4 point 12
   asks for a square frame and that crop needs to be judged by eye. */
.urgent__photo img,
.pair img,
.about__portrait img {
  filter: var(--image-filter);
  border: var(--border-width-hairline) solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  object-fit: cover;
}
.urgent__photo img,
.pair img { aspect-ratio: var(--ratio-portrait); }
/* ==========================================================================
   ETW MARK
   ========================================================================== */
/* A plain label, not a box: with a border it read as a button that did
   nothing when tapped. The official ETW mark replaces it once Aram sends it. */
.etw {
  display: inline-block;
  color: var(--color-accent);
  font-family: var(--font-display);
  font-size: var(--font-size-meta);
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  font-weight: var(--font-weight-semibold);
  line-height: var(--leading-meta);
}
.theme-dark .etw { color: var(--color-accent-on-dark); }
/* ==========================================================================
   HERO
   ========================================================================== */
.hero { padding-block: var(--space-section); }
.hero__grid { display: grid; gap: var(--space-6); }
/* min-width:0 stops a grid item from being forced to the intrinsic pixel width
   of the photograph it contains. Without it the page scrolls sideways. */
.hero__grid > * { min-width: 0; }
.hero__lead {
  font-size: var(--font-size-lead);
  line-height: var(--leading-normal);
  max-width: var(--measure-narrow);
  margin-top: var(--space-4);
}
.hero h1 { margin-top: var(--space-4); }
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-6);
}
.hero__facts {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: var(--border-width-hairline) solid var(--color-line);
  display: grid;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  max-width: var(--measure);
}
@media (min-width: 840px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(280px, 380px); gap: var(--space-7); align-items: start; }
}
/* ==========================================================================
   SERVICE CARDS
   ========================================================================== */
.card-grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 560px) { .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .card-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--color-surface);
  border: var(--border-width-hairline) solid var(--color-line);
  border-radius: var(--radius-xs);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  transition: var(--transition-interactive);
}
.card__media {
  width: 100%;
  height: auto;
  aspect-ratio: var(--ratio-portrait);
  object-fit: cover;
  filter: var(--image-filter);
  border-bottom: var(--border-width-hairline) solid var(--color-line-strong);
}
/* One card per row on a phone: a portrait photo filled the whole screen per
   service, four screens for four services. Landscape halves that. */
@media (max-width: 559px) { .card__media { aspect-ratio: var(--ratio-landscape); } }
/* For a photo whose subject sits low in the frame, so the landscape crop keeps it. */
.card__media--low { object-position: 50% 85%; }
.card__body { padding: var(--space-5) var(--space-5) var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); flex: 1; }
.card__title {
  font-family: var(--font-display);
  font-size: var(--font-size-md);
  line-height: var(--leading-snug);
  font-weight: var(--font-weight-semibold);
  border-bottom: var(--border-width-strong) solid transparent;
  align-self: flex-start;
  transition: var(--transition-interactive);
}
.card__text { max-width: none; font-size: 1rem; line-height: 1.55; color: var(--color-text-muted); margin: 0; }
.card__more {
  margin-top: auto;
  padding-top: var(--space-3);
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
  color: var(--color-accent);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.card__arrow { transition: transform var(--duration-fast) var(--easing); }
.card:hover { border-color: var(--color-line-contrast); }
.card:hover .card__title { border-bottom-color: var(--color-accent); }
.card:hover .card__arrow { transform: translateX(4px); }
.card:focus-visible { border-color: var(--color-line-contrast); }
/* "Hagen, tuinaanleg en tuinonderhoud doe ik niet." Aram asked for this
   sentence himself, to stop the requests he keeps getting, and it was set in
   15px muted — the smallest type on the page. Body size and body colour now. */
.exclusion {
  margin-top: var(--space-5);
  max-width: var(--measure);
  color: var(--color-text);
}
/* ==========================================================================
   FORM
   ========================================================================== */
.field { margin-bottom: var(--space-5); max-width: 34rem; }
.field__label {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
  margin-bottom: var(--space-2);
}
.field__optional { font-weight: var(--font-weight-regular); color: var(--color-text-muted); }
.field__hint {
  display: block;
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
  line-height: 1.5;
}
.field__control {
  width: 100%;
  min-height: 52px;
  padding: var(--space-3);
  background: var(--color-surface);
  border: var(--border-width-hairline) solid var(--color-line-strong);
  border-radius: var(--radius-xs);
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--leading-normal);
  transition: var(--transition-interactive);
}
textarea.field__control { min-height: 140px; resize: vertical; }
.field__control--short { max-width: 10rem; }
.field__control::placeholder { color: var(--color-text-muted); opacity: 1; }
/* This selector (0,2,0) outranks the global :focus-visible (0,1,0), so it is
   what a keyboard user actually gets. It used to paint the ring in the accent
   green — 5.40:1 on paper, the weakest ring on the site — while every other
   focusable element got near-black at 15.40:1. Same token as everywhere else
   now; the border change is what marks the field itself as active. */
.field__control:focus {
  border-color: var(--color-text);
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: 1px;
}
.field--error .field__control {
  border-width: var(--border-width-strong);
  border-color: var(--color-text-error);
}
.field__error {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--font-size-sm);
  line-height: 1.5;
  color: var(--color-text-error);
  font-weight: var(--font-weight-semibold);
}
.form__footnote { font-size: var(--font-size-sm); color: var(--color-text-muted); margin-top: var(--space-3); }
/* Filled in only by bots that fill in every field. Off-screen rather than
   display:none, which some bots know to skip. */
.form__trap {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
/* Empty until a send fails; role="alert" announces whatever lands in it. */
.form__alert:not(:empty) {
  margin-bottom: var(--space-5);
  max-width: 34rem;
  padding-left: var(--space-3);
  border-left: var(--border-width-strong) solid var(--color-text-error);
  color: var(--color-text-error);
  font-weight: var(--font-weight-semibold);
  line-height: 1.5;
}
/* ==========================================================================
   DARK BAND + FOOTER
   ========================================================================== */
.theme-dark {
  background: var(--color-surface-dark);
  color: var(--color-text-on-dark);
  border-top: var(--border-width-band) solid var(--color-accent-graphic);
}
.theme-dark .meta { color: var(--color-text-muted-on-dark); }
.theme-dark a { color: var(--color-accent-on-dark); }
.theme-dark a:hover { color: var(--color-text-on-dark); }
.site-footer { padding-block: var(--space-7); }
.site-footer__logo { height: 44px; width: auto; margin-bottom: var(--space-6); }
.site-footer__cols { display: grid; gap: var(--space-6); }
.site-footer__cols > * { min-width: 0; }
/* Four columns at 720px left each one 149px — too narrow for the longest link
   at the type size below, which wants 177px. Two columns carry it from 560px,
   four only once there is room: at 1000px each column is 217px. */
@media (min-width: 560px) {
  .site-footer__cols {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-6) var(--space-5);
  }
}
@media (min-width: 1000px) {
  .site-footer__cols { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-5); }
}
.site-footer h2 {
  font-size: var(--font-size-meta);
  line-height: var(--leading-meta);
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-muted-on-dark);
  margin-bottom: var(--space-3);
}
.site-footer ul { display: grid; gap: var(--space-2); list-style: none; padding: 0; }
/* huisstijl.md 2.2 is unambiguous: running text never below 18px and never
   below a line height of 1.65. The footer sat at 16px/1.6 in the lightest text
   colour on the site — the one place an audience of 35 to 70 is asked to read
   an address and a VAT number. The column count above makes the room for it. */
.site-footer li, .site-footer p {
  font-size: var(--font-size-base);
  line-height: var(--leading-body);
}
.site-footer p { color: var(--color-text-muted-on-dark); }
/* White instead of the accent green. This band was the greenest screen on the
   site — ten green spots against the two that huisstijl.md 2.1 allows — and
   white is the better read anyway: 16.92:1 against the dark base where the
   green reaches 7.74. Green stays for the 4px rule above the band and the ETW
   mark, which is what makes it a signal. */
.site-footer a {
  color: var(--color-text-on-dark);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.site-footer a:hover { text-decoration-thickness: 2px; }
/* Footer links measured 22.78px tall: under the 24px of WCAG 2.2 SC 2.5.8, and
   far under the 48px huisstijl.md 2.3 asks for. A pointing device only needs
   enough padding to clear the criterion; a touch screen gets the full 48px,
   which is where mis-taps actually happen. */
.site-footer li a { display: inline-block; padding-block: var(--space-1); }
@media (pointer: coarse) {
  .site-footer ul { gap: 0; }
  .site-footer li a {
    display: flex;
    align-items: center;
    min-height: var(--tap-target-min);
  }
}
.site-footer__legal {
  margin-top: var(--space-7);
  padding-top: var(--space-4);
  border-top: var(--border-width-hairline) solid var(--color-line-on-dark);
  font-size: var(--font-size-xs);
  color: var(--color-text-muted-on-dark);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

/* ==========================================================================
   PAGE COMPONENTS — added for the homepage on top of the specimen styles.
   ========================================================================== */

/* --- section heading ------------------------------------------------------ */
.section__head { margin-bottom: var(--space-6); }
.section__head .meta { display: block; margin-bottom: var(--space-2); }
.section__head p { margin-top: var(--space-3); color: var(--color-text-muted); }

/* --- urgent band: storm damage -------------------------------------------- */
.urgent {
  background: var(--color-surface-quiet);
  border-block: var(--border-width-hairline) solid var(--color-line);
  padding-block: var(--space-7);
}
/* On a phone the photo used to sit between the sentence and the phone number,
   and at 4:5 that is 437px of tall image in between — the number ended up 661px
   below the top of its own band. Capping the photo at 200px fixed the distance
   and broke the picture: 200px in a 349px column is 51% of the width with 169px
   of empty paper beside it. Reordering solves both. Someone with a tree on
   their driveway reads the sentence, then the number; the photo follows as
   evidence and keeps the full width it needs. */
.urgent__grid {
  display: grid;
  gap: var(--space-5);
  align-items: center;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "text"
    "tel"
    "photo";
}
.urgent__grid > div { grid-area: text; }
.urgent__photo { grid-area: photo; }
@media (min-width: 720px) {
  .urgent__grid {
    grid-template-columns: 200px minmax(0, 1fr) auto;
    grid-template-areas: "photo text tel";
  }
}
.urgent__grid > .urgent__contact { grid-area: tel; }
.urgent__contact { display: grid; gap: var(--space-3); justify-items: start; }
.urgent__photo { margin: 0; }
.urgent__title { font-size: var(--font-size-lg); }
.urgent__text { margin-top: var(--space-2); color: var(--color-text-muted); }
.urgent__tel {
  font-family: var(--font-display);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
}

/* --- numbered steps ------------------------------------------------------- */
.steps {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-6);
  counter-reset: step;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 840px) { .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-5); } }
/* Four things were wrong in this one component: the step number sat at
   --font-size-md (21px) above a heading at --font-size-base (19px), so the
   numeral outranked the words; the space above and below the heading was 8px
   either side instead of the 2:1 huisstijl.md 2.3 asks for; the rule on top was
   2px --color-line-strong, heavier than the 1px --color-line that divides whole
   sections; and three green numerals put this screen over the two green spots
   huisstijl.md 2.1 allows. The number is a label now, styled like every other
   label on the site, and the heading takes the rank it should have. */
.steps li {
  counter-increment: step;
  padding-top: var(--space-4);
  border-top: var(--border-width-hairline) solid var(--color-line-strong);
}
.steps li::before {
  content: "Stap " counter(step);
  display: block;
  font-family: var(--font-display);
  font-size: var(--font-size-meta);
  line-height: var(--leading-meta);
  letter-spacing: var(--tracking-meta);
  text-transform: uppercase;
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}
.steps h3 { font-size: var(--font-size-md); margin-bottom: var(--space-3); }
.steps p { color: var(--color-text-muted); }

/* --- about ---------------------------------------------------------------- */
/* Three columns on a wide screen: the section heading, the running text at its
   measure, and the portrait. Earlier attempts at this block each fixed one thing
   and broke another — the portrait in the left column pushed the text off the
   96px spine; sizing the text at 1fr parked the portrait against the outer edge
   and opened a 459px hole in the middle; sizing it at the measure closed the
   hole but left the whole row at 67% of the band. Taking the heading into the
   row solves all three at once: 363.5 + 500.5 + 288 plus two gaps is exactly the
   1248px band at a 1440px viewport.
   The columns are placed explicitly, so the markup keeps its reading order —
   heading, portrait with its caption, then the text — which is also the order it
   stacks in below 840px. */
.about__grid { display: grid; gap: var(--space-6); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 840px) {
  .about__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, var(--measure)) 288px;
    gap: var(--space-7);
    align-items: start;
  }
  .about__grid > .section__head { grid-area: 1 / 1; margin-bottom: 0; }
  .about__text     { grid-area: 1 / 2; }
  .about__portrait { grid-area: 1 / 3; }
}
/* A fixed frame, like every other photograph on the site (huisstijl.md 2.4
   point 1). Point 12 asks for a square frame here, but that rule was written
   for the portrait of Aram that does not exist yet — this is a stand-in work
   photo of a climber on a stem, and a square centre crop would cut both head
   and feet. Square it once the real portrait arrives. */
.about__portrait { margin: 0; max-width: 288px; }
.about__portrait img { aspect-ratio: var(--ratio-portrait); }
.about p + p { margin-top: var(--space-4); }

/* --- dark collaboration band ---------------------------------------------- */
/* Two faults here. The eyebrow, the heading and the lead all had margin 0, so
   the three ran together — the open point on this sat in todo.md. And on a
   1440px screen the band was 46% empty: a filled dark surface shows its own
   emptiness in a way paper never does, so the text now uses the width in two
   columns, heading left and argument right, instead of one 500px stripe. */
.together { padding-block: var(--space-section); }
.together .meta { display: block; margin-bottom: var(--space-2); }
.together h2 { max-width: var(--measure-narrow); }
.together p { color: var(--color-text-on-dark); }
.together .lead { margin-top: var(--space-4); max-width: var(--measure); }
.together p + p { margin-top: var(--space-4); }
@media (min-width: 840px) {
  .together .container {
    display: grid;
    grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
    gap: var(--space-5) var(--space-7);
    align-items: start;
  }
  .together .meta  { grid-area: 1 / 1; margin-bottom: 0; }
  .together h2     { grid-area: 2 / 1; }
  .together .lead  { grid-area: 1 / 2; margin-top: 0; }
  .together p + p  { grid-area: 2 / 2; margin-top: 0; }
}

/* --- form ----------------------------------------------------------------- */
/* The form had four different right edges: 1180px for the two-column row,
   585px for the intro paragraph, 544px for the fields and 127px for the submit
   button — a spread of 636px, with 92px of dead space between two fields in the
   same row where the gap should have been 24px. Two columns and a single right
   edge cannot both exist while huisstijl.md 2.5 caps a field at 34rem, so the
   two-column row goes and every field lines up in one 34rem column. That is
   also the better form: one edge to scan down, labels and fields in a single
   rhythm, and no field narrow enough to make its own hint wrap to three lines. */
.form { max-width: 34rem; }
.form__intro { margin-bottom: var(--space-6); color: var(--color-text-muted); }

/* --- long compound words -------------------------------------------------- */
/* Dutch compounds like "boomveiligheidscontrole" are wider than a card column.
   hyphenate-limit-chars keeps breaks to words that genuinely need one and
   leaves readable fragments either side: 12 characters before a word may break
   at all, 5 before the hyphen and 4 after.
   Headings drop back to manual from 840px up, where none of them needs a break
   — the longest, "Boomveiligheidscontrole", measures 511px in an 820px column.
   h3 keeps automatic hyphenation, because card titles stay narrow at every
   width. */
body { overflow-wrap: break-word; }
h1, h2, h3 {
  -webkit-hyphens: auto;
  hyphens: auto;
  -webkit-hyphenate-limit-chars: 12 5 4;
  hyphenate-limit-chars: 12 5 4;
}
@media (min-width: 840px) {
  h1, h2 { -webkit-hyphens: manual; hyphens: manual; }
}

/* --- service page body ---------------------------------------------------- */
/* A service page held three stacked blocks of about 576px in a 1248px field,
   which left the right half of every screen empty. The explanation and the
   "when do you need this" list are now one row: what it is on the left, when
   you need it on the right. Below 840px they stack in the same reading order. */
.service-body {
  display: grid;
  gap: var(--space-7);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 840px) {
  .service-body {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-8);
    align-items: start;
  }
}
.service-body__when .section__head { margin-bottom: var(--space-5); }

/* --- split block: the heading beside its content -------------------------- */
/* Some blocks hold content that is narrower than the band by nature: a form
   capped at 34rem by huisstijl.md 2.5, a pair of photo frames deliberately kept
   small so the hero stays the largest image, a portrait next to a column of
   text. Stacked under a full-width heading, those rows used a little over half
   the page. Measured on the homepage before this: "Voor en na" reached 57.7% of
   the band with 528px empty at the right, "Over Aram" 67.0% with 411.5px, while
   the card grid and the steps sat at 99.9% and 98.1%. Putting the heading beside
   the content instead fills the row and gives the page one arrangement rather
   than one per section. Blocks whose content already fills the band keep their
   heading above — that is the rule: heading above when the content is as wide as
   the page, beside it when it is not. */
.split {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: minmax(0, 1fr);
}
.offer        { --split-content: 34rem; }
.split--pair  { --split-content: var(--image-max-portrait); }
@media (min-width: 840px) {
  .split {
    grid-template-columns: minmax(0, 1fr) minmax(0, var(--split-content));
    gap: var(--space-8);
    align-items: start;
  }
  .split > .section__head { margin-bottom: 0; }
}

/* --- checklist ------------------------------------------------------------ */
.checklist { list-style: none; padding: 0; margin: 0; }
.checklist li {
  position: relative;
  padding-left: var(--space-5);
  padding-block: var(--space-3);
  border-top: var(--border-width-hairline) solid var(--color-line);
  max-width: var(--measure);
}
.checklist li:last-child { border-bottom: var(--border-width-hairline) solid var(--color-line); }
/* On the service pages these items are statements, but on 404.html each one is
   a link and the list is the only way out of that page. Same treatment as the
   footer links: 26px clears WCAG 2.2 SC 2.5.8 on a mouse, a touch screen gets
   the 48px huisstijl.md 2.3 asks for. */
@media (pointer: coarse) {
  .checklist li:has(> a:only-child) { padding-block: 0; }
  .checklist a {
    display: flex;
    align-items: center;
    min-height: var(--tap-target-min);
  }
}
.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--space-3) + 0.6em);
  width: var(--space-3);
  height: var(--border-width-strong);
  background: var(--color-accent);
}
/* Paragraph spacing for running text, wherever it sits. This used to hang on
   .container--narrow alone, so moving the service page body into .service-body
   silently ran its three paragraphs together into one block. Bound to the
   blocks that actually carry running text now. */
.container--narrow p + p,
.service-body__text p + p { margin-top: var(--space-4); }

/* --- before / after pair -------------------------------------------------- */
/* Each frame rendered at 612px against a hero of 380px, which made the
   before/after pair the largest image on the page and pushed the photo share to
   19.8% against the 8% huisstijl.md 2.1 budgets. Capped at --image-max-portrait
   the pair is 720px, so each frame is about 348px — the same order as the 294px
   card media, which is what makes a page read as one set of images. */
.pair {
  display: grid;
  gap: var(--space-5);
  /* Side by side at every width: stacked, "voor" and "na" were a full screen
     apart on a phone and could not be compared. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: var(--image-max-portrait);
}
@media (max-width: 559px) { .pair { gap: var(--space-3); } }
.pairs { display: grid; gap: var(--space-7); }
.pair__label { margin: 0 0 var(--space-3); }
.pair figure { margin: 0; }
.pair figcaption { margin-top: var(--space-2); }

/* ==========================================================================
   FORCED COLOURS (Windows high contrast, and the same mode on other systems)
   The browser throws away every colour here and substitutes the user's own
   palette. Anything that carried meaning through colour alone has to be redrawn
   with a system colour keyword or it simply vanishes.
   ========================================================================== */
@media (forced-colors: active) {
  /* The filled button loses its green and its transparent border at once,
     leaving unmarked text. */
  .btn--primary { border-color: ButtonBorder; }
  /* Boundaries that separate one surface from another. */
  .site-header { border-bottom-color: CanvasText; }
  .card,
  .frame__media,
  .card__media,
  .urgent__photo img,
  .pair img,
  .about__portrait img { border-color: CanvasText; }
  .theme-dark { border-top-color: CanvasText; }
  /* Painted shapes, not text: these are backgrounds and would disappear. */
  .checklist li::before { background: CanvasText; }
  /* The focus ring must use the system highlight to stay visible. */
  :focus-visible { outline-color: Highlight; }
  .theme-dark :focus-visible { outline-color: Highlight; }
}
