/* ==========================================================================
   Emotics - one stylesheet, no build step.

   Everything is driven by the custom properties in "Tokens" below, which are a
   direct transcription of DESIGN.md §3-5. Changing a brand colour is one edit
   here and nothing else (DESIGN.md hard constraint 5).

   Contents
     1. Fonts          self-hosted WOFF2, latin + latin-ext
     2. Tokens         colour (light + dark), type, space, radius, layout
     3. Reset & base
     4. Typography
     5. Focus & skip link
     6. Layout
     7. Header
     8. Footer
     9. Buttons
    10. The trail
    11. Home
    12. Reading pages
    13. Project pages
    14. Portfolio cards
    15. Contact form
    16. The shared dialog
    17. Utilities
    18. Reduced motion
   ========================================================================== */

/* ==========================================================================
   1. Fonts
   Self-hosted from /wwwroot/fonts. No CDN, no @import, no Google Fonts link:
   a rendered page must make zero external requests (DESIGN.md constraint 1).
   Licences ship alongside the files as OFL-*.txt.

   The ?v= is the Fontsource release these files came from, and it is the only
   cache-buster they have: asp-append-version stamps the stylesheet but cannot
   reach URLs inside it, so without this a returning visitor keeps their cached
   copy of a replaced font forever. Bump it whenever fonts/ changes, and keep it
   in step with fonts/SOURCES.md.
   ========================================================================== */

@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/newsreader-400.woff2?v=5.3.0") format("woff2");
  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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/newsreader-400-ext.woff2?v=5.3.0") format("woff2");
  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: "Newsreader";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/newsreader-500.woff2?v=5.3.0") format("woff2");
  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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/newsreader-500-ext.woff2?v=5.3.0") format("woff2");
  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: "Karla";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/karla-400.woff2?v=5.3.0") format("woff2");
  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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Karla";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/karla-400-ext.woff2?v=5.3.0") format("woff2");
  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: "Karla";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/karla-500.woff2?v=5.3.0") format("woff2");
  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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Karla";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/karla-500-ext.woff2?v=5.3.0") format("woff2");
  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: "Karla";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/karla-700.woff2?v=5.3.0") format("woff2");
  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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Karla";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/karla-700-ext.woff2?v=5.3.0") format("woff2");
  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: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/jbmono-400.woff2?v=5.3.0") format("woff2");
  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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/jbmono-400-ext.woff2?v=5.3.0") format("woff2");
  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: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/jbmono-500.woff2?v=5.3.0") format("woff2");
  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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/jbmono-500-ext.woff2?v=5.3.0") format("woff2");
  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;
}

/* ==========================================================================
   2. Tokens - DESIGN.md §3 (colour), §4 (type), §5 (layout)
   ========================================================================== */

:root {
  /* -- Colour: light theme (default). Ratios are measured, see DESIGN.md §3. -- */
  --paper: #F2F4EE;          /* page background - cool green-grey paper        */
  --card: #FBFCF8;           /* raised surface                                 */
  --sunken: #E8EBE2;         /* recessed blocks, code, quotes                  */

  --ink: #1C231D;            /* primary text            14.49:1 on paper       */
  --muted: #4E5850;          /* secondary text           6.68:1 on paper       */

  --pine: #1E4D38;           /* primary / RHike          8.72:1 on paper       */
  --berry: #8A2F3B;          /* accent / Eventra         7.43:1 on paper       */
  --night: #2C4470;          /* Whisperwood              8.74:1 on paper       */

  --border: #D3D8CA;         /* decorative dividers only                       */
  --border-strong: #7E8A78;  /* form fields, focus       3.27:1 on paper       */
  --on-fill: #FBFCF8;        /* text on pine/berry fills - 9.38:1 / 7.99:1     */

  /* Backdrop for modal surfaces - the one place a non-token colour is allowed,
     because it is the pine ink at 45% and not a new hue. */
  --backdrop: rgba(28, 35, 29, 0.45);

  /* -- Which of the paired theme icons is showing. Flipped in the dark block
        below so the toggle never needs a duplicated rule per theme. -- */
  --icon-sun-display: none;
  --icon-moon-display: inline-block;

  /* -- Type: DESIGN.md §4. No fixed pixel font sizes anywhere on the site. -- */
  --font-display: "Newsreader", Georgia, serif;
  --font-body: "Karla", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;

  --step--1: clamp(0.83rem, 0.80rem + 0.15vw, 0.89rem);
  --step-0: clamp(1.00rem, 0.95rem + 0.25vw, 1.13rem);
  --step-1: clamp(1.25rem, 1.15rem + 0.50vw, 1.50rem);
  --step-2: clamp(1.56rem, 1.35rem + 1.05vw, 2.00rem);
  --step-3: clamp(1.95rem, 1.60rem + 1.75vw, 2.66rem);
  --step-4: clamp(2.44rem, 1.85rem + 2.95vw, 3.55rem);

  /* -- Space, radius, measure: DESIGN.md §5 -- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;

  --radius-sm: 3px;
  --radius-md: 6px;
  --radius-lg: 10px;

  --measure: 68ch;
  --content-max: 1140px;

  /* -- The trail (handoff §"Site-wide construction"). The route enters at
        --rail, swings out to --rail-side for reading stretches, and returns to
        --rail at the exit. Both collapse to 8% below 820px, where the wide and
        narrow SVG paths swap over. --trail-colour is overridden per project
        page; focus rings stay --pine everywhere. -- */
  --rail: 50%;
  --rail-side: 9%;
  --trail-colour: var(--pine);
  --trail-width: 2.5px;

  /* -- Motion: fades and colour only, 150-200ms (DESIGN.md §9) -- */
  --transition: 180ms ease;

  --touch-target: 44px;
  --header-height: 64px;
}

