/* ────────────────────────────────────────────────────────────────────
   Managed-page blocks — the design-system layer for PUBLIC page content.

   core/lib/ui/components.css (.ui-*) is admin chrome and is not loaded on
   the public site, so managed pages get their own `.cmsb-*` namespace here.
   Every colour, radius and font resolves through the CSS custom properties
   the site skins already define, with a neutral fallback. That is the whole
   trick behind "a generated page is on-brand by construction": the blocks
   never carry their own palette, they borrow the site's.

   Tokens read (all optional): --accent --accent-contrast --text --muted
   --surface --surface-2 --border --radius --font-heading --font-body
   ──────────────────────────────────────────────────────────────────── */

/* ─── Eén kanaal, geen laag ──────────────────────────────────────────
 *
 * Dit bestand stond in @layer block-defaults. De gedachte was: de site
 * moet het laatste woord hebben over kleur en typografie, dus laat zijn
 * ongelaagde h1 winnen van onze .cmsb-title.
 *
 * Dat was verkeerd, om twee redenen.
 *
 * 1. Het contract is al het kanaal. Sinds site-tokens.css halen de
 *    blokken kleur en lettertype uit --foreground / --primary /
 *    --font-heading. De site vult die in. Er is geen tweede route
 *    nodig waarlangs de site ons overschrijft — dat zijn er dan twee,
 *    en dan is er geen contract meer maar een onderhandeling.
 *
 * 2. Ongelaagd wint ALTIJD van gelaagd, ook een selector van niets.
 *    Probalist opent zijn CSS met `*, *::before, *::after { margin: 0;
 *    padding: 0 }`. Een reset die elke site heeft. Die nul-selector
 *    veegde daarmee ELKE padding en marge uit dit bestand weg: blokken
 *    zonder binnenmarge, koppen tegen de schermrand, een cta-knop met
 *    tekst tot op de rand. Dat is wat er op /home-uit-prompt te zien
 *    was — niet het sjabloon, maar een reset die van ons won.
 *
 * Dus: ongelaagd. De blokken bezitten hun eigen maatvoering en lezen
 * kleur en letter uit het contract. Wil een site een blok anders
 * kleuren, dan vult hij een token — niet een h1-regel die toevallig
 * ook binnen een blok valt.
 */


.cmsb-page {
  /* Fallbacks are RELATIVE, never a literal colour.
   *
   * They used to be #1a1a1a on #fff, which is only correct on a light site
   * that happens to use these token names. Ayni names its palette --ink /
   * --cream / --terra, so nothing matched and every block fell back to
   * near-black text — invisible the moment the site switched to dark.
   *
   * inherit / currentColor / transparent cannot be wrong: an unmapped site
   * renders PLAIN (body colours, no bands, no accent) instead of unreadable.
   * Map the site's own names onto this contract in the site's CSS to get the
   * brand back — that is where site-specific knowledge belongs. */
  /* Nu afgeleid van het contract (core/lib/ui/site-tokens.css). De oude
   * namen --accent / --text / --surface staan er nog achter als tweede
   * kans, zodat een site die daarop staat niet omvalt tijdens de
   * migratie. Die tussenlaag mag weg zodra de vier sites conform zijn. */
  --cmsb-accent:      var(--primary, var(--accent, currentColor));
  --cmsb-on-accent:   var(--primary-foreground, var(--on-accent, Canvas));
  --cmsb-text:        var(--foreground, var(--text, inherit));
  --cmsb-muted:       var(--muted-foreground, var(--muted, color-mix(in srgb, currentColor 72%, transparent)));
  --cmsb-surface:     var(--card, var(--surface, transparent));
  --cmsb-surface-2:   var(--muted, var(--surface-2, color-mix(in srgb, currentColor 7%, transparent)));
  --cmsb-border:      var(--border, color-mix(in srgb, currentColor 18%, transparent));
  --cmsb-radius:      var(--radius, 14px);
  --cmsb-max:         72ch;

  color: var(--cmsb-text);
  font-family: var(--font-body, inherit);
}

.cmsb-page--wide { --cmsb-max: 1100px; }
.cmsb-page--full { --cmsb-max: 100%; }

/* ─── Block shell ─────────────────────────────────────────────────── */

