/* ---------------------------------------------------------------------------
   GvC Amsterdam
   A quiet, printed-page feeling. Warm paper ground, one serif, hairline rules,
   and the logo's three colours used sparingly as the only ornament.
   --------------------------------------------------------------------------- */

:root {
  /* Ground — warm linen, the colour of good uncoated paper */
  --paper:        #EAE3D7;
  --paper-raised: #F1EBE1;
  --paper-sunk:   #E1D8C9;
  --ink:          #231F1A;
  --ink-soft:     #4A423A;
  --ink-mute:     #655A4D;
  --rule:         #CCC0AF;
  --rule-soft:    #DBD2C4;
  --paper-rail:   #E1D8C7;   /* the sidebar, a couple of shades back */

  /* From the logo: the canal houses and the badge */
  --indigo: #393D98;
  --red:    #ED1C24;
  --orange: #F7941E;
  --orange-ink: #B86A00;   /* the orange, dark enough to read on paper */
  /* The official files draw the logo's frame and wordmark in #3A4394, six
     values off the --indigo above, which was sampled from the old mark. They
     are indistinguishable on screen and nothing sits close enough to compare,
     so the token is left as it is rather than moved for its own sake. */
  --lit:    #F4D690;       /* a window with its light on, at dusk. L*87 on
                              the paper's L*90 — a soft warm glow. Pushing it
                              lighter than about L*88 leaves nothing to see. */

  --dark:      #241F19;
  --dark-ink:  #E6DFD3;
  --dark-mute: #9E9284;
  --dark-rule: #3E362D;

  --serif: "EB Garamond", "Garamond", "Hoefler Text", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;

  --measure: 34rem;        /* ~61 characters at the lead size */
  --measure-body: 28.5rem; /* ~61 at body size */
  --measure-small: 24rem;  /* ~62 at detail size */
  --gutter: clamp(1.5rem, 5vw, 5rem);
  --page: 64rem;

  --step-label: 0.75rem;
  --step-small: 0.9rem;
  --step-body:  1.15rem;
  --step-lead:  1.4rem;
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--step-body);
  font-weight: 400;
  line-height: 1.52;
  /* The served cut of EB Garamond has no oldstyle figures, so asking for
     "onum" was a claim the font could not honour — and font-feature-settings
     on the body overrides every font-variant-numeric below it. Proportional
     figures are present, and are what running text wants. */
  font-variant-numeric: proportional-nums;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; height: auto; display: block; }

a {
  text-decoration: none;
  transition: color .18s ease;
}

/* --- links -------------------------------------------------------------
   The hero's indigo, plus half a step of weight.

   The colour cannot carry this alone. On this paper a link needs luminance
   >= 0.144 to sit 3:1 from the near-black body text, and <= 0.131 to stay
   4.5:1 on the paper itself — those bands do not overlap, so NO colour
   distinguishes a link here by itself. The indigo is 7.18:1 on the paper but
   only 1.79:1 against the ink beside it, and that difference is hue, which a
   colour-blind reader does not see. Weight is the cue that does not depend
   on colour, and the typeface is variable 400-600 so 500 costs no download.

   Every link takes it; the chrome opts out below. Scoping it the other way —
   listing the places that should be blue — meant each new link had to be
   added to that list, and three were missed. */
a {
  color: var(--indigo);
  font-weight: 500;
}
a:hover { color: var(--ink); }

/* Navigation, brands, buttons, media tiles and the dark footer are not links
   in running text and keep their own treatment. */
.skip,
.rail__brand,
.rail__nav a,
.drawer a,
.topbar__brand,
.btn,
.film__frame,
.plate a,
.colophon a,
.adm-table a,
.adm-back,
.gable-rule a {
  color: inherit;
  font-weight: inherit;
}
.rail__brand:hover,
.rail__nav a:hover,
.drawer a:hover,
.topbar__brand:hover { color: var(--ink); }
.colophon a:hover { color: #fff; }

h1, h2, h3, h4 {
  font-weight: 400;
  line-height: 1.18;
  margin: 0;
  /* Tightening is for display sizes only; at text size a Renaissance
     old-style needs its sidebearings. */
  letter-spacing: 0;
}

p { margin: 0 0 1.05em; }
p:last-child { margin-bottom: 0; }

hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}

/* Selected text is the logo's orange, with ink on it at 7.2:1. Not the
   indigo: that is already the colour of a link and of the focus ring, and
   selecting a link would have looked like focusing it. The light blue it
   replaces came from nowhere in the brand. */
::selection { background: var(--orange); color: var(--ink); }

:focus-visible {
  outline: 2px solid var(--indigo);
  outline-offset: 3px;
}

/* --- primitives ---------------------------------------------------------- */

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

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