/* Dark theme. Not an inversion: surfaces get lighter as they rise and the brand
   colours are desaturated so they do not vibrate (DESIGN.md §3).

   Declared twice on purpose - once for visitors who have never touched the
   toggle (system preference), once for the explicit choice stored in
   localStorage. :not([data-theme="light"]) lets an explicit "light" win over a
   dark system setting. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #141813;
    --card: #1D231C;
    --sunken: #0F120E;

    --ink: #E3E8DE;          /* not pure white          14.42:1 on paper       */
    --muted: #A3AC9E;        /*                          7.65:1 on paper       */

    --pine: #8CC4A2;         /*                          9.00:1 on paper       */
    --berry: #E8949C;        /*                          7.82:1 on paper       */
    --night: #9BB4E0;        /*                          8.55:1 on paper       */

    --border: #2E362C;
    --border-strong: #646E61; /*                         3.37:1 on paper       */
    --on-fill: #141813;      /* dark text on light fills - 9.00:1 / 7.82:1     */

    --icon-sun-display: inline-block;
    --icon-moon-display: none;
  }
}

:root[data-theme="dark"] {
  --paper: #141813;
  --card: #1D231C;
  --sunken: #0F120E;

  --ink: #E3E8DE;
  --muted: #A3AC9E;

  --pine: #8CC4A2;
  --berry: #E8949C;
  --night: #9BB4E0;

  --border: #2E362C;
  --border-strong: #646E61;
  --on-fill: #141813;

  --icon-sun-display: inline-block;
  --icon-moon-display: none;
}

/* ==========================================================================
   3. Reset & base
   ========================================================================== */

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

/* No font-size on <html>: the visitor's own browser setting is the base, and
   every size on the site is a rem or a clamp() derived from it. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  transition: background-color var(--transition), color var(--transition);
  /* Nothing may push the page sideways at any width (DESIGN.md §11). */
  overflow-x: hidden;
}

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

/* ==========================================================================
   4. Typography
   ========================================================================== */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-4);
  text-wrap: balance;
}

h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-2); }
h3 { font-size: var(--step-1); }

p {
  margin: 0 0 var(--space-4);
  text-wrap: pretty;
}

a {
  color: var(--berry);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  transition: color var(--transition);
}

a:hover {
  color: var(--pine);
}

/* Mono utility labels - status, tech lines, marked gaps. The one place ALL CAPS
   is allowed (DESIGN.md §4). */
.mono-label {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500;
}

/* A fact the handoff has not supplied yet. Rendered, never guessed - see the
   "Marked gaps" list in the handoff README. */
/* No white-space: nowrap here. A long gap such as
   "[ datum volgt bij publicatie ]" is wider than a 320px column, and forcing it
   onto one line pushed the whole page sideways. */