.cmsb { padding: clamp(2rem, 5vw, 3.5rem) 1.25rem; }
.cmsb-inner { max-width: var(--cmsb-max); margin: 0 auto; }
.cmsb--hero { padding-top: clamp(3rem, 8vw, 5.5rem); }
.cmsb--divider { padding-block: 0; }

/* Alternating bands so consecutive blocks stay legible without the
   operator having to think about backgrounds. */
.cmsb--cta-band.cmsb--v-accent {
  background: var(--cmsb-surface-2);
}
.cmsb--feature-grid.cmsb--v-card { background: var(--cmsb-surface-2); }

/* ─── Typography ──────────────────────────────────────────────────── */

.cmsb-eyebrow {
  font-family: var(--font-heading, inherit);
  font-size: .74rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--cmsb-accent);
  margin-bottom: .7rem;
}
.cmsb-title {
  font-family: var(--font-heading, inherit);
  font-size: clamp(2rem, 5vw, 2.9rem);
  font-weight: 800; letter-spacing: -.03em; line-height: 1.08;
  margin: 0 0 .8rem;
}
.cmsb-h2 {
  font-family: var(--font-heading, inherit);
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  font-weight: 700; letter-spacing: -.02em; line-height: 1.2;
  margin: 0 0 .7rem;
}
.cmsb-intro {
  font-size: clamp(1rem, 2vw, 1.15rem);
  color: var(--cmsb-muted); line-height: 1.65;
  margin: 0 0 1.2rem;
}
.cmsb-body { line-height: 1.7; }

/* ── Links in lopende tekst ──────────────────────────────────────────
 * Elke token met een terugval, want sites vullen het contract maar half
 * in: kleur meestal wel, de rest niet. Zonder die ketting verdwijnt de
 * opmaak stil op precies de sites die hem het hardst nodig hebben.
 *
 * Geldt voor alle velden die lopende tekst dragen, niet alleen de body:
 * een link in een intro of een bijschrift is dezelfde link.
 *
 * De onderstreping is niet decoratief. Kleur alleen is geen verschil
 * voor wie kleurenblind is, en een link die alleen aan zijn kleur te
 * herkennen is, is voor die lezer geen link. */
.cmsb-body, .cmsb-intro, .cmsb-figcaption, .cmsb-card-text, .cmsb-quote-text {
  --cmsb-link:       var(--link, var(--primary, var(--accent, currentColor)));
  --cmsb-link-hover: var(--link-hover, color-mix(in srgb, var(--cmsb-link) 82%, black));
}
.cmsb-body a, .cmsb-intro a, .cmsb-figcaption a, .cmsb-card-text a, .cmsb-quote-text a {
  color: var(--cmsb-link);
  text-decoration: underline;
  text-underline-offset: var(--link-underline-offset, 3px);
  /* from-font volgt de letter in plaats van een vaste dikte op te
     leggen; op een serif-site is 1px te dun en op een schreefloze te dik. */
  text-decoration-thickness: from-font;
}
.cmsb-body a:hover, .cmsb-intro a:hover, .cmsb-figcaption a:hover,
.cmsb-card-text a:hover, .cmsb-quote-text a:hover {
  color: var(--cmsb-link-hover);
}
/* De tweede rol: een link die eruit springt. De schrijver zegt dat met
   vet, wat elk model al produceert — er valt dus niets nieuws te leren.
   Zonder --link-strong blijft het gewoon een vette link. */
.cmsb-body strong a, .cmsb-intro strong a, .cmsb-card-text strong a,
/* Een betaalde link draagt rel="sponsored" — het attribuut dat Google
   ervoor vraagt. Dat is meteen de haak voor de opmaak, zodat markering
   en uiterlijk uit één feit over de link volgen in plaats van uit een
   klasse die iemand moet onthouden. */
.cmsb-body a[rel~="sponsored"], .cmsb-intro a[rel~="sponsored"],
.cmsb-figcaption a[rel~="sponsored"], .cmsb-card-text a[rel~="sponsored"] {
  color: var(--link-strong, var(--cmsb-link));
}
.cmsb-body a[rel~="sponsored"], .cmsb-intro a[rel~="sponsored"],
.cmsb-figcaption a[rel~="sponsored"], .cmsb-card-text a[rel~="sponsored"] {
  font-weight: 600;
}
.cmsb-body > :first-child { margin-top: 0; }
.cmsb-body > :last-child  { margin-bottom: 0; }