.label {
  font-size: var(--step-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 1.4;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
}
.skip:focus {
  left: 1rem; top: 1rem;
  z-index: 50;
  background: var(--paper-raised);
  padding: .6rem 1rem;
  border: 1px solid var(--ink);
}

/* Words for assistive technology and not for the screen: the name of a
   table's actions column, where an empty <th> announces as a blank header,
   and the date inside an event's heading, where the date on screen sits
   before the heading and is skipped by anyone moving heading to heading. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The one piece of ornament: the logo's three colours as a hairline.
   Drawn as a gradient so it needs no markup of its own. */
.tricolour {
  height: 3px;
  width: 5.5rem;
  background: linear-gradient(
    to right,
    var(--red) 0 33.34%,
    var(--indigo) 33.34% 66.67%,
    var(--orange) 66.67% 100%
  );
}


/* --- the shell: a ruled column on the left, content on the right --------- */

:root {
  --rail: 15.5rem;
  /* The hero's foot and the gable band's padding. */
  --opening-foot: clamp(1.25rem, 3vh, 2rem);
  --gable-band-pad: clamp(.75rem, 2vh, 1.25rem);
}

.shell { display: flex; align-items: flex-start; min-height: 100vh; }
.content {
  flex: 1 1 auto;
  min-width: 0;
  /* The dividing rule belongs here, not on the rail: the rail is only one
     viewport tall, so its own border would stop at the fold. */
  border-left: 1px solid var(--rule);
  margin-left: -1px;
}

.rail {
  /* The one rule on this page that is not a hairline, and the only one with a
     colour. Everything structural here is 1px of --rule; this is 2px of the
     logo's orange, which is why it reads as the church's mark rather than as
     another line. It is the same weight and the same orange as the tricolour
     under the hero and the underline on the current language, so it extends a
     vocabulary the page already has instead of introducing one.

     Asked for by a visitor who said the header should be "more orange" — the
     old WordPress site's header is a filled orange band. Filled was tried (see
     the note on .topbar): on desktop the rail is a column, not a band, so the
     same colour covers about six times the area, stops being an accent, and
     swallows the orange houses in the logo standing on it. An edge carries the
     recognition at none of that cost. box-sizing is border-box, so the extra
     pixel comes out of the rail rather than moving the page. */
  border-right: 2px solid var(--orange);
  flex: 0 0 var(--rail);
  width: var(--rail);
  align-self: stretch;
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  /* No bottom padding: the street sits flush with the foot of the column. */
  padding: 1.75rem 0 0;
  background: var(--paper-rail);
  /* Above the street. See the note on .canal: the drawing runs the full width
     of the page and the rail is what hides its left end. */
  z-index: 1;
}

.rail__brand {
  display: block;
  border-bottom: 0;
  padding: 0 1.5rem 1.6rem;
}
.rail__brand img { width: 44px; height: 44px; margin-bottom: .8rem; }
.rail__name { display: block; font-size: 1.12rem; line-height: 1.1; letter-spacing: .003em; }
.rail__sub {
  display: block;
  margin-top: .3rem;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  line-height: 1.5;
}

/* The rules between the links are the layout. */
.rail__nav { display: flex; flex-direction: column; border-top: 1px solid var(--rule); }
.rail__nav a {
  display: block;
  padding: .85rem 1.5rem;
  font-size: 1.02rem;
  line-height: 1.3;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--rule-soft);
  position: relative;
  transition: color .18s ease, background .18s ease;
}
.rail__nav a:last-child { border-bottom: 0; }
.rail__nav a:hover { color: var(--ink); background: var(--paper-raised); }
.rail__nav a[aria-current="page"] { color: var(--ink); }
/* The current page is marked by a short tick in the margin, not a box, in the
   logo's orange — the same thing the language switcher says with its underline
   (see .lang a[aria-current]). Where you are was being said in two colours: an
   indigo tick for the page and an orange underline for the language. One
   colour means one idea, and indigo is already spoken for as the colour of a
   link, which is the opposite of the page you are on. Contrast is not a
   concern here for the same reason it is not there: the ink has already told
   you, and this repeats it in the church's own colour. */
.rail__nav a[aria-current="page"]::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 1.1rem;
  background: var(--orange);
}

.rail__foot { margin-top: auto; padding: 1.75rem 1.5rem 0; }
.rail__foot .rail__canal { margin-bottom: 0; }
.rail__foot .label { display: block; margin-bottom: .3rem; }
.rail__time {
  font-size: 1.32rem;
  line-height: 1.1;
  font-variant-numeric: lining-nums;
  letter-spacing: -0.012em;
  margin: 0 0 .2rem;
}
.rail__where { font-size: var(--step-small); color: var(--ink-mute); margin: 0 0 1rem; line-height: 1.35; }

/* --- the two accounts, as marks ------------------------------------------

   Drawn in views/partials/social.ejs at a 1.3px stroke, which is the weight
   of the canal houses and of every rule on the page — so they read as part of
   the same drawing rather than as two brand badges dropped onto it. They
   replaced a Follow us page that was, once the noise came off, one heading
   and two handles.

   currentColor throughout, so the mark takes the link colour on hover with no
   second rule and no second palette. */
.social {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: 1.5rem;
}
.social__link {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: var(--ink);
  font-weight: 400;
}
.social__link:hover { color: var(--indigo); }
.social__icon { display: block; width: 1.5rem; height: 1.5rem; }
.social__icon svg { display: block; width: 100%; height: 100%; }
.social__name { font-size: var(--step-body); }

/* In the rail foot and the mobile drawer: the mark alone, a little smaller,
   set in the muted ink the time and the venue beside it use. In the rail it
   sits ABOVE the service time, so the margin is below it. */
.social--rail { gap: 1.1rem; margin: 0 0 1.4rem; }
.social--rail .social__link { color: var(--ink-mute); }
.social--rail .social__link:hover { color: var(--indigo); }
.social--rail .social__icon { width: 1.3rem; height: 1.3rem; }

/* In the dark footer the marks take the colophon's own ink, like every other
   link down there, and brighten to white on hover. */