.gap {
  font-family: var(--font-mono);
  font-size: 0.9em;
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* ==========================================================================
   5. Focus & skip link
   Every interactive element keeps a visible ring. Never outline: none.
   ========================================================================== */

:focus-visible {
  outline: 2px solid var(--pine);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  background: var(--pine);
  color: var(--on-fill);
  font-weight: 500;
  text-decoration: none;
  border-radius: var(--radius-md);
  /* Off-screen until focused, but never display:none - it must stay the first
     focusable element on the page. */
  transform: translateY(calc(-100% - var(--space-5)));
  transition: transform var(--transition);
}

.skip-link:focus-visible {
  transform: translateY(0);
  color: var(--on-fill);
  outline-offset: 3px;
}

/* ==========================================================================
   6. Layout
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-5);
}

main {
  display: block;
}

/* Vertical rhythm between major sections: --space-9 on desktop, --space-7 on
   mobile (DESIGN.md §5). */
.section {
  padding-block: var(--space-9);
}

.page-notice {
  padding-block: var(--space-9);
  max-width: var(--measure);
}

@media (max-width: 639px) {
  .section,
  .page-notice {
    padding-block: var(--space-7);
  }
}

/* ==========================================================================
   7. Header
   ========================================================================== */

.site-header {
  position: relative; /* containing block for the mobile menu panel */
  background: var(--paper);
  border-bottom: 1px solid var(--border);
  transition: background-color var(--transition), border-color var(--transition);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  min-height: var(--header-height);
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-5);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-inline-end: auto;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.35rem);
  color: var(--ink);
  text-decoration: none;
}

.wordmark__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--pine);
  flex: none;
}

.site-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav__list a {
  display: inline-block;
  color: var(--ink);
  text-decoration: none;
  padding-block: var(--space-2);
  border-bottom: 2px solid transparent;
}

.site-nav__list a:hover {
  color: var(--pine);
}

.site-nav__list a[aria-current="page"] {
  color: var(--pine);
  font-weight: 700;
  border-bottom-color: var(--pine);
}