.cmsb--v-center .cmsb-inner { text-align: center; }
.cmsb--v-center .cmsb-intro { margin-left: auto; margin-right: auto; max-width: 60ch; }
.cmsb--v-narrow .cmsb-inner { max-width: 58ch; }

/* ─── Call to action ──────────────────────────────────────────────── */

/* Styled by descendant selector, never by a class on the <a>: the
   operator edits this anchor with the inline link editor, and a retyped
   link must not lose its looks. */
.cmsb-cta { margin: 1.4rem 0 0; }
.cmsb-cta a {
  display: inline-block;
  padding: .72rem 1.4rem;
  border-radius: calc(var(--cmsb-radius) - 4px);
  /* De derde rol: de knop. Een eigen token zodat een site zijn knoppen
     anders kan zetten dan de rest van het merk, met --primary als
     terugval — wat voor de meeste sites het goede antwoord is. */
  background: var(--cta, var(--cmsb-accent));
  color: var(--cta-foreground, var(--cmsb-on-accent));
  font-weight: 600; text-decoration: none;
  transition: filter .15s, transform .15s;
}
.cmsb-cta a:hover { filter: brightness(1.08); transform: translateY(-1px); }
.cmsb-cta--link a {
  padding: 0; background: none; color: var(--cta, var(--cmsb-accent));
  text-decoration: underline; text-underline-offset: 3px;
}
.cmsb-cta--link a:hover { transform: none; }

/* ─── Feature grid ────────────────────────────────────────────────── */

.cmsb-grid {
  display: grid; gap: 1.1rem;
  grid-template-columns: repeat(var(--cmsb-cols, 3), minmax(0, 1fr));
  margin-top: 1.6rem;
}
.cmsb-grid--2 { --cmsb-cols: 2; }
.cmsb-grid--3 { --cmsb-cols: 3; }
.cmsb-grid--4 { --cmsb-cols: 4; }

.cmsb-card {
  background: var(--cmsb-surface);
  border: 1px solid var(--cmsb-border);
  border-radius: var(--cmsb-radius);
  padding: 1.3rem;
}
.cmsb--v-plain .cmsb-card { background: none; border: 0; padding: 0; }

/* Rows instead of columns. Once an item runs to a paragraph, a narrow card is
   the wrong container for it — and on a phone the cards are stacked anyway,
   so this is what they were already becoming, done on purpose. */
.cmsb--v-list .cmsb-grid,
.cmsb--v-numbered .cmsb-grid { grid-template-columns: 1fr; gap: 1rem; }
.cmsb--v-list .cmsb-card,
.cmsb--v-numbered .cmsb-card {
  background: none; border: 0; padding: 0;
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .15rem .9rem;
  align-items: start;
}
.cmsb--v-list .cmsb-card-icon,
.cmsb--v-numbered .cmsb-card-icon { grid-row: 1 / span 2; margin: 0; line-height: 1.2; }
.cmsb--v-list .cmsb-card-title,
.cmsb--v-numbered .cmsb-card-title { margin: 0; }

/* Numbered: the count comes from the position, so the icon has nothing to say.
   Shrunk rather than hidden in edit mode — a field you cannot see is a field
   you cannot get your old glyph back from when you switch away again. */
/* The number takes the icon's column, so numbered and list are the same shape
   with a different marker — and the body text lines up under the title instead
   of starting back at the margin. */
.cmsb--v-numbered .cmsb-grid { counter-reset: cmsb-step; }
.cmsb--v-numbered .cmsb-card { counter-increment: cmsb-step; }
.cmsb--v-numbered .cmsb-card::before {
  content: counter(cmsb-step) ".";
  grid-row: 1 / span 2;
  color: var(--cmsb-accent); font-weight: 700;
  font-variant-numeric: tabular-nums;   /* 9. and 10. keep the same column */
}
.cmsb--v-numbered .cmsb-card-icon { display: none; }
/* Shrunk rather than hidden in edit mode: a field you cannot see is a field you
   cannot get your old glyph back from when you switch away again. */