.colophon .social { margin-top: 1rem; gap: 1.35rem; }
.colophon .social__link { color: var(--dark-mute); }
.colophon .social__link:hover { color: #fff; }
.colophon .social__name { font-size: var(--step-small); }
/* A block of the same street, cropped to the column. One asset, so it can
   never drift out of step with the full-width strip at the page foot. */
.rail__canal {
  height: calc(var(--canal-h, 154px) * 0.72);
  margin: 0 -1.5rem 0;
  background-image: var(--canal-src, url("/img/canal-houses.svg"));
  background-repeat: no-repeat;
  background-position: calc(50% + var(--canal-cx, 0px) * 0.72) bottom;
  background-size: calc(var(--canal-w, 3037px) * 0.72) calc(var(--canal-h, 154px) * 0.72);
}
.rail__foot .tricolour { margin-bottom: 1.25rem; }

/* --- mobile: a plain bar and a drawer ----------------------------------- */

.topbar, .drawer, .topbar-wrap { display: none; }

@media (max-width: 64rem) {
  .shell { display: block; min-height: 0; }
  .rail { display: none; }
  .content { border-left: 0; margin-left: 0; }

  .topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .75rem var(--gutter);
    /* The rail's orange edge, turned through ninety degrees: on a phone the
       rail is gone and this bar is the header, so it carries the mark. See
       .rail for why an edge rather than the old site's filled band — though a
       filled band is a far better fit here than on desktop, this being a band
       and not a column. Worth revisiting on this breakpoint alone if the
       church wants more of it. */
    border-bottom: 2px solid var(--orange);
    background: var(--paper);
    min-height: 3.75rem;
  }
  /* Whatever the browser scrolls to — a focused link, an #anchor — stops
     below the sticky bar, not under it, where a keyboard user would lose it. */
  html { scroll-padding-top: 4.75rem; }

  /* The wrapper is what sticks, so the drawer opens under the bar wherever
     the reader is scrolled. With the drawer in normal flow it opened at the
     top of the document instead. */
  .topbar-wrap {
    display: block;
    position: sticky;
    top: 0;
    z-index: 20;
  }
  .topbar__brand { display: flex; align-items: center; gap: .6rem; border-bottom: 0; }
  .topbar__brand img { width: 32px; height: 32px; }

  .topbar__toggle {
    display: flex;
    align-items: center;
    gap: .55rem;
    background: none;
    border: 0;
    /* The bar has 3.75rem of height to spend; the control was 25px tall. */
    padding: .85rem 0;
    color: var(--ink);
    font-family: inherit;
    cursor: pointer;
  }
  .topbar__bars { display: flex; flex-direction: column; gap: 4px; width: 20px; }
  .topbar__bars span { display: block; height: 1px; background: var(--ink); }
  .topbar__word {
    font-size: var(--step-label);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-mute);
  }

  .drawer {
    display: none;
    flex-direction: column;
    border-bottom: 1px solid var(--rule);
    background: var(--paper-raised);
  }
  .drawer[data-open="true"] { display: flex; }
  .drawer a {
    padding: .95rem var(--gutter);
    font-size: 1.05rem;
    color: var(--ink);
    border-bottom: 1px solid var(--rule-soft);
  }
  .drawer a:last-child { border-bottom: 0; }
  /* The rail's tick, in the drawer's own terms — and in the same orange, so
     the menu marks where you are the way every other surface does. */
  .drawer a[aria-current="page"] { box-shadow: inset 3px 0 0 var(--orange); }
  .drawer__minor { font-size: .92rem !important; color: var(--ink-mute) !important; }

  /* The language switcher as the drawer's last row: the same padding and the
     same hairline as the three pages above it, so it reads as a fourth item
     rather than as something tacked on the end. The row above it keeps its
     rule — only the language row drops one, being last. */
  /* The two things in this menu that are not pages, on one row: the marks at
     the left edge the page names start from, the language hard right. They
     had a full-height row each, which gave a four-item menu six rows. */
  .drawer__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .7rem var(--gutter);
  }
  .drawer__foot .social--rail { margin: 0; gap: 1.35rem; }
  .drawer__foot .social__icon { width: 1.4rem; height: 1.4rem; }
  /* `.drawer a` gives every link in the menu a full row's padding and a
     hairline under it, which is right for a page name and wrong for a mark or
     for EN / NL sitting on one line with it. */
  .drawer__foot a { padding: 0; border-bottom: 0; }

  .drawer__lang {
    padding: 0;
  }
  .drawer__lang .lang {
    /* Left, with the links at the size the pages are set at. In the top bar
       this sits at 12px and letter-spaced; in a full-width row that reads as
       fine print rather than as a choice. */
    justify-content: flex-start;
    gap: .55rem;
    font-size: 1.05rem;
    letter-spacing: 0.06em;
  }
  .drawer__lang .lang a { padding: .1rem 0; }
}

/* --- opening ------------------------------------------------------------- */

.opening {
  padding: clamp(3.5rem, 11vh, 8rem) 0 var(--opening-foot);
}

.opening__tagline {
  font-size: clamp(2.15rem, 6.2vw, 4.2rem);
  line-height: 1.06;
  letter-spacing: -0.022em;
  max-width: 26ch;
  margin: 0 0 1.6rem;
  text-wrap: balance;
}
.opening__tagline em {
  font-style: italic;
  color: var(--indigo);
}

.opening__body {
  max-width: var(--measure);
  font-size: var(--step-lead);
  line-height: 1.56;
  color: var(--ink-soft);
  margin-top: 1.8rem;
}

/* --- the practical block: what someone actually came for ----------------- */

.practical--open { border-top: 0 !important; padding-top: .4rem; }
/* In the hero the facts sit between the tricolour and the prose, so they
   carry their own rules rather than a section's. */
.practical--hero {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin: 2rem 0 0;
  padding: 1.4rem 0;
}
.practical .practical__item .label { margin-bottom: .3rem; }
.practical {
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--rule);
  padding: 1.6rem 0 2.2rem;
  display: grid;
  grid-template-columns: max-content max-content minmax(0, 1fr);
  gap: 1.5rem clamp(1rem, 3vw, 2.5rem);
}
.practical > * { min-width: 0; }
.practical__item .label { display: block; margin-bottom: .55rem; }

/* The fourth column — how to get in touch — beside when, where and how to
   get here.

   Contact is max-content, NOT the flexible column: an email address cannot
   wrap and cannot be clipped, and making it the 1fr did exactly that at
   1120px, where the row printed "hello@gvc.amster" and stopped. The transit
   routes take the slack instead, because they are two short lines that can
   wrap without losing anything.

   Four columns only above 76rem. The measured need is about 780px of content
   for all four, the rail takes roughly 190–254px off the viewport before
   this grid sees any of it, and a 1216px window is the point where that
   fits with room to spare rather than to the pixel. Below it the row is a
   square of two by two, which is the better shape anyway. */
@media (min-width: 76.01rem) {
  .practical--open { grid-template-columns: max-content max-content minmax(0, 1fr) max-content; }
}
.practical__value--contact { font-size: 1rem; line-height: 1.5; }
.practical__value--contact span + span { margin-top: .3rem; }

/* The same row on the Contact page, held off the block above it. On the home
   page this row follows the hero and needs no margin of its own.

   No closing rule: on the home page one is right because the page carries on
   underneath it, but here the row IS the end of the page and the next thing
   below is the canal street. A hairline with nothing under it reads as a line
   waiting for content that never comes. */
.practical--contact {
  margin-top: clamp(2rem, 5vh, 3rem);
  border-bottom: 0;
}
.practical__value {
  font-size: 1.12rem;
  line-height: 1.42;
}
.practical__value strong { font-weight: 600; }
.practical__value .big {
  display: block;
  font-size: clamp(1.5rem, 3.4vw, 2.1rem);
  line-height: 1.1;
  font-variant-numeric: lining-nums;
  letter-spacing: -0.015em;
}
.practical__value span { display: block; }

/* Two rows of two. The four-column row folds sooner than the three-column
   one, because it is carrying one more column in the same width: the home
   page's row gives up at 76rem, the Contact page's three columns — when,
   where, getting there, and no contact — hold on to 60rem, which is where
   they have always folded. One threshold for both would either break the
   home row at 1200px or squeeze the Contact row for no reason. */