.site-header__tools {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.lang-link {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  text-decoration: none;
  /* Small text, but still a full touch target. */
  min-width: var(--touch-target);
  min-height: var(--touch-target);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.lang-link:hover {
  color: var(--pine);
}

.icon {
  width: 20px;
  height: 20px;
  flex: none;
}

.theme-toggle,
.site-menu__toggle {
  width: var(--touch-target);
  height: var(--touch-target);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: color var(--transition), border-color var(--transition);
}

.theme-toggle:hover,
.site-menu__toggle:hover {
  color: var(--pine);
  border-color: var(--pine);
}

.icon--sun { display: var(--icon-sun-display); }
.icon--moon { display: var(--icon-moon-display); }

/* The toggle only does anything with JavaScript, so it is not offered without
   it - an inert control is worse than an absent one. */
.no-js .theme-toggle {
  display: none;
}

/* -- Mobile menu: <details>/<summary>, works with no script at all ---------- */

.site-menu__toggle {
  list-style: none; /* hide the default disclosure triangle */
}

.site-menu__toggle::-webkit-details-marker {
  display: none;
}

.site-menu[open] .icon--menu { display: none; }
.site-menu:not([open]) .icon--close { display: none; }

.site-menu__panel,
.site-menu > .site-nav {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: var(--paper);
  border-bottom: 1px solid var(--border);
  padding: var(--space-2) var(--space-5) var(--space-4);
  z-index: 50;
}

.site-nav--panel .site-nav__list {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
}

.site-nav--panel .site-nav__list a {
  display: flex;
  align-items: center;
  min-height: var(--touch-target);
  padding-block: var(--space-2);
  border-bottom: 1px solid var(--border);
}

.site-nav--panel .site-nav__list a[aria-current="page"] {
  border-bottom-color: var(--pine);
}

.site-nav--panel .site-nav__list li:last-child a {
  border-bottom: none;
}

/* Below 640px the inline nav gives way to the hamburger; the language link and
   theme toggle stay visible next to it (handoff §Header). */
.site-nav--inline {
  display: none;
}

@media (min-width: 640px) {
  .site-nav--inline {
    display: block;
  }

  .site-menu {
    display: none;
  }
}

/* Sticky on desktop, static on mobile (DESIGN.md §7). */
@media (min-width: 1024px) {
  .site-header {
    position: sticky;
    top: 0;
    z-index: 40;
  }
}

/* ==========================================================================
   8. Footer
   ========================================================================== */

/* No margin above the footer: the trail has to arrive at the footer's top edge
   with no gap, or the route visibly breaks. Each page brings its own closing
   space instead. */
.site-footer {
  position: relative; /* so an optional trail rail can run through it */
  border-top: 1px solid var(--border);
  padding-block: 2.5rem;
  color: var(--muted);
  font-size: 0.875rem;
}

.site-footer__details,
.site-footer__links {
  /* Give the text its own ground where the rail passes behind it. */
  position: relative;
  background: var(--paper);
  transition: background-color var(--transition);
}

.site-footer__inner {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  align-items: flex-start;
  justify-content: space-between;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-5);
}

.site-footer p {
  margin: 0 0 var(--space-1);
}

.site-footer__credits {
  margin-top: var(--space-2);
}

.site-footer a {
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.site-footer a:hover {
  color: var(--pine);
}

.site-footer a[aria-current="page"] {
  color: var(--pine);
  font-weight: 700;
}

/* ==========================================================================
   9. Buttons - DESIGN.md §7
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-target);
  padding-inline: var(--space-5);
  border-radius: var(--radius-md);
  font-weight: 500;
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
  border: 1px solid transparent;
  transition: background-color var(--transition), border-color var(--transition), color var(--transition);
}

.btn--primary {
  background: var(--pine);
  color: var(--on-fill);
}

.btn--primary:hover {
  color: var(--on-fill);
  background: var(--ink);
}

.btn--secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--border-strong);
}

.btn--secondary:hover {
  color: var(--ink);
  border-color: var(--ink);
}

.btn-row {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-6);
}

/* ==========================================================================
   10. The trail - handoff §"Site-wide construction", DESIGN.md §6

   One continuous route line runs down every page: it enters at the top edge and
   leaves at the bottom, so the whole site reads as a single walk. Two kinds of
   piece make it up, and they meet because both are anchored to the same --rail:

     - straight runs are a 2.5px <div> at left:var(--rail), pulled back by half
       its own width;
     - curves are their own SVG on a 0..100 viewBox with preserveAspectRatio
       "none", so the box stretches to whatever the section is while
       vector-effect keeps the stroke at a true 2.5px.

   Each curve SVG carries TWO paths. The wide one starts and ends at x=50, the
   narrow one at x=8 - the same values --rail takes above and below 820px - so
   whichever is showing lines up with the straight runs at every width. They are
   swapped by media query alone; nothing measures anything in JavaScript.
   ========================================================================== */

.trail-curve {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
  pointer-events: none;
}

.route-narrow {
  display: none;
}

.rail {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--rail);
  margin-left: -1.25px;
  width: var(--trail-width);
  background: var(--trail-colour);
  pointer-events: none;
}

/* The reading pages swing the route out of the text column and back again. */
.rail--side {
  left: var(--rail-side);
}

/* Crossing the pine band the line stays visible by lightening, not by changing
   hue - there is no separate token for a line on a filled surface. */
.rail--on-fill {
  background: var(--on-fill);
  opacity: 0.55;
}

.route-start {
  position: absolute;
  top: 20px;
  left: var(--rail);
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  pointer-events: none;
}

.route-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--trail-colour);
  flex: none;
}

.route-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1;
  letter-spacing: 0.1em;
  color: var(--trail-colour);
}