body.is-edit-mode .cmsb--v-numbered .cmsb-card-icon {
  display: block; grid-column: 2; font-size: .75rem; opacity: .35;
}
.cmsb-card-icon  { font-size: 1.5rem; color: var(--cmsb-accent); margin-bottom: .5rem; }
.cmsb-card-title { font-family: var(--font-heading, inherit); font-size: 1.05rem; font-weight: 700; margin: 0 0 .35rem; }
.cmsb-card-text  { color: var(--cmsb-muted); font-size: .95rem; line-height: 1.6; }

/* ─── Image + text ────────────────────────────────────────────────── */

/* Pending-images notice — edit mode only, so it may borrow the admin chrome's
   look rather than the site's palette: it is a tool, not content. */
/* Same treatment as the post bar it sits under: two tools stacked above a
   post read as one panel, not as two different kinds of box. */
.cmsb-pending {
  display: flex; align-items: center; flex-wrap: wrap; gap: .6rem .9rem;
  max-width: var(--cmsb-max); margin: 0 auto 1.4rem;
  padding: .7rem .9rem;
  background: color-mix(in srgb, currentColor 7%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
  border-radius: var(--cmsb-radius);
  font-size: .9rem;
}
.cmsb-pending-text { flex: 1 1 12rem; }
.cmsb-pending-cost { opacity: .65; font-size: .82rem; }
.cmsb-pending-btn {
  font: inherit; cursor: pointer;
  /* A finger target, and no double-tap-to-zoom delay swallowing the first tap. */
  min-height: 44px; touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  padding: .35rem .9rem; border-radius: 999px;
  border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
  background: color-mix(in srgb, currentColor 8%, transparent); color: inherit;
}
.cmsb-pending-btn:hover,
.cmsb-pending-btn:active { background: color-mix(in srgb, currentColor 14%, transparent); }
/* Disabled until its handler is attached — a button that looks ready but is
   not is worse than one that admits it. */
.cmsb-pending-btn[disabled] { opacity: .45; cursor: default; }

/* Image description under an empty placeholder — edit mode only. Built on
   currentColor so it reads on any site skin, light or dark. */
.cmsb-imgprompt {
  max-width: var(--cmsb-max); margin: .5rem auto 0;
  display: flex; flex-direction: column; gap: .3rem;
}
.cmsb-imgprompt-label {
  font-size: .78rem; letter-spacing: .04em; text-transform: uppercase;
  opacity: .6;
}
.cmsb-imgprompt-input {
  width: 100%; box-sizing: border-box;
  padding: .55rem .7rem;
  font: inherit; font-size: 16px;      /* under 16px iOS zooms on focus */
  line-height: 1.45; resize: vertical;
  color: inherit;
  background: color-mix(in srgb, currentColor 5%, transparent);
  background-color: rgba(128, 128, 128, .10);
  border: 1px dashed color-mix(in srgb, currentColor 28%, transparent);
  border-radius: var(--cmsb-radius);
}
.cmsb-imgprompt-input:focus {
  outline: none;
  border-style: solid;
  border-color: color-mix(in srgb, currentColor 45%, transparent);
}
.cmsb-imgprompt-msg { font-size: .75rem; opacity: .6; min-height: 1em; }

/* Post bar — edit mode only, above the post. Same reasoning as the pending
   notice: a tool, not content, so it borrows the chrome's look.

   It sits on top of the edit-mode watermark, so it gets a filled ground rather
   than a see-through dashed box: on a phone the diagonal EDIT MODE stripes ran
   straight through the labels and everything read as noise. */
.cmsb-postbar {
  max-width: var(--cmsb-max); margin: 0 auto 1.2rem;
  padding: .6rem .9rem .8rem;
  background: color-mix(in srgb, currentColor 7%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
  border-radius: var(--cmsb-radius);
  font-size: .9rem;
}
.cmsb-postbar-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: .5rem .8rem;
  min-height: 44px;                     /* a finger-sized row for the switch */
}
/* The buttons wrap as a group rather than one at a time: three of them at
   390px do not fit beside the switch, and a single button orphaned on its own
   line reads as a mistake. */
.cmsb-postbar-actions {
  display: flex; gap: .5rem; flex-wrap: wrap; margin-left: auto;
  justify-content: flex-end;
}
/* A chip, not a sentence. Left as plain text with a small chevron it read as
   a caption — nobody taps a caption. inline-flex so it hugs its words: a
   full-width invisible target is exactly as unclickable-looking as text, it
   just has more of it. */
/* Air between the buttons and the chip. Stacked flush they read as one dense
   block and the eye stops separating them. */
.cmsb-postbar-more { margin-top: .55rem; }
.cmsb-postbar-more > summary {
  cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: .45rem;
  min-height: 34px; padding: .25rem .7rem;
  border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 6%, transparent);
  font-size: .82rem; opacity: .85;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.cmsb-postbar-more > summary:hover,
.cmsb-postbar-more > summary:active {
  background: color-mix(in srgb, currentColor 14%, transparent);
  opacity: 1;
}
.cmsb-postbar-more > summary::-webkit-details-marker { display: none; }
/* Own marker, so it points the right way in both states without relying on the
   UA's, which sits on the wrong side and cannot be spaced.
   Drawn with borders rather than typed as ▾: a glyph is whatever the font
   happens to have — thin, differently centred, sometimes barely there — while
   a triangle is the same solid shape everywhere, and it can turn. */
.cmsb-postbar-more > summary::after {
  content: ''; flex: none;
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid currentColor;
  opacity: .85;
  transition: transform .15s ease;
}
.cmsb-postbar-more[open] > summary::after { transform: rotate(180deg); }
.cmsb-postbar-more[open] > summary { margin-bottom: .5rem; }
.cmsb-postbar-grid {
  /* auto-fit rather than a breakpoint: two columns when two fit at a readable
     width, one when they do not. On a phone that lands on one, which is what
     the cramped date/author pair needed. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .55rem .8rem;
}
.cmsb-postbar-msg { font-size: .75rem; opacity: .65; text-align: right; }
.cmsb-postbar-blocks {
  font: inherit; font-size: .8rem; cursor: pointer; white-space: nowrap;
  min-height: 34px; padding: .25rem .7rem; border-radius: 999px;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  color: inherit;
  background: color-mix(in srgb, currentColor 8%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
}
.cmsb-postbar-blocks:hover,
.cmsb-postbar-blocks:active { background: color-mix(in srgb, currentColor 16%, transparent); }
.cmsb-postbar-field { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.cmsb-postbar-field--wide { grid-column: 1 / -1; }
.cmsb-postbar-field > span {
  font-size: .7rem; letter-spacing: .04em; text-transform: uppercase; opacity: .55;
}
.cmsb-postbar-field input,
.cmsb-postbar-field textarea {
  width: 100%; box-sizing: border-box;
  padding: .45rem .6rem;
  font: inherit; font-size: 16px;      /* under 16px iOS zooms on focus */
  line-height: 1.4; resize: vertical;
  /* iOS centres a date input's own text; left is where the other fields are. */
  text-align: left;
  color: inherit;
  background: color-mix(in srgb, currentColor 8%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
  border-radius: calc(var(--cmsb-radius) / 2);
}
/* A floor for the empty case; the script grows it to its text from there, so
   the summary is never cut off halfway down a word. */
.cmsb-postbar-field textarea { min-height: 4.6em; max-height: 260px; overflow: auto; }
.cmsb-postbar-field input:focus,
.cmsb-postbar-field textarea:focus {
  outline: none;
  border-color: color-mix(in srgb, currentColor 55%, transparent);
}

/* Post list — cards by default, a compact stack on the --v-compact variant.
   Everything is built on the site's own tokens, so it takes the site's brand
   without a line of per-site CSS. */
.cmsb-postlist { display: grid; gap: clamp(1rem, 3vw, 1.8rem); grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); }
.cmsb-postcard { display: block; color: inherit; text-decoration: none; border-radius: var(--cmsb-radius); overflow: hidden; }
/* height:auto is load-bearing, not decoration. These images carry intrinsic
   width/height attributes (so the browser reserves the box before they
   land), and the height attribute is a presentational hint that beats
   aspect-ratio unless the author sets a height. Without it a portrait
   photo renders at its full intrinsic height instead of a 3:2 card. */
.cmsb-postcard-img { width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; display: block; }
.cmsb-postcard-body { padding: .8rem 0 0; }
.cmsb-postcard-title { margin: 0 0 .25rem; font-size: 1.1em; line-height: 1.25; }
.cmsb-postcard-meta { margin: 0 0 .35rem; font-size: .82em; opacity: .65; }
.cmsb-postcard-ex { margin: 0; font-size: .92em; opacity: .85; }
.cmsb-postcard:hover .cmsb-postcard-title { text-decoration: underline; }
.cmsb-postlist-empty { opacity: .7; font-style: italic; }

/* Compact: one column, image shrunk to a thumbnail beside the text. */
.cmsb--v-compact .cmsb-postlist { grid-template-columns: 1fr; gap: 1rem; }
.cmsb--v-compact .cmsb-postcard { display: grid; grid-template-columns: 96px 1fr; gap: .9rem; align-items: start; }
.cmsb--v-compact .cmsb-postcard-img { aspect-ratio: 1 / 1; border-radius: var(--cmsb-radius); }
.cmsb--v-compact .cmsb-postcard-body { padding: 0; }
.cmsb--v-compact .cmsb-postcard-ex { display: none; }

/* Image block — a figure, so the caption is semantically tied to the image.
   --v-wide breaks the inner column to full width; the page's own max-width
   still applies, so it is "wider", never edge-to-edge on a narrow site. */
.cmsb-figure { margin: 0; }
.cmsb-figure-img { width: 100%; height: auto; display: block; border-radius: var(--cmsb-radius); }
.cmsb--v-wide .cmsb-inner { max-width: none; }
.cmsb--v-wide .cmsb-figure-img { border-radius: 0; }
.cmsb-figcaption { margin: .6rem 0 0; font-size: .9em; opacity: .75; text-align: center; }

.cmsb-split { display: grid; gap: clamp(1.2rem, 4vw, 2.6rem); align-items: center; grid-template-columns: 1fr 1fr; }
.cmsb-split--textonly { grid-template-columns: 1fr; }
.cmsb--v-image-right .cmsb-split-media { order: 2; }
.cmsb-split-img { width: 100%; height: auto; border-radius: var(--cmsb-radius); display: block; }

/* Every arrangement below is CSS only — same markup, same four fields — which
   is what makes switching between them lossless: the content does not move,
   only the shape around it. */

/* Stacked. Image above the text, or below it. */
.cmsb--v-image-top .cmsb-split,
.cmsb--v-image-bottom .cmsb-split { grid-template-columns: 1fr; align-items: start; }
.cmsb--v-image-bottom .cmsb-split-media { order: 2; }

/* Two columns that are not equal. One picture carrying the block, or one
   picture supporting a long piece of text. */
.cmsb--v-image-wide .cmsb-split { grid-template-columns: 3fr 2fr; }
.cmsb--v-text-wide  .cmsb-split { grid-template-columns: 2fr 3fr; }

/* Text over the image.
   Guarded on :not(--textonly): with no picture the overlay's white type would
   land on the site's own background and become unreadable. No image, no
   overlay — it falls back to a plain stack. */
.cmsb--v-overlay .cmsb-split:not(.cmsb-split--textonly) {
  grid-template-columns: 1fr;
  border-radius: var(--cmsb-radius);
  overflow: hidden;
  min-height: clamp(260px, 42vh, 460px);
  align-items: stretch;
  gap: 0;
}
.cmsb--v-overlay .cmsb-split:not(.cmsb-split--textonly) > .cmsb-split-media,
.cmsb--v-overlay .cmsb-split:not(.cmsb-split--textonly) > .cmsb-split-text {
  grid-area: 1 / 1;                 /* both children share the one cell */
}
.cmsb--v-overlay .cmsb-split-media { position: relative; }
.cmsb--v-overlay .cmsb-split-img { height: 100%; object-fit: cover; border-radius: 0; }
/* The scrim is what makes the type legible, so the type may commit to white:
   it is reading against this gradient, not against the site's palette. That is
   the one place in this sheet where a literal colour is the correct answer. */
.cmsb--v-overlay .cmsb-split-media::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,.75), rgba(0,0,0,.20) 55%, rgba(0,0,0,0));
}
.cmsb--v-overlay .cmsb-split:not(.cmsb-split--textonly) > .cmsb-split-text {
  position: relative; z-index: 1;
  align-self: end;
  padding: clamp(1.1rem, 4vw, 2.4rem);
  color: #fff;
}
.cmsb--v-overlay .cmsb-split-text .cmsb-h2,
.cmsb--v-overlay .cmsb-split-text .cmsb-body,
.cmsb--v-overlay .cmsb-split-text a { color: inherit; }