@media (max-width: 76rem) {
  .practical--open { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 60rem) {
  .practical { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 28rem) {
  .practical { grid-template-columns: 1fr; }
}

/* --- generic section ----------------------------------------------------- */

/* Sections do not draw their own closing rule. Every hairline on the page is
   placed deliberately — the practical block, the programme, the verse, the
   facts table, the gable — and a per-section border landed a second line a
   few dozen pixels from one of those, which read as a mistake. */
.section { padding: clamp(3rem, 8vh, 5.5rem) 0; }
.section--tight { padding: clamp(2rem, 5vh, 3.25rem) 0; }

/* One left edge for the whole page. The small label sits above its title as
   an eyebrow rather than in a column beside it: a 13rem label column was
   indenting every prose section 264px while the practical block, the
   photographs and the event rows all ran flush, which gave the page four
   different left edges. */
.section__head { margin-bottom: clamp(1.5rem, 4vh, 2.75rem); }
.section__head .label { display: block; margin-bottom: .7rem; }
.section__title {
  font-size: clamp(1.55rem, 3.2vw, 2.25rem);
  letter-spacing: -0.012em;
}
/* A page's own heading needs a step above the subheads beneath it. */
h1.section__title {
  font-size: clamp(2rem, 4.4vw, 2.9rem);
  letter-spacing: -0.018em;
}
.section__note {
  max-width: var(--measure-body);
  color: var(--ink-soft);
  margin-top: .7rem;
}

.section__body > .prose,
.section__body > .flow { max-width: var(--measure); }

.prose p, .prose ul, .prose ol { max-width: var(--measure-body); }
.prose p, .opening__body p, .entry__detail p, .section__note { text-wrap: pretty; }

/* --- the band: the words and a silent Sunday, between two rules ----------

   "About our service" is not a section with a picture beside it, it is one
   band, bounded by a hairline top and bottom — the same rule the verse and
   the gables draw, stopping exactly where they stop. The words are held in
   the middle of the left half and the reel fills the right end, top rule to
   bottom rule. Nothing floats: both halves begin and end on the same two
   lines, which is what makes them read as one object rather than a column of
   text with a video parked beside it. Before this the reel was top-aligned
   next to four short paragraphs, ran 200px past the last line, and left the
   bottom third of the section as blank paper.

   The band's opening rule is the verse's closing one. Held apart by the usual
   section padding, two full-width hairlines with a hand's width of blank
   paper between them read as an empty row of the same table — the one thing a
   ruled page must not do — so the two are butted together and are what they
   then look like: two bands of a printed page, quote and then service. Its
   closing rule does the job of the gable that used to follow this section, so
   that gable is gone and the page draws no more lines here than before.

   20rem for the reel is the number that makes both halves right. It leaves
   the words --measure-body at the 64rem page, and at the band's height it is
   within a few pixels of the source's own 480x854, so the footage is barely
   cropped — the room stays the room. */
.verse:has(+ .gable-band) { padding-bottom: 0; }
.gable-band:has(+ .section--band) { padding-bottom: clamp(1.5rem, 4vh, 2.5rem); }
.section--band { padding-top: 0; padding-bottom: clamp(3rem, 8vh, 5.5rem); }

.band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: stretch;
  min-height: clamp(27rem, 50vh, 35rem);
  /* Only the closing rule is drawn here: the verse above already draws the
     opening one, in the same place and at the same width, and two hairlines a
     pixel apart read as a mistake. */
  border-bottom: 1px solid var(--rule);
  /* Out through the wrap's padding, so the band's two rules are exactly the
     verse's two rules above it — the wrap's own border box — instead of
     stopping a gutter short of them and reading as a near miss. The gutter
     comes back as padding on this side only, which keeps the words on the
     page's one left edge and lets the reel end where the rules end. */
  margin-inline: calc(-1 * var(--gutter));
  padding-left: var(--gutter);
}

/* The words sit in the middle of their half, against the whole of the reel,
   rather than at the top of it. */
.band__words {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(2rem, 5vh, 3.25rem) 0;
  min-width: 0;
}
.band__words .section__head { margin-bottom: clamp(1.25rem, 3vh, 2rem); }

/* Pause, set like a label on a small tab in the footage's corner, so it reads
   as a caption rather than as a player's controls. */
.reel__toggle {
  position: absolute;
  left: .75rem;
  bottom: .75rem;
  min-height: 24px;
  padding: .45rem .7rem;
  font-family: inherit;
  font-size: .68rem;
  line-height: 1;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--ink-mute);
  cursor: pointer;
}
.reel__toggle:hover { background: var(--indigo); border-color: var(--indigo); color: var(--paper); }

/* On a phone the band unstacks: the prose keeps the top and the reel follows
   it, at its own aspect and its own full hairline, across the whole of the
   text's width. It was held to 17rem so it would stay a reel rather than a
   panel; on a phone that read as a thumbnail with a column of blank paper
   beside it, out of line with every rule on the page. Full width is the same
   left and right edge as the words above it, so the band still reads as one
   object. */
@media (max-width: 52rem) {
  .band {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    padding-right: var(--gutter);
  }
  .band__words { padding-bottom: clamp(1.5rem, 4vh, 2rem); }
}

/* --- verse --------------------------------------------------------------- */

.verse { padding: clamp(3.5rem, 10vh, 6.5rem) 0; text-align: center; }
/* No rule above or below any more, so no extra inset either: the section's
   own padding is the space. */