@keyframes trail-reveal {
  from { clip-path: inset(0 0 100% 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* Drawn once on load, top to bottom. Never on scroll: it is a map, not an
   effect (DESIGN.md §6). */
.route-anim {
  clip-path: inset(0 0 0 0);
  animation: trail-reveal 2.8s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  /* clip-path must go to none, not stay at its start value - switching the
     animation off has to leave the route VISIBLE, not clipped away to nothing. */
  .route-anim {
    clip-path: none;
    animation: none;
  }
}

/* Below 820px the route moves off the centre line to an 8% margin rail, and the
   narrow paths take over from the wide ones. */
@media (max-width: 820px) {
  :root {
    --rail: 8%;
    --rail-side: 8%;
  }

  .route-wide { display: none; }
  .route-narrow { display: block; }

  .route-start {
    transform: none;
    margin-left: -7px;
  }
}

/* ==========================================================================
   11. Home
   ========================================================================== */

/* -- Hero: contour map, route and the one big statement ------------------- */

.hero {
  position: relative;
  overflow: hidden;
}

.hero__contours {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

.hero__inner {
  position: relative;
  z-index: 1;
  max-width: var(--content-max);
  margin-inline: auto;
  padding: clamp(4rem, 2rem + 8vw, 8.125rem) var(--space-5) clamp(5rem, 2rem + 10vw, 10.625rem);
}

.hero h1 {
  max-width: 19ch;
  font-size: clamp(2.1rem, 1.3rem + 3.4vw, 3.6rem);
  margin: 0;
}

.hero__lede {
  max-width: 40ch;
  color: var(--muted);
  margin: var(--space-5) 0 0;
  font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.35rem);
  line-height: 1.5;
}

@media (max-width: 520px) {
  .hero h1 { max-width: none; }
}

/* -- Waypoints: the three products, alternating across the rail ------------ */

.waypoints {
  position: relative;
  padding-bottom: var(--space-6);
}

.waypoints__list {
  max-width: var(--content-max);
  margin-inline: auto;
  padding: clamp(1.5rem, 5vw, 3rem) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: 5.5rem;
}

.wp {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 96px 1fr;
  align-items: center;
}

.wp__dot {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--pine);
  /* The ring is the page colour, so the dot reads as sitting ON the line. */
  border: 3px solid var(--paper);
}

.wp__stub {
  grid-column: 2;
  grid-row: 1;
  align-self: center;
  width: 48px;
  height: var(--trail-width);
  background: var(--pine);
}

.wp--left .wp__stub {
  justify-self: start;
  transform: translateX(-46px);
}

.wp--right .wp__stub {
  justify-self: end;
  transform: translateX(46px);
}

.wp__link {
  grid-row: 1;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 440px;
  text-decoration: none;
  color: var(--ink);
  border-radius: var(--radius-md);
}

.wp--left .wp__link {
  grid-column: 1;
  justify-self: end;
  padding-right: var(--space-5);
}

.wp--right .wp__link {
  grid-column: 3;
  justify-self: start;
  padding-left: var(--space-5);
}

.wp__link:focus-visible {
  outline-offset: 6px;
}

.wp__visual {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--card);
}

.wp__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* The tag carries the product's own colour - the status is spelled out in the
   text as well, so colour is never the only signal (DESIGN.md §3). */
.wp__tag {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.wp__tag--pine { color: var(--pine); }
.wp__tag--berry { color: var(--berry); }
.wp__tag--night { color: var(--night); }

.wp__name {
  font-size: clamp(1.35rem, 1.15rem + 0.7vw, 1.6rem);
  margin: 0;
  transition: color var(--transition);
}

.wp__link:hover .wp__name {
  color: var(--pine);
}

.wp__line {
  margin: 0;
  color: var(--muted);
}

.wp__deco {
  position: absolute;
  left: -40px;
  top: -30px;
  width: 300px;
  height: 200px;
  opacity: 0.5;
  pointer-events: none;
}

@media (max-width: 820px) {
  .waypoints__list {
    padding-inline: 0;
  }

  .wp {
    grid-template-columns: 1fr;
    gap: 18px;
    padding-left: calc(8% + 30px);
    padding-right: var(--space-5);
  }

  .wp__link {
    grid-column: 1;
    justify-self: stretch;
    padding-inline: 0;
    max-width: none;
  }

  /* The dot leaves the grid and sits directly on the 8% rail. */
  .wp__dot {
    position: absolute;
    grid-column: auto;
    grid-row: auto;
    justify-self: auto;
    left: var(--rail);
    top: 6px;
    transform: translateX(-50%);
  }

  .wp__stub,
  .wp__deco {
    display: none;
  }
}

/* -- Pine band: the page's single focal point ------------------------------ */

.band {
  position: relative;
  background: var(--pine);
  color: var(--on-fill);
  overflow: hidden;
  transition: background-color var(--transition), color var(--transition);
}

.band__inner {
  position: relative;
  max-width: var(--content-max);
  margin-inline: auto;
  padding: clamp(3rem, 8vw, 7.5rem) var(--space-5);
}

.band h2 {
  font-size: clamp(2.6rem, 1.2rem + 7vw, 7rem);
  line-height: 1.02;
  max-width: 9ch;
  color: var(--on-fill);
  margin: 0;
}

.band__lede {
  margin: 1.25rem 0 0;
  font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.35rem);
  max-width: 40ch;
  opacity: 0.82;
}

.band__principles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-7);
  margin-top: var(--space-9);
}