/* ─── Quote ───────────────────────────────────────────────────────── */

/* Two columns of running text. Nothing about the content changes — the same
   paragraphs, flowing differently — which is why it is a variant. */
.cmsb--v-two-column .cmsb-body { column-count: 2; column-gap: clamp(1.4rem, 4vw, 2.6rem); }

.cmsb-quote { margin: 0; padding-left: 1.1rem; border-left: 3px solid var(--cmsb-accent); }
.cmsb-quote-text { font-size: clamp(1.1rem, 2.4vw, 1.35rem); line-height: 1.55; font-style: italic; }
.cmsb--v-large .cmsb-quote-text { font-size: clamp(1.35rem, 3.4vw, 1.9rem); }
.cmsb-quote-author { display: block; margin-top: .7rem; font-size: .9rem; font-style: normal; color: var(--cmsb-muted); }

/* ─── Divider ─────────────────────────────────────────────────────── */

.cmsb-rule { border: 0; border-top: 1px solid var(--cmsb-border); margin: 0; }
.cmsb--v-space .cmsb-rule { border-top: 0; }

/* ─── Mobile ──────────────────────────────────────────────────────── */

@media (max-width: 780px) {
  /* The variant rules above are more specific than a bare .cmsb-split, so a
     2fr/3fr split would survive into a phone screen unless it is named here
     too. Overlay is deliberately absent: it is the one layout that is BETTER
     narrow, so it stays. */
  .cmsb-split,
  .cmsb--v-image-wide .cmsb-split,
  .cmsb--v-text-wide .cmsb-split { grid-template-columns: 1fr; }
  .cmsb--v-image-right .cmsb-split-media { order: 0; }
  .cmsb-grid { grid-template-columns: 1fr; }
  .cmsb-grid--4, .cmsb-grid--2 { grid-template-columns: 1fr; }
  /* Two columns of text need roughly 320px each; below that they stop being
     easier to read and start being harder. */
  .cmsb--v-two-column .cmsb-body { column-count: 1; }
}
@media (min-width: 781px) and (max-width: 1000px) {
  .cmsb-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ─── Edit-mode affordances ───────────────────────────────────────── */

/* An empty field is invisible on the public site; in edit mode it shows a
   dimmed placeholder so there is something to click. */
body.is-edit-mode .cmsb-empty { opacity: .45; }
body.is-edit-mode .cmsb-widget-empty {
  border: 1px dashed var(--cmsb-border);
  border-radius: var(--cmsb-radius);
  padding: 1.2rem; text-align: center;
  color: var(--cmsb-muted); font-size: .9rem;
}


/* ═══════════════════════════════════════════════════════════════════
 * Maatvoering — de vangrails
 *
 * Probalist zet h1 op clamp(2.8rem, 6.5vw, 5.8rem) met max-width:840px,
 * ontworpen voor .hero: een vak van 100vh met eigen centrering. In een
 * blokcontainer op een telefoon is dat te groot en loopt de pagina over.
 *
 * "De site wint" is niet hetzelfde als "de site past". Deze regels zijn
 * de ondergrens: wat er ook aan sitestijl binnenkomt, niets binnen een
 * blok wordt breder dan het blok.
 * ═══════════════════════════════════════════════════════════════════ */

/* Maten uit het contract, met dezelfde waarden als hiervoor hard stonden.
   Het verschil zit erin dat een sjabloon ze nu kan verzetten: een
   sjabloon dat alleen kleur verandert voelt als dezelfde site met een
   ander verfje, en dat is niet wat "van sjabloon wisselen" moet zijn. */
.cmsb-title {
  font-size: var(--scale-h1);
  max-width: 100%;
  overflow-wrap: break-word;
}
.cmsb-h2 {
  font-size: var(--scale-h2);
  max-width: 100%;
  overflow-wrap: break-word;
}
.cmsb-card-title { font-size: var(--scale-h3); max-width: 100%; overflow-wrap: break-word; }

/* Niets binnen een blok mag breder zijn dan het blok. Eén regel die een
   hele klasse overloopfouten uitsluit — een lange URL, een tabel, een
   widget die zich verslikt in een smal scherm. */
.cmsb, .cmsb * { min-width: 0; }
.cmsb img, .cmsb video, .cmsb iframe, .cmsb table { max-width: 100%; }
.cmsb pre, .cmsb code { overflow-wrap: anywhere; }
