/* ────────────────────────────────────────────────────────────────────
   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
   ──────────────────────────────────────────────────────────────────── */

.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. */
  --cmsb-accent:      var(--accent, currentColor);
  --cmsb-on-accent:   var(--accent-contrast, var(--on-accent, #fff));
  --cmsb-text:        var(--text, inherit);
  --cmsb-muted:       var(--muted, color-mix(in srgb, currentColor 72%, transparent));
  --cmsb-surface:     var(--surface, transparent);
  --cmsb-surface-2:   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; }
.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);
  background: var(--cmsb-accent);
  color: 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(--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;
}