.band__principle {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.band__principle span {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.band__principle p {
  margin: 0;
  opacity: 0.82;
}

@media (max-width: 820px) {
  .band__inner {
    padding-left: calc(8% + 30px);
  }

  .band__principles {
    grid-template-columns: 1fr;
    gap: var(--space-4);
    margin-top: var(--space-7);
  }
}

/* -- Outro: the quiet contact line, then the trail simply ends ------------- */

.outro {
  position: relative;
}

.outro .rail {
  bottom: auto;
  height: 120px;
}

.outro__col {
  position: relative;
  z-index: 1;
  max-width: var(--content-max);
  margin-inline: auto;
  padding: var(--space-7) var(--space-5) 0;
}

.outro__col p {
  margin: 0;
  max-width: 38ch;
  color: var(--muted);
  /* Sits over the descending rail, so it needs its own ground. */
  background: var(--paper);
  transition: background-color var(--transition);
}

.trail-end {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 3.5rem var(--space-5) 2.5rem;
}

.trail-end__dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--pine);
}

.trail-end__label {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

@media (max-width: 820px) {
  .outro__col {
    padding-left: calc(8% + 30px);
  }
}

/* ==========================================================================
   12. Reading pages - /over and /privacy

   One column, no cards, no icons. The route enters at --rail, swings out to
   --rail-side to run alongside the text for the whole reading stretch, and
   returns to --rail at the exit so the footer picks it up in the middle again.
   ========================================================================== */

.read-col {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--space-5);
}

.read-hero {
  position: relative;
  overflow: hidden;
}

.read-hero .read-col {
  position: relative;
  z-index: 1;
  padding-block: clamp(6rem, 3rem + 8vw, 10.625rem) clamp(3rem, 2rem + 3vw, 5.625rem);
}

.read-hero h1 {
  margin: 0;
}

/* Quieter than the home hero: contours sit behind the title only. */
.read-hero__contours {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

/* /privacy is the plainest page: smaller title, tighter hero, no contours. */
.read-hero--compact .read-col {
  padding-block: clamp(4.5rem, 2rem + 7vw, 8.125rem) var(--space-6);
}

.read-hero--compact h1 {
  font-size: var(--step-3);
}

.read-hero__lede {
  margin: var(--space-4) 0 0;
  color: var(--muted);
}

.read-hero__lede--narrow {
  max-width: 46ch;
}

.read-body {
  position: relative;
}

.read-body .read-col {
  padding-block: var(--space-5) var(--space-9);
  display: flex;
  flex-direction: column;
  gap: 3.5rem;
}

.read-body h2 {
  font-size: clamp(1.35rem, 1.15rem + 0.7vw, 1.6rem);
  margin: 0 0 var(--space-4);
}

.read-body p {
  margin: 0 0 var(--space-4);
}

.read-body p:last-child {
  margin-bottom: 0;
}

/* A marked gap that is a whole answer rather than a word: the fact is missing,
   so the page says so plainly instead of quietly leaving the section out. */
.gap-card {
  background: var(--sunken);
  border-radius: var(--radius-md);
  padding: 1.25rem var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transition: background-color var(--transition);
}

.gap-card__label {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.gap-card p {
  margin: 0;
  color: var(--muted);
}

.last-updated {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: baseline;
  color: var(--muted);
}

.last-updated__label {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.read-outro {
  position: relative;
  overflow: hidden;
}

/* /privacy ends on the curve alone - no closing sentence, just the route
   travelling back to the middle before the footer picks it up. */
.read-outro--bare {
  height: 150px;
}

.read-outro .read-col {
  position: relative;
  z-index: 1;
  padding-block: var(--space-7) clamp(4rem, 2rem + 5vw, 6.875rem);
}

.read-outro p {
  margin: 0;
  color: var(--muted);
}

/* -- Promises band: same pine band, but on the reading column -------------- */

.promises {
  position: relative;
  padding-block: clamp(3rem, 7vw, 5.5rem);
}

.promises h2 {
  margin: 0 0 var(--space-6);
  color: var(--on-fill);
}

.promises__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.promises__list li {
  display: flex;
  gap: 1.25rem;
  align-items: baseline;
}

.promises__num {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  flex-shrink: 0;
}

.promises__list p {
  margin: 0;
}

@media (max-width: 820px) {
  /* The column stops centring and tucks in beside the 8% rail. */
  .read-col {
    margin-inline: 0;
    padding-left: calc(8% + 30px);
    padding-right: var(--space-5);
  }
}

/* ==========================================================================
   13. Project pages

   The whole trail on these pages runs in the product's own colour, set once as
   --trail-colour on the article and inherited by every piece below it. Focus
   rings stay --pine everywhere (DESIGN.md §9) - they are wayfinding, not brand.

   The <article> is display:contents so its three blocks stack exactly like the
   designed page, while still being ONE node the dialog can pick up in phase 4.
   ========================================================================== */

.project {
  display: contents;
}

.project__col {
  max-width: 820px;
  margin-inline: auto;
  padding-inline: var(--space-5);
}

.project__col--stack {
  display: flex;
  flex-direction: column;
  gap: 3.5rem;
}

.project__enter {
  position: relative;
  overflow: hidden;
}

.project__enter .project__col {
  position: relative;
  z-index: 1;
  padding-block: clamp(4rem, 2rem + 6vw, 7.5rem) var(--space-7);
}

.project__body {
  position: relative;
}

.project__body .project__col {
  padding-block: var(--space-2) var(--space-9);
}

.project__exit {
  position: relative;
  overflow: hidden;
  height: 130px;
}

.project__status {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--trail-colour);
  margin: 0;
}

.project__name {
  font-size: var(--step-3);
  margin: var(--space-2) 0 0;
}

.project__lede {
  margin: var(--space-4) 0 0;
  color: var(--muted);
  font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.35rem);
  line-height: 1.5;
  max-width: 46ch;
}

.project__visual {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--card);
}