.verse > .wrap { padding-bottom: clamp(1rem, 3vh, 2rem); }
.verse__text {
  font-size: clamp(1.45rem, 3.6vw, 2.25rem);
  font-style: italic;
  line-height: 1.34;
  max-width: 30ch;
  margin: 0 auto 1.5rem;
  color: var(--ink);
  text-wrap: balance;
}
.verse__ref {
  font-size: var(--step-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.verse .tricolour { margin: 0 auto 2rem; }

/* --- pictures ------------------------------------------------------------ */

.plates {
  display: grid;
  grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
  gap: clamp(1rem, 3vw, 2rem);
}
@media (max-width: 52rem) {
  .plates { grid-template-columns: repeat(min(var(--cols, 3), 2), minmax(0, 1fr)); }
}
@media (max-width: 32rem) {
  .plates { grid-template-columns: minmax(0, 1fr); }
}
.plate { margin: 0; }
.plate img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  filter: saturate(.93) contrast(1.02);
  background: var(--paper-sunk);
}
.plate figcaption {
  margin-top: .65rem;
  font-size: var(--step-small);
  color: var(--ink-mute);
  line-height: 1.4;
}
.plates[style*="--cols: 1"] { max-width: 34rem; }
.plates[style*="--cols: 2"] { max-width: 52rem; }

/* --- the programme: events as a ruled list ------------------------------ */

/* The list's own top border matches its row separators. Full --ink here
   read as a second, heavier rule under the ornamented divider above it. */
.programme { border-top: 1px solid var(--rule); }

/* The entries past the first month, until the reader asks for them. The
   [hidden] attribute cannot do this by itself: .entry sets display: grid,
   and a class beats the user-agent rule behind the attribute. */
.entry[hidden] { display: none; }
.programme__more { margin: 1.75rem 0 0; }

.entry {
  display: grid;
  grid-template-columns: 5.5rem 1fr auto;
  gap: .35rem clamp(1.25rem, 3.5vw, 2.75rem);
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--rule-soft);
  align-items: baseline;
}
.entry__date {
  grid-row: span 2;
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 1;
  padding-top: .15rem;
  /* A date column in a printed programme is right-aligned: left-aligned, "3"
     and "31" have their left edges flush and their right edges 18px apart. */
  text-align: right;
  padding-right: .75rem;
}
.entry__day {
  display: block;
  font-size: 1.8rem;
  /* Figures are never tracked negatively — they need their advances. */
  letter-spacing: 0;
}
.entry__month {
  display: block;
  margin-top: .28rem;
  font-size: var(--step-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.entry__title {
  font-size: 1.28rem;
  line-height: 1.28;
  letter-spacing: -0.012em;
}
.entry__meta {
  grid-column: 3;
  grid-row: 1;
  text-align: right;
  font-size: var(--step-small);
  color: var(--ink-mute);
  font-variant-numeric: lining-nums;
  white-space: nowrap;
}
.entry__detail {
  grid-column: 2 / -1;
  max-width: var(--measure-small);
  color: var(--ink-soft);
  font-size: var(--step-small);
  line-height: 1.55;
}
.entry__detail p { margin-bottom: .5em; }
.entry--past { color: var(--ink-mute); }
.entry--past .entry__title { color: var(--ink-mute); }

@media (max-width: 40rem) {
  .entry { grid-template-columns: 3.75rem 1fr; }
  .entry__day { font-size: 1.5rem; }
  .entry__meta {
    grid-column: 2;
    grid-row: auto;
    text-align: left;
  }
  .entry__detail { grid-column: 2; }
}

.empty {
  padding: 2.5rem 0;
  color: var(--ink-mute);
  font-style: italic;
}

/* --- films --------------------------------------------------------------- */

.films {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: clamp(1.5rem, 3.5vw, 2.5rem);
}

/* Two columns, fixed. The Sermons page lists everything and lets auto-fill
   decide, which is right there; on the home page and the Follow us page the
   block is exactly four recordings and it should read as a square of them.
   With auto-fill the column count came out of the window instead — three
   across at one width, two-and-a-gap at another — so a set of four could
   land as a row of three with one stranded underneath it. */
.films--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 40rem) {
  .films--pair { grid-template-columns: minmax(0, 1fr); }
}
.film { padding-top: .25rem; }
.film__frame {
  display: block;
  border-bottom: 0;
  position: relative;
  background: var(--paper-sunk);
  aspect-ratio: 16 / 9;
  overflow: hidden;
}
.film__frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: saturate(.93);
  transition: transform .4s ease;
}
.film__frame:hover img { transform: scale(1.02); }
.film__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid rgba(35, 31, 26, .12);
  pointer-events: none;
}
.film__play {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 3.25rem; height: 3.25rem;
  border-radius: 50%;
  background: rgba(234, 227, 215, .92);
  display: grid;
  place-items: center;
  color: var(--indigo);
}
.film__play svg { width: 1rem; height: 1rem; margin-left: 2px; }
.film__title {
  margin-top: .85rem;
  font-size: 1.12rem;
  line-height: 1.3;
}
.film__meta {
  margin-top: .35rem;
  font-size: var(--step-label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.film__note {
  margin-top: .6rem;
  font-size: var(--step-small);
  color: var(--ink-soft);
  line-height: 1.5;
}

/* --- definition list, for the ANBI figures ------------------------------ */

.facts {
  margin: 0;
  border-top: 1px solid var(--rule);
}
.facts__row {
  display: grid;
  grid-template-columns: 15rem 1fr;
  gap: .3rem clamp(1.5rem, 4vw, 3rem);
  padding: .95rem 0;
  border-bottom: 1px solid var(--rule-soft);
}
.facts dt {
  font-size: var(--step-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-top: .28rem;
}
.facts dd {
  margin: 0;
  font-variant-numeric: lining-nums;
  line-height: 1.45;
}
@media (max-width: 44rem) {
  .facts__row { grid-template-columns: 1fr; }
}

/* --- files --------------------------------------------------------------- */

.files { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.files li { border-bottom: 1px solid var(--rule-soft); }
.files a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1rem 0;
  border-bottom: 0;
}
.files__name { line-height: 1.35; }
.files__meta {
  font-size: var(--step-label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  white-space: nowrap;
}

/* --- forms (public: members gate) --------------------------------------- */

.field { margin-bottom: 1.25rem; }
.field label {
  display: block;
  font-size: var(--step-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-bottom: .45rem;
}
.input,
input[type="text"], input[type="email"], input[type="password"],
input[type="date"], input[type="time"], input[type="number"],
input[type="url"], textarea, select {
  width: 100%;
  font-family: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--paper-raised);
  border: 1px solid var(--ink-mute);
  border-radius: 0;
  padding: .65rem .75rem;
  line-height: 1.4;
  -webkit-appearance: none;
  appearance: none;
}
textarea { min-height: 8rem; resize: vertical; line-height: 1.55; }
select {
  background-image: none;
  padding-right: .75rem;
}
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--indigo);
  box-shadow: inset 0 0 0 1px var(--indigo);
}

.btn {
  display: inline-block;
  font-family: inherit;
  font-size: var(--step-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: var(--ink);
  color: var(--paper);
  border: 1px solid var(--ink);
  border-radius: 0;
  padding: .78rem 1.5rem;
  cursor: pointer;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.btn:hover { background: var(--indigo); border-color: var(--indigo); color: #fff; }
.btn--quiet {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink-mute);
}
/* A quiet button FILLS on hover rather than only recolouring its hairline.
   Tim, on the WhatsApp button: "can it fill in yellow when I hover instead of
   just the border, or maybe fill in blue so it's more clear with the white
   text". Blue, and for a reason: the indigo is already the site's link colour,
   and --paper on it measures 7.18:1, where the lit yellow (--lit, L*87) is a
   window-light tint that cannot carry text at all — it is within three points
   of the paper's own lightness. The text goes to --paper rather than #fff
   because that is what .btn already reverses to at rest, so a filled quiet
   button and a filled primary one are the same object.

   .btn--danger:hover is declared after this and wins, so Delete and Remove in
   the admin still fill red. */
.btn--quiet:hover { background: var(--indigo); border-color: var(--indigo); color: var(--paper); }

/* A destructive button says so at rest, not only on hover — hover never fires
   on a tablet, and the admin is plausibly used on one. The label stays --ink
   because the red is 3.44:1 on paper and would fail as text; the red carries
   the border. On hover the ground is #D1161D rather than the logo red, which
   measured 4.38:1 against white where 4.5 is required. */
.btn--danger { color: var(--ink); border-color: var(--red); }
.btn--danger:hover { background: #D1161D; border-color: #D1161D; color: #fff; }

/* Tap targets. "Menu" measured 75x25 and is the only navigation control a
   phone has. */
.btn--small { padding: .7rem .9rem; font-size: .68rem; }

.notice {
  border-left: 2px solid var(--indigo);
  background: var(--paper-raised);
  padding: .85rem 1.1rem;
  margin-bottom: 1.5rem;
  font-size: var(--step-small);
}
.notice--bad { border-left-color: var(--red); }
.notice--good { border-left-color: var(--indigo); }

/* --- contact strip ------------------------------------------------------- */

.note-small { font-size: .9rem; }

/* --- the ways to reach us ------------------------------------------------

   The email and the WhatsApp link as a pair of labelled items rather than as
   a sentence with a link buried in it: somebody who came to the page to get
   in touch should find the address without reading a paragraph first. Each
   item is labelled with its channel, in the same small caps the facts above
   the fold use, so this reads as the same kind of block as those.

   auto-fit on a 13rem minimum, so the pair sits side by side wherever there
   is room and stacks when there is not, and so the three-item row on the
   Contact page needs no separate rule. */
.reach {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1.75rem clamp(1.5rem, 4vw, 3rem);
  margin-top: 2rem;
}

/* The address and the button, on one line, vertically centred on each other.
   They are two controls of the same rank, not a labelled grid: the labels and
   the explanatory note that used to sit around them said what the address and
   the button already say. Wraps to two rows on a narrow screen. */
.reach--ways {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem clamp(1.5rem, 4vw, 3rem);
}
.reach--ways p { margin: 0; }
.reach__action { line-height: 1; }


/* --- follow us: two doors ------------------------------------------------

   YouTube and Instagram side by side, each a heading, a line of prose and a
   way out. Neither is an embed — see the note in views/follow.ejs for why
   that is a decision and not an omission.

   One hairline between them at desktop width, which is the only thing
   dividing them: no panel, no card, no shadow. On a phone they stack and the
   rule moves to the top of the second, because a vertical rule between two
   stacked blocks divides nothing. */
.doors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: clamp(2rem, 5vw, 3.5rem);
  margin-top: 2.5rem;
}
.door { min-width: 0; }
.door + .door {
  border-top: 1px solid var(--rule);
  padding-top: clamp(2rem, 5vw, 3rem);
}
@media (min-width: 52rem) {
  .door + .door {
    border-top: 0;
    padding-top: 0;
    border-left: 1px solid var(--rule);
    padding-left: clamp(2rem, 5vw, 3.5rem);
  }
}
.door__name {
  font-size: clamp(1.35rem, 3vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 .65rem;
}
.door .contact-line { margin: 0; }


/* --- colophon ------------------------------------------------------------ */

.colophon {
  background: var(--dark);
  color: var(--dark-ink);
  padding: clamp(3rem, 8vh, 4.5rem) 0 2rem;
  margin-top: 0;
}
.colophon a { color: var(--dark-ink); }
.colophon a:hover { color: #fff; }
.colophon .label { color: var(--dark-mute); }

.colophon__grid {
  display: grid;
  /* Narrow screens: as many 11rem columns as fit, wrapping as they must. The
     minimum came down from 12rem when the mark became a fourth column —
     four 12rem tracks and their gaps needed 912px of the 878 this grid has,
     so the last column wrapped to a row of its own. */
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: clamp(1.75rem, 4vw, 3rem);
  padding-bottom: 2.5rem;
  border-bottom: 1px solid var(--dark-rule);
}
/* Once the rail is showing there is room for all four side by side, and the
   mark should take only the width it needs rather than an equal quarter: it
   is 7.5rem of artwork, and a 1fr share would leave it floating in a column
   twice its size while the three text columns were squeezed. */
@media (min-width: 64.0625rem) {
  .colophon__grid { grid-template-columns: auto repeat(3, minmax(0, 1fr)); }
}
.colophon__col .label { display: block; margin-bottom: .75rem; }
.colophon__col p,
.colophon__col li { font-size: var(--step-small); line-height: 1.6; margin: 0; }
.colophon__col ul { list-style: none; margin: 0; padding: 0; }
.colophon__col li { margin-bottom: .4rem; }

.colophon__mark {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-bottom: 1rem;
}
.colophon__mark img { width: 40px; height: 40px; }

.colophon__foot {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem 1.75rem;
  justify-content: space-between;
  padding-top: 1.5rem;
  font-size: var(--step-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dark-mute);
}
.colophon__foot a { border-bottom: 0; color: var(--dark-mute); }
.colophon__foot a:hover { color: var(--dark-ink); }

/* --- misc --------------------------------------------------------------- */

.stack > * + * { margin-top: 1.1rem; }
.stack-lg > * + * { margin-top: clamp(1.75rem, 4vh, 2.75rem); }
.center { text-align: center; }
.mute { color: var(--ink-mute); }
.hide { display: none !important; }

@media print {
  .rail, .topbar, .drawer, .canal, .colophon { display: none; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .section { border: 0; padding: 1rem 0; }
  a { border-bottom: 0; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
}

/* --- the gable rule ------------------------------------------------------
   A hairline broken by three canal houses traced from the badge, the middle
   one carrying a small cross. The only picture the page draws for itself. */

.gable-band { padding: var(--gable-band-pad) 0; }

/* A section that hands over to a gable or the verse should not also draw its
   own closing rule — two hairlines a pixel apart read as a mistake. */
.gable-band + .section { border-top: 0; }

.gable-rule {
  display: flex;
  align-items: flex-end;
  color: var(--ink-mute);
}
.gable-rule::before,
.gable-rule::after {
  content: "";
  flex: var(--gable-before, 1) 1 0;
  height: 1px;
  background: currentColor;
  margin-bottom: .5px;
}
.gable-rule::after { flex-grow: var(--gable-after, 1); }
/* Where the little house sits on its rule. Varying it between sections is
   what keeps the motif from reading as a repeated stamp. */
.gable-rule--left  { --gable-before: 0.1; --gable-after: 1; }
.gable-rule--right { --gable-before: 1;   --gable-after: 0.1; }
.gable-rule svg {
  flex: 0 0 auto;
  height: 28px;
  display: block;
}

/* --- the street ----------------------------------------------------------
   A row of canal houses drawn as a 1px elevation: real gable types
   (trapgevel, halsgevel, klokgevel, puntgevel, kroonlijst), tall ground
   floors with a stoop, hoisting beams, two houses leaning the way the real
   ones do, the church standing once in the middle, and one windmill to its
   right, no taller than the roofs. Served as one cached
   SVG and repeated rather than scaled, so the houses are the same size on a
   phone as on a monitor — narrow screens simply see less of the street.
   Regenerate with: node tools/build-canal-houses.js */

.canal {
  /* The houses stand directly on the footer. Size and source both come from
     the head, which reads them from the drawing the build actually produced —
     hardcoding either is how the CSS and the drawing drifted apart before. */
  height: var(--canal-h, 154px);
  margin-left: calc(-1 * var(--rail));
  width: calc(100% + var(--rail));
  border-top: 0;
  /* The lit panes are an inline overlay, positioned to match the background
     exactly. overflow keeps the drawing, which is wider than any viewport,
     from giving the page a horizontal scrollbar.

     This position: relative is why .rail needs a z-index. The street runs the
     full width of the page, under the sidebar, and the sidebar is what hides
     its left end — which worked while .canal was unpositioned, because a
     sticky element paints above in-flow block backgrounds. Once .canal became
     positioned the two were siblings with z-index auto, so DOM order decided,
     and the footer comes last. */
  position: relative;
  overflow: hidden;
}

/* The street, painted OVER the lit glass so that every window frame stays on
   top of its glow. A pseudo-element rather than .canal's own background,
   because a background always paints under the element's children and the
   glass has to be behind the lines, not over them.

   The church is shifted to the middle of the CONTENT column: the houses either
   side of it vary in width so it does not land there on its own, and half the
   rail moves it off the window centre. The shift arrives pre-negated, because
   a negative value inside a calc() subtraction is invalid CSS and silently
   drops the whole declaration. */
.canal::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--canal-src, url("/img/canal-houses.svg"));
  background-repeat: repeat-x;
  background-position: calc(50% + var(--canal-cx, 0px) + var(--rail) / 2) bottom;
  background-size: var(--canal-w, 3037px) var(--canal-h, 154px);
  pointer-events: none;
}

/* Lit windows. Only the panes are inline; the street behind them is the
   cached background image, because CSS cannot reach inside one to animate it.
   One copy is enough — the drawing is wider than any real viewport, and on an
   ultrawide screen the repeated background carries the overflow with its
   houses simply unlit. */
.canal__lit {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: var(--canal-w, 3037px);
  height: var(--canal-h, 154px);
  transform: translateX(calc(-50% + var(--canal-cx, 0px) + var(--rail) / 2));
  pointer-events: none;
}
.canal__lit rect,
.canal__lit path {
  fill: var(--lit);
  opacity: 0;
  /* A light switch, not a dimmer: step-end holds each state and then snaps,
     so a window goes dark the way someone flicking a switch would do it.
     Mostly on, with two short dark spells per 43s cycle, and delays spread
     across the cycle so no two windows ever change together. */
  animation: lamp 43s step-end infinite;
}
@keyframes lamp {
  0%  { opacity: 1; }
  38% { opacity: 0; }
  46% { opacity: 1; }
  81% { opacity: 0; }
  89% { opacity: 1; }
}
/* Someone who has asked for less motion still gets the dusk, just held. */
@media (prefers-reduced-motion: reduce) {
  .canal__lit rect, .canal__lit path { animation: none; opacity: .85; }
}
@media (max-width: 64rem) {
  .canal { margin-left: 0; width: 100%; }
  /* No sidebar here, so no rail to allow for. */
  .canal::after { background-position: calc(50% + var(--canal-cx, 0px)) bottom; }
  .canal__lit { transform: translateX(calc(-50% + var(--canal-cx, 0px))); }
}
/* Scaled, not cropped: leaving background-size alone here sliced the tops off
   every gable and took the cross off the church, on the device most visitors
   use. calc keeps it tied to the real drawing size. */
@media (max-width: 48rem) {
  .canal { height: calc(var(--canal-h, 154px) * 0.8); }
  .canal::after {
    background-size: calc(var(--canal-w, 3037px) * 0.8) calc(var(--canal-h, 154px) * 0.8);
    background-position: calc(50% + var(--canal-cx, 0px) * 0.8) bottom;
  }
  /* The overlay scales with the background, or the panes drift out of their
     frames on a phone. */
  .canal__lit {
    width: calc(var(--canal-w, 3037px) * 0.8);
    height: calc(var(--canal-h, 154px) * 0.8);
    transform: translateX(calc(-50% + var(--canal-cx, 0px) * 0.8));
  }
}

/* The lockup, as the footer's first column. Wide enough that the three lines
   of the wordmark are readable, which was the whole reason for moving it out
   of the rail. No bottom margin: the grid's own gap does that job now, and a
   margin here would only disagree with it on one column out of four. The top
   nudge sets the badge's crown against the cap height of the labels beside
   it, which an image and a line of type do not otherwise share. */
.colophon__logo {
  display: block;
  width: 7.5rem;
  height: auto;
  margin-top: -.15rem;
}

/* A range is one object: it must not break across lines. */
.nowrap, .rail__time, .practical__value .big { white-space: nowrap; }

/* --- documents -----------------------------------------------------------
   The ANBI form is embedded, not just linked: a publication obligation
   should be readable on the page. The frame is sized to a portrait page and
   scrolls internally; the link beside the title is the way out for anything
   that will not render inline. */

.docs { margin-top: clamp(2.5rem, 6vh, 4rem); }
.docs__label { display: block; margin-bottom: .9rem; }

.doc { margin: 0 0 clamp(1.75rem, 4vh, 2.5rem); }
.doc__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.25rem;
  flex-wrap: wrap;
  border-top: 1px solid var(--rule);
  padding: .85rem 0;
}
.doc__title { font-size: 1.08rem; line-height: 1.35; }
.doc__meta {
  font-size: var(--step-label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  white-space: nowrap;
}
.doc__frame {
  display: block;
  width: 100%;
  height: min(78vh, 780px);
  border: 1px solid var(--rule);
  background: var(--paper-raised);
}
@media (max-width: 40rem) {
  /* A portrait page squeezed into a phone column is unreadable, so the frame
     stays tall enough to scroll rather than shrinking to nothing. */
  .doc__frame { height: 62vh; }
}
.doc__fallback {
  margin: 0;
  padding: 2rem 1.5rem;
  font-size: var(--step-small);
  color: var(--ink-mute);
}

/* --- the language switcher ----------------------------------------------
   Two links and a separator, in the same small-caps as every other label.
   Links, not a control, so it works without JavaScript and a crawler can
   follow it to the other language. */
.lang {
  display: flex;
  align-items: baseline;
  gap: .4rem;
  font-size: var(--step-label);
  letter-spacing: 0.14em;
}
.lang a {
  color: var(--ink-mute);
  font-weight: inherit;
  padding: .5rem .1rem;
  /* The underline belongs to the current language alone, so the others must
     not carry the one links get by default anywhere else. */
  text-decoration: none;
}
.lang a:hover { color: var(--ink); }

/* The language you are reading, marked with the logo's yellow.
   [aria-current] on its own matched BOTH links: the attribute is always
   present and reads "false" on the other one, so the mark has to be on the
   value. The yellow is the badge's own #F7941E, which is too light to carry
   meaning as text on this paper (1.79:1) but is doing no such job here — the
   ink already tells you which is current, and this says it a second time in
   the logo's own colour. */
.lang a[aria-current="true"] {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--orange);
  text-decoration-thickness: 2px;
  text-underline-offset: .32em;
  /* Safari shortens a skipped underline under the descenders; there are none
     in EN or NL, but the switcher is not the only thing this rule could ever
     mark. */
  text-decoration-skip-ink: none;
}
.lang__sep { color: var(--rule); }

/* On desktop it sits in the whitespace at the top right of the content
   column, aligned with the text measure rather than the viewport edge.
   Absolutely positioned, so it occupies space nobody was using and does not
   push the hero down. */
.content { position: relative; }
.lang-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
  /* The bar spans the column but only the links themselves take clicks, so
     it cannot swallow a press meant for the page under it. */
  pointer-events: none;
}
.lang-bar .wrap {
  display: flex;
  justify-content: flex-end;
  padding-top: clamp(1.4rem, 3.5vh, 2.4rem);
}
.lang-bar .lang { pointer-events: auto; }

@media (max-width: 64rem) {
  /* On a phone the sticky bar carries it, beside the Menu button — an
     absolutely positioned copy would sit underneath that bar. */
  .lang-bar { display: none; }
  /* One control in the bar now; the rule is kept so the button stays
     vertically centred against the brand beside it. */
  .topbar__right { display: flex; align-items: center; }
}

/* --- the opening: dark, with the welcome reel ------------------------------
   Chosen by the redesign team in October 2026 from three versions; the other
   two are in the git history. The band holds the words only; the facts row
   follows on paper (see views/home.ejs). */

.opening { position: relative; }
.opening > .wrap { position: relative; z-index: 2; }

.dark-band { position: relative; background: var(--dark); color: var(--dark-ink); overflow: hidden; }
.dark-band .opening { position: relative; z-index: 2; background: transparent; }
.dark-band .opening__tagline { color: var(--dark-ink); }
.dark-band .opening__tagline em { color: var(--lit); }
.dark-band .opening__body { color: var(--dark-ink); }
body:has(.dark-band) .lang-bar { z-index: 4; }
body:has(.dark-band) .lang-bar a { color: var(--dark-ink); }
body:has(.dark-band) .lang-bar .lang__sep { color: var(--dark-mute); }

/* The facts row, in the band: light on dark, its rule on top. Labels a
   shade lighter than --dark-mute, to stay over 4.5:1 above the reel. */
.dark-band .section--in-band { position: relative; z-index: 2; background: transparent; padding-bottom: clamp(2rem, 5vh, 3rem); }
.dark-band .practical--open { border-top: 1px solid var(--dark-rule) !important; border-bottom: 0; padding-top: clamp(2.2rem, 5vh, 3rem); }
.dark-band .label { color: #B8AC9B; }
.dark-band .practical__value, .dark-band .big { color: var(--dark-ink); }
.dark-band a { color: var(--lit); }
.band--words-only { grid-template-columns: minmax(0, 1fr); min-height: 0; padding-bottom: clamp(2rem, 5vh, 3rem); }

/* The reel runs the full height of the band, fading in only from the left. */
.hero-dark { position: absolute; z-index: 1; right: 0; top: 0; bottom: 0; width: 46%;
  -webkit-mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.3) 30%, #000 70%);
  mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.3) 30%, #000 70%); }
.hero-dark__media { width: 100%; height: 100%; object-fit: cover; object-position: 50% 40%;
  filter: sepia(.3) saturate(.85) brightness(.7) contrast(1.05); }
/* Full height, but darker where the facts row crosses it, so its small
   labels and links stay over 4.5:1 on the brightest frames. */
.hero-dark::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, transparent 50%, rgba(36,31,25,.82) 72%, rgba(36,31,25,.88)); }
.hero-dark__toggle { position: absolute; z-index: 3; left: auto; right: 1.25rem; bottom: 1.25rem;
  background: var(--dark); color: var(--dark-ink); border-color: var(--dark-mute); }

/* Until there is room beside the words — on a phone, and on a laptop too
   narrow for both — the reel plays behind everything under a dark veil. 80rem
   is where the text column and the reel first fit side by side. */
@media (max-width: 80rem) {
  .hero-dark { inset: 0; width: auto; -webkit-mask-image: none; mask-image: none; }
  .hero-dark::after { content: ""; position: absolute; inset: 0;
    background: linear-gradient(to bottom, rgba(36,31,25,.74), rgba(36,31,25,.88) 40%, rgba(36,31,25,.95)); }
  .hero-dark__media { filter: sepia(.3) saturate(.8) brightness(.8); }
}
/* On a wide screen the words move to the left, and the reel takes what is
   left after the text column, up to 42rem — never behind the words, where the
   brighter frames dropped the headline to 2.8:1, and never so wide that
   covering the band's height crops ever closer in. */
@media (min-width: 80.0625rem) {
  .dark-band .wrap { margin-left: 0; }
  .hero-dark { width: min(42rem, calc(100% - var(--gutter) - 37rem)); }
}

/* The test copy says so on every page (SITE_ENV=test). Orange, because it is
   the one colour here that means "look". */
.test-strip {
  margin: 0;
  padding: .45rem var(--gutter);
  background: var(--orange);
  color: var(--ink);
  font-size: .8rem;
  letter-spacing: .04em;
  text-align: center;
  position: relative;
  z-index: 30;
}