.project__h2 {
  font-size: clamp(1.35rem, 1.15rem + 0.7vw, 1.6rem);
  margin: 0 0 var(--space-4);
}

.project__prose {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  max-width: var(--measure);
}

.project__prose p {
  margin: 0;
}

/* -- Does / doesn't: two lists of equal weight ----------------------------- */

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

/* Mono and the same size on both sides on purpose: "wat het niet doet" is a
   feature of the product, not small print. */
.project__list-heading {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 var(--space-4);
}

.project__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.project__list li {
  display: flex;
  gap: var(--space-3);
  align-items: baseline;
}

.project__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--trail-colour);
  flex-shrink: 0;
  transform: translateY(-1px);
}

/* Outlined instead of filled - the shape carries the difference, not the colour. */
.project__dot--open {
  background: transparent;
  border: 1.5px solid var(--trail-colour);
}

/* -- Detail shots ---------------------------------------------------------- */

.project__trio {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

/* Not links: there is no image to open yet. The gap says so inside the frame,
   and these become anchors once real detail shots exist. */
.project__detail svg {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--sunken);
}

.project__tech {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  color: var(--muted);
}

/* -- Availability ---------------------------------------------------------- */

.project__availability {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: background-color var(--transition), border-color var(--transition);
}

.project__availability-text {
  margin: 0;
  max-width: 52ch;
}

.project__cta {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  flex-wrap: wrap;
  margin-top: var(--space-1);
}

/* The one button that carries a product colour rather than --pine, because it
   belongs to that product (DESIGN.md §3). */
.btn--product {
  background: var(--trail-colour);
  color: var(--on-fill);
}

.btn--product:hover {
  color: var(--on-fill);
}

/* Destination still unknown, so it is a <span> rather than a link. It keeps the
   full product fill: fading it out dropped --on-fill on --night to about 2.9:1,
   well under AA, and the gap beside it already says why it does nothing. */
.btn--product[aria-disabled="true"] {
  cursor: default;
}

@media (max-width: 820px) {
  .project__col {
    margin-inline: 0;
    padding-left: calc(8% + 30px);
    padding-right: var(--space-5);
  }

  .project__duo {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }

  .project__trio {
    grid-template-columns: 1fr 1fr;
  }
}

/* ==========================================================================
   14. Portfolio cards - DESIGN.md §7, §8

   No .dc.html exists for the portfolio index, so this follows the written spec:
   --card surface, 1px --border, --radius-lg, no shadow, and the entire hover
   treatment is the border shifting to --border-strong.
   ========================================================================== */

.cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color var(--transition), background-color var(--transition);
}

.card:hover {
  border-color: var(--border-strong);
}

.card__link {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: var(--space-5);
  align-items: center;
  padding: var(--space-5);
  text-decoration: none;
  color: var(--ink);
}

.card__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.card__link:hover .wp__name {
  color: var(--pine);
}

.card .wp__visual {
  border-radius: var(--radius-md);
}

.card .project__tech {
  margin-top: var(--space-2);
}

@media (max-width: 639px) {
  .card__link {
    grid-template-columns: 1fr;
  }
}

/* ==========================================================================
   15. Contact form

   All three states below are designed, so they are styled here even though the
   server side that switches between them arrives in a later phase.
   ========================================================================== */

/* The form column closes tighter than a reading column - there is no prose
   under it, just the exit curve. */
.read-body .read-col.contact-col {
  padding-bottom: 2.5rem;
}

.form {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  max-width: 440px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.field label {
  font-weight: 500;
}

.field input,
.field textarea {
  width: 100%;
  padding: 10px 14px;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  color: var(--ink);
  font-family: inherit;
  font-size: inherit;
  transition: border-color var(--transition), background-color var(--transition);
}

.field input {
  min-height: var(--touch-target);
}

.field textarea {
  line-height: 1.65;
  resize: vertical;
}

/* Error: a thicker berry edge, plus aria-invalid and a message the field points
   at with aria-describedby. Colour never carries the meaning on its own. */
.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"] {
  border-width: 2px;
  border-color: var(--berry);
}

.field__error {
  margin: 0;
  color: var(--berry);
  font-size: 0.9375rem;
}

/* Sending: everything recedes rather than disappearing, so the form does not
   jump while the request is in flight. */
.field input:disabled,
.field textarea:disabled {
  background: var(--sunken);
  border: 1px solid var(--border);
  color: var(--muted);
}

.form--sending .field label {
  color: var(--muted);
}

.form__note {
  margin: 0;
  color: var(--muted);
  font-size: 0.875rem;
  max-width: 44ch;
}

.btn--submit {
  align-self: flex-start;
}

.btn:disabled,
.btn[aria-busy="true"] {
  background: var(--sunken);
  color: var(--muted);
  border-color: var(--border-strong);
  cursor: default;
}

/* Success */
.sent {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 440px;
}

.sent__label {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pine);
}

/* Same card, berry label. The design has no failure state, but the page must
   never say "verstuurd" when nothing was sent. */
.sent--problem .sent__label {
  color: var(--berry);
}

.sent h2 {
  font-size: clamp(1.35rem, 1.15rem + 0.7vw, 1.6rem);
  margin: 0;
}

.sent p {
  margin: 0;
  color: var(--muted);
}

/* ==========================================================================
   16. The shared dialog

   Holds whatever the intercepted link pointed at. The content arrives exactly as
   the full page renders it, so the overrides below are presentation only: the
   trail belongs to the page, not to a modal card, and the reading column has
   already been narrowed by the dialog itself.
   ========================================================================== */

.em-dialog {
  width: min(720px, 92vw);
  max-height: 84vh;
  /* Scrolls inside itself - designed against the longest project. */
  overflow: auto;
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0;
}

.em-dialog::backdrop {
  background: var(--backdrop);
}

.em-dialog--narrow {
  width: min(480px, 90vw);
}

/* Stays reachable however far down the visitor has scrolled. */
.em-dialog__bar {
  position: sticky;
  top: 0;
  display: flex;
  justify-content: flex-end;
  padding: var(--space-4) var(--space-4) 0;
  background: var(--card);
  z-index: 1;
}

.em-dialog__close {
  width: var(--touch-target);
  height: var(--touch-target);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--transition), color var(--transition);
}

.em-dialog__close:hover {
  color: var(--pine);
  border-color: var(--pine);
}

.em-dialog__head {
  padding: 0 2.25rem;
}

.em-dialog__title {
  font-size: clamp(1.35rem, 1.15rem + 0.7vw, 1.6rem);
  margin: 0;
}

.em-dialog__lede {
  margin: var(--space-2) 0 0;
  color: var(--muted);
}

.em-dialog__host {
  padding: var(--space-2) 2.25rem 2.25rem;
}

/* -- What the page contributes that a modal should not -------------------- */

.em-dialog .trail-curve,
.em-dialog .rail,
.em-dialog .project__exit {
  display: none;
}

/* On the page the article is display:contents so its blocks stack into the page
   layout; inside the dialog it is the card's own content flow. */
.em-dialog .project {
  display: block;
}

.em-dialog .project__enter,
.em-dialog .project__body {
  overflow: visible;
}

.em-dialog .project__col {
  max-width: none;
  margin-inline: 0;
  padding-inline: 0;
}

.em-dialog .project__enter .project__col {
  padding-block: 0 var(--space-5);
}

.em-dialog .project__body .project__col {
  padding-block: 0;
}

.em-dialog .form {
  max-width: none;
}

/* ==========================================================================
   17. Utilities
   ========================================================================== */

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

/* ==========================================================================
   18. Reduced motion
   All transitions off; nothing on this site conveys meaning through movement.
   The trail's draw-in has its own escape hatch in §10, because switching the
   animation off must leave the route VISIBLE, not clipped away.
   ========================================================================== */

@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;
    scroll-behavior: auto !important;
  }
}
