
/* --- schedule module (cz-*) ------------------------------------------- */
.cz-schedule { font-family: var(--cz-font-body); color: var(--cz-text); }
.cz-schedule a { color: var(--cz-link); text-decoration: underline; text-underline-offset: 2px; }
.cz-schedule a:hover { color: var(--cz-link-hover); }

.cz-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.6rem;
  font-weight: 500;
  margin: 3.5rem 0 1rem;
}
.cz-year-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.35rem;
  font-weight: 500;
  margin: 2rem 0 0.5rem;
}
.cz-empty { color: var(--cz-text-muted); margin: 1rem 0; }

/* Event unit — list variant: date column beside the body.

   The column width is a hook because it depends on the *date style* the
   design chose, which the module cannot see from CSS: 6.5rem holds "16/10/26"
   and "16 Oct 2026" but not "16 November 2026", and .cz-event-date is
   nowrap, so a long style silently overprints the heading rather than
   wrapping. Found in a browser, on the first design that asked for the
   long date style. */
.cz-schedule--list .cz-event {
  display: grid;
  grid-template-columns: var(--cz-event-date-width, 6.5rem) 1fr;
  gap: 0.35rem 2rem;
  padding: 1.25rem 0;
  border-bottom: 1px solid var(--cz-line);
}
@media (max-width: 40rem) {
  .cz-schedule--list .cz-event { grid-template-columns: 1fr; gap: 0.15rem; }
}

/* Event unit — cards variant: bordered card in a responsive grid. */
.cz-schedule-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
  align-items: start;
  margin: 1.25rem 0;
}
@media (min-width: 40rem) { .cz-schedule-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .cz-schedule-grid { grid-template-columns: repeat(3, 1fr); } }
.cz-schedule--cards .cz-event {
  border: 1px solid var(--cz-line);
  border-radius: var(--cz-radius);
  padding: 1.25rem;
}
.cz-schedule--cards .cz-event-date { margin-bottom: 0.35rem; }

.cz-event-date { color: var(--cz-text); white-space: nowrap; }
.cz-event-chip {
  display: inline-block;
  background: var(--cz-chip-bg, var(--cz-accent));
  color: var(--cz-chip-fg, var(--cz-on-accent));
  font-size: 0.85em;
  line-height: 1.3;
  padding: 0.25em 0.7em;
  border-radius: var(--cz-radius);
  white-space: normal;
}
.cz-event-heading { font-family: var(--cz-font-heading); font-size: 1.1rem; margin: 0; }
.cz-event-heading a { color: inherit; }
.cz-event-heading a:hover { color: var(--cz-link-hover); }
.cz-event-meta { color: var(--cz-text-muted); margin: 0.1rem 0 0; }
.cz-event-programme { margin: 0.1rem 0 0; }
.cz-event-note { font-style: italic; font-size: 0.95em; margin: 0.35rem 0 0; }
.cz-event-note em { font-style: normal; }
.cz-event-links { margin: 0.4rem 0 0; }
.cz-event-links a { margin-right: 1rem; }
/* Self-hosted downloads ride the links row; the arrow marks the affordance
   (worksModule's data-kind="download" idiom). */
.cz-event-links a[data-kind="download"]::before { content: "\2193\00a0"; }
.cz-event-status {
  color: var(--cz-accent);
  font-family: var(--cz-font-body);
  font-size: 0.85rem;
  text-decoration: none;
  display: inline-block;
}
.cz-event--off .cz-event-heading { text-decoration: line-through; color: var(--cz-text-muted); }
.cz-event--off .cz-event-status { text-decoration: none; }

.cz-event-quote { margin: 0.75rem 0 0; padding: 0; border: 0; }
.cz-event-quote p { font-style: italic; margin: 0; }
/* Emphasis inside italic goes upright — see the press module's note. */
.cz-event-quote p em { font-style: normal; }
.cz-event-quote cite {
  display: block;
  font-style: normal;
  color: var(--cz-text-muted);
  margin-top: 0.15rem;
  font-size: 0.9em;
}

/* Past years: native details accordions, newest first, first one open. */
.cz-year { border-bottom: 1px solid var(--cz-line); }
.cz-year-summary {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.25rem;
  padding: 0.75rem 0;
  cursor: pointer;
  list-style: none;
}
.cz-year-summary::-webkit-details-marker { display: none; }
.cz-year-summary::after { content: "+"; float: right; color: var(--cz-text-muted); }
.cz-year[open] > .cz-year-summary::after { content: "\2013"; }

/* The all-dates link under an upcoming teaser. Link colours come from the
   module-wide .cz-schedule a rules; this is only spacing and emphasis. */
.cz-schedule-all { margin: 1.5rem 0 0; }
.cz-schedule--cards .cz-schedule-all { margin-top: 0.25rem; }

.cz-event-compact {
  margin: 0;
  padding: 0.4rem 0;
  border-top: 1px solid var(--cz-line);
  font-size: 0.95rem;
}
.cz-event-compact .cz-event-date { color: var(--cz-text-muted); margin-right: 0.5rem; }
.cz-event-compact.cz-event--off { text-decoration: line-through; color: var(--cz-text-muted); }


/* --- news module (cz-*) ------------------------------------------------ */
.cz-news { font-family: var(--cz-font-body); color: var(--cz-text); }
.cz-news a { color: var(--cz-link); text-decoration: underline; text-underline-offset: 2px; }
.cz-news a:hover { color: var(--cz-link-hover); }
.cz-news .cz-empty { color: var(--cz-text-muted); margin: 1rem 0; }

.cz-post-title {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.35rem;
  font-weight: 500;
  margin: 0.15rem 0 0.75rem;
}
.cz-post-date { color: var(--cz-text-muted); margin: 0; font-size: 0.9rem; }
.cz-post-chip {
  display: inline-block;
  background: var(--cz-chip-bg, var(--cz-accent));
  color: var(--cz-chip-fg, var(--cz-on-accent));
  font-size: 0.85em;
  line-height: 1.3;
  padding: 0.25em 0.7em;
  border-radius: var(--cz-radius);
}
.cz-post-body p { margin: 0 0 0.9rem; }
.cz-post-body p:last-child { margin-bottom: 0; }
.cz-post-image {
  display: block;
  width: 100%;
  border-radius: var(--cz-radius);
}

/* Post unit — stacked variant: full-width articles separated by hairlines,
   photo in a side column on wide screens (the sonata/madrigal ground). */
.cz-news--stacked .cz-post {
  padding: 1.75rem 0;
  border-bottom: 1px solid var(--cz-line);
}
.cz-news--stacked .cz-post--imaged {
  display: grid;
  grid-template-columns: 1fr 18rem;
  gap: 1.5rem;
  align-items: start;
}
.cz-news--stacked .cz-post--imaged .cz-post-main { grid-column: 1; grid-row: 1; }
.cz-news--stacked .cz-post--imaged .cz-post-image { grid-column: 2; grid-row: 1; }
@media (max-width: 40rem) {
  .cz-news--stacked .cz-post--imaged { grid-template-columns: 1fr; }
  .cz-news--stacked .cz-post--imaged .cz-post-main { grid-column: 1; grid-row: 2; }
  .cz-news--stacked .cz-post--imaged .cz-post-image { grid-column: 1; grid-row: 1; }
}

/* Post unit — cards variant: bordered cards in a responsive grid, photo on
   top (the fanfare ground). */
.cz-news-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  align-items: start;
  margin: 1.25rem 0;
}
@media (min-width: 40rem) { .cz-news-grid { grid-template-columns: repeat(2, 1fr); } }
.cz-news--cards .cz-post {
  border: 1px solid var(--cz-line);
  border-radius: var(--cz-radius);
  padding: 1.25rem;
}
.cz-news--cards .cz-post-image {
  aspect-ratio: 16 / 9;
  object-fit: cover;
  margin-bottom: 0.9rem;
}

/* The archive: everything before this year, one details per year, newest
   open. Declared here as well as in the schedule module, and deliberately
   identically — a design may place news without ever placing a diary, and a
   module that leaned on another module's stylesheet would render an
   unstyled accordion on exactly those sites. */
.cz-news .cz-year { border-bottom: 1px solid var(--cz-line); }
.cz-news .cz-year-summary {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.25rem;
  padding: 0.75rem 0;
  cursor: pointer;
  list-style: none;
}
.cz-news .cz-year-summary::-webkit-details-marker { display: none; }
.cz-news .cz-year-summary::after { content: "+"; float: right; color: var(--cz-text-muted); }
.cz-news .cz-year[open] > .cz-year-summary::after { content: "\2013"; }

/* The link through to the full news page under a teaser — the all-dates
   link's twin. Spacing and emphasis only; link colours come from the
   module-wide rules above. */
.cz-news-all { margin: 1.5rem 0 0; }
.cz-news--cards .cz-news-all { margin-top: 0.25rem; }


/* --- gallery module (cz-*) -------------------------------------------- */
.cz-gallery { font-family: var(--cz-font-body); color: var(--cz-text); }
.cz-gallery .cz-empty { color: var(--cz-text-muted); margin: 1rem 0; }

.cz-album-title {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.35rem;
  font-weight: 500;
  margin: 2.5rem 0 1rem;
}
.cz-album:first-child .cz-album-title { margin-top: 0; }

/* Photo layout — grid variant: two across, three on wide screens. */
.cz-album-photos {
  display: grid;
  gap: 1rem;
  align-items: start;
  margin: 1rem 0 0;
}
.cz-gallery--grid .cz-album-photos { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 64rem) { .cz-gallery--grid .cz-album-photos { grid-template-columns: repeat(3, 1fr); } }

/* Photo layout — rows variant: large single column, doubling on wide. */
.cz-gallery--rows .cz-album-photos { grid-template-columns: 1fr; gap: 2rem; }
@media (min-width: 64rem) { .cz-gallery--rows .cz-album-photos { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; } }

.cz-photo { margin: 0; }
.cz-photo-frame {
  display: block;
  overflow: hidden;
  border-radius: var(--cz-radius);
  background: var(--cz-surface);
}
/* The frame is an <a> to the master, so a photo opens full-size with or
 * without a shell lightbox. It carries no link colour or underline — it is
 * a picture, not a piece of text. */
.cz-photo-link { cursor: zoom-in; text-decoration: none; color: inherit; }
.cz-photo-img {
  display: block;
  width: 100%;
  transition: transform 0.4s ease, opacity 0.4s ease;
}
/* Grid tiles crop to a steady ratio (no distortion); rows keep the
 * photograph's own aspect. */
.cz-gallery--grid .cz-photo-img { aspect-ratio: 3 / 2; object-fit: cover; }
.cz-gallery--rows .cz-photo-img { height: auto; }
.cz-photo:hover .cz-photo-img { transform: scale(1.03); opacity: 0.92; }

.cz-photo-caption {
  color: var(--cz-text-muted);
  font-size: 0.9rem;
  margin-top: 0.4rem;
}
.cz-album-credit {
  color: var(--cz-text-muted);
  font-size: 0.85rem;
  margin: 0.75rem 0 0;
}

/* The way through to the whole of it, beside a limited region. */
.cz-gallery-all { margin: 1.5rem 0 0; }


/* --- press module (cz-*) ----------------------------------------------- */
.cz-press { font-family: var(--cz-font-body); color: var(--cz-text); }
.cz-press a { color: var(--cz-link); text-decoration: underline; text-underline-offset: 2px; }
.cz-press a:hover { color: var(--cz-link-hover); }
.cz-press .cz-empty { color: var(--cz-text-muted); margin: 1rem 0; }

/* Pull-quote unit — shared by the quote wall and the articles fallback:
   accent-edged blockquote (the sonata/nocturne/toccata ground). */
.cz-quote {
  border-left: 2px solid var(--cz-accent);
  margin: 0;
  padding: 0.25rem 0 0.25rem 1.25rem;
}
.cz-quote-text { font-size: 1.15rem; line-height: 1.6; font-style: italic; margin: 0; }
/* Emphasis inside an italic quotation goes upright — the typesetter's rule,
   and the only way an italicised work title reads at all in here. Paired with
   the declaration above: a design that sets this block roman overrides both. */
.cz-quote-text em { font-style: normal; }
.cz-quote-stars { color: var(--cz-accent); font-style: normal; white-space: nowrap; }
.cz-quote-source {
  display: block;
  margin-top: 0.6rem;
  font-size: 0.9rem;
  font-style: normal;
  color: var(--cz-text-muted);
}
.cz-quote-date { white-space: nowrap; }
.cz-quote-link { margin: 0.5rem 0 0; font-size: 0.9rem; }

/* Quote wall — responsive 1→2 column grid. */
.cz-press-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem 2.5rem;
  align-items: start;
  margin: 1.25rem 0;
}
@media (min-width: 48rem) { .cz-press-grid { grid-template-columns: repeat(2, 1fr); } }

/* Long-form articles — hairline-separated full-width units; paragraph-less
   entries keep the pull-quote presentation in the same flow (the
   madrigal/fanfare ground). */
.cz-press--articles .cz-quote { margin: 1.75rem 0; }
.cz-article { padding: 1.75rem 0; border-bottom: 1px solid var(--cz-line); }
.cz-article:last-child { border-bottom: 0; }
.cz-article-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.35rem;
  font-weight: 500;
  margin: 0 0 0.35rem;
}
.cz-article-date { color: var(--cz-text-muted); font-size: 0.9rem; margin: 0 0 0.35rem; }
.cz-article-context { font-style: italic; color: var(--cz-accent-deep); margin: 0 0 0.5rem; }
.cz-article-lede { margin: 0.5rem 0 0.9rem; }
.cz-article-body p { margin: 0 0 0.9rem; }
.cz-article-body p:last-child { margin-bottom: 0; }
.cz-article-link { margin: 0.5rem 0 0; font-size: 0.9rem; }

/* The way through to the whole of it, beside a limited region. */
.cz-press-all { margin: 1.5rem 0 0; }


/* --- recordings module (cz-*) ----------------------------------------- */
.cz-recordings { font-family: var(--cz-font-body); color: var(--cz-text); }
.cz-recordings a { color: var(--cz-link); text-decoration: underline; text-underline-offset: 2px; }
.cz-recordings a:hover { color: var(--cz-link-hover); }
.cz-recordings .cz-empty { color: var(--cz-text-muted); margin: 1rem 0; }

/* Card grid — two across, four on wide screens (the prelude/bravura/
   fanfare ground): cover on top, notes beneath. */
.cz-recordings-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2.5rem 1.75rem;
  align-items: start;
  margin: 1.25rem 0;
}
@media (min-width: 64rem) { .cz-recordings-grid { grid-template-columns: repeat(4, 1fr); } }

.cz-recording { margin: 0; }
.cz-recording-cover {
  overflow: hidden;
  border-radius: var(--cz-radius);
  background: var(--cz-surface);
}
.cz-recording-img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  transition: transform 0.4s ease;
}
.cz-recording:hover .cz-recording-img { transform: scale(1.03); }

.cz-recording-title {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1.3;
  margin: 0.85rem 0 0.25rem;
}
.cz-recording-credit { color: var(--cz-text-muted); font-size: 0.9rem; margin: 0 0 0.35rem; }
.cz-recording-personnel { font-size: 0.9rem; margin: 0 0 0.5rem; }
.cz-recording-personnel p { margin: 0 0 0.15rem; }
.cz-recording-blurb { line-height: 1.65; margin: 0.5rem 0; }

/* Track listings — detailed units only; headings pick up the heading face. */
.cz-recording-tracks { font-size: 0.9rem; color: var(--cz-text-muted); margin: 0.75rem 0; }
.cz-track-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-text);
  margin: 0.75rem 0 0.25rem;
}
.cz-recording-tracks > :first-child { margin-top: 0; }
.cz-track-line { margin: 0 0 0.15rem; }

/* Accolades — a compact awards line (madrigal/fanfare). */
.cz-recording-accolades {
  margin: 0.75rem 0 0;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.cz-accolade { color: var(--cz-accent-deep); margin-right: 0.75rem; }
.cz-recordings a.cz-accolade { text-decoration: none; }
.cz-recordings a.cz-accolade:hover { color: var(--cz-accent); text-decoration: underline; }

/* Review quote under its recording (the opt-in recordingQuotes ctx knob). */
.cz-recording-quote {
  border-left: 2px solid var(--cz-accent);
  margin: 0.9rem 0 0;
  padding: 0.1rem 0 0.1rem 0.9rem;
  font-size: 0.9rem;
}
.cz-recording-quote p { font-style: italic; margin: 0; }
/* Emphasis inside italic goes upright — see the press module's note. */
.cz-recording-quote p em { font-style: normal; }
.cz-recording-stars { color: var(--cz-accent); font-style: normal; white-space: nowrap; }
.cz-recording-quote cite {
  display: block;
  margin-top: 0.3rem;
  font-style: normal;
  color: var(--cz-text-muted);
}

/* Listening / buying / review links. */
.cz-recording-links { margin: 0.85rem 0 0; font-size: 0.9rem; }
.cz-recording-link { margin-right: 1rem; white-space: nowrap; }
/* Self-hosted downloads ride the links row; the arrow marks the affordance
   (worksModule's data-kind="download" idiom). A download's label is the
   file's own, and an author's label runs long — "BBC Radio 3 Record Review –
   Harriet Smith and Andrew McGregor discuss the Op 20 CD" — so unlike the
   short outbound labels it wraps rather than overflowing the column. */
.cz-recording-link[data-kind="download"] { white-space: normal; }
.cz-recording-link[data-kind="download"]::before { content: "\2193\00a0"; }

/* Detailed units — full width, cover beside the notes, hairline separated. */
.cz-recordings--detailed .cz-recording {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  padding: 2rem 0;
  border-bottom: 1px solid var(--cz-line);
}
.cz-recordings--detailed .cz-recording:first-child { padding-top: 0.5rem; }
.cz-recordings--detailed .cz-recording:last-child { border-bottom: 0; }
@media (min-width: 48rem) {
  .cz-recordings--detailed .cz-recording { grid-template-columns: 16rem 1fr; gap: 2.5rem; }
}
.cz-recordings--detailed .cz-recording-title { font-size: 1.4rem; margin-top: 0; }

/* The way through to the whole of it, beside a limited region. */
.cz-recordings-all { margin: 1.5rem 0 0; }


/* --- videos module (cz-*) --------------------------------------------- */
.cz-videos { font-family: var(--cz-font-body); color: var(--cz-text); }
.cz-videos a { color: var(--cz-link); text-decoration: underline; text-underline-offset: 2px; }
.cz-videos a:hover { color: var(--cz-link-hover); }
.cz-videos .cz-empty { color: var(--cz-text-muted); margin: 1rem 0; }

/* Embed grid — one across, two at 48rem, three on wide screens. */
.cz-videos-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem 1.5rem;
  align-items: start;
  margin: 1.25rem 0;
}
@media (min-width: 48rem) { .cz-videos-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .cz-videos-grid { grid-template-columns: repeat(3, 1fr); } }

.cz-video-frame {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--cz-radius);
  background: var(--cz-surface);
}
.cz-video-embed { display: block; width: 100%; height: 100%; border: 0; }

/* Poster still — the chosen frame linking out to the watch page. The play
   badge and the caption scrim sit over a photograph, so their blacks and
   whites are literals like every scrim's: the picture isn't themed. The
   badge takes the site's accent on hover, which is the themed part. */
.cz-videos .cz-video-poster {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--cz-radius);
  background: var(--cz-surface);
  text-decoration: none;
}
.cz-video-still {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.cz-video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 4.25rem;
  height: 3rem;
  border-radius: 0.75rem;
  background: rgba(0, 0, 0, 0.6);
  transition: background 0.15s ease;
}
.cz-video-play::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-42%, -50%);
  border-style: solid;
  border-width: 0.55rem 0 0.55rem 0.95rem;
  border-color: transparent transparent transparent #ffffff;
}
.cz-video-poster:hover .cz-video-play,
.cz-video-poster:focus-visible .cz-video-play {
  background: var(--cz-accent);
}
.cz-video-caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 2.25rem 0.9rem 0.7rem;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
  color: #ffffff;
  font-family: var(--cz-font-heading);
  font-size: 0.95rem;
  line-height: 1.35;
  white-space: pre-line;
}
.cz-video--lead .cz-video-caption { font-size: 1.15rem; }

.cz-video-title {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.05rem;
  font-weight: 500;
  line-height: 1.3;
  margin: 0.6rem 0 0;
}
/* Titles above the frame keep their breathing room on the other side. */
.cz-video-title:first-child { margin: 0 0 0.6rem; }

/* Lead embed — the featured variant's single large video (madrigal's
   home-page treatment), centred above the rest of the grid. */
.cz-video--lead { max-width: 48rem; margin: 0 auto 2.5rem; }
.cz-video--lead .cz-video-title { font-size: 1.35rem; }

/* The way through to the whole of it, beside a limited region. */
.cz-videos-all { margin: 1.5rem 0 0; }


/* --- works module (cz-*) ---------------------------------------------- */
.cz-works { font-family: var(--cz-font-body); color: var(--cz-text); }
.cz-works a { color: var(--cz-link); text-decoration: underline; text-underline-offset: 2px; }
.cz-works a:hover { color: var(--cz-link-hover); }
.cz-works .cz-empty { color: var(--cz-text-muted); margin: 1rem 0; }

.cz-works-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.35rem;
  font-weight: 500;
  margin: 2.5rem 0 0.75rem;
}
.cz-works-group:first-child .cz-works-heading { margin-top: 0; }

/* List variant — a no-JS accordion, the source catalogue's own idiom. */
.cz-works--list .cz-work { border-bottom: 1px solid var(--cz-line); }
.cz-work-summary {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.1rem;
  line-height: 1.4;
  margin: 0;
  padding: 0.85rem 1.5rem 0.85rem 0;
  cursor: pointer;
  list-style: none;
  position: relative;
}
.cz-work-summary::-webkit-details-marker { display: none; }
.cz-work-summary::after {
  content: "+";
  position: absolute;
  right: 0;
  color: var(--cz-text-muted);
}
.cz-work[open] > .cz-work-summary::after { content: "\2013"; }
/* A title-only work is a plain row: no widget, no toggle affordance. */
.cz-work--bare .cz-work-summary { cursor: default; }
.cz-work--bare .cz-work-summary::after { content: none; }

.cz-work-name { font-weight: 500; }
.cz-work-year { color: var(--cz-text-muted); font-size: 0.9em; white-space: nowrap; }
.cz-work-chip {
  display: inline-block;
  background: var(--cz-chip-bg, var(--cz-accent));
  color: var(--cz-chip-fg, var(--cz-on-accent));
  font-size: 0.85em;
  line-height: 1.3;
  padding: 0.2em 0.6em;
  border-radius: var(--cz-radius);
}

.cz-work-body { padding: 0 0 1.25rem; max-width: 46rem; }
.cz-work-body > :first-child { margin-top: 0; }

.cz-work-meta { color: var(--cz-text-muted); margin: 0 0 0.5rem; }
.cz-work-forces { font-style: italic; }
.cz-work-text { line-height: 1.65; margin: 0 0 0.6rem; }

.cz-work-figure {
  overflow: hidden;
  border-radius: var(--cz-radius);
  background: var(--cz-surface);
  margin: 0 0 0.9rem;
}
.cz-work-img { display: block; width: 100%; height: auto; }

.cz-work-frame {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--cz-radius);
  background: var(--cz-surface);
  margin: 0.9rem 0;
}
.cz-work-embed { display: block; width: 100%; height: 100%; border: 0; }

.cz-work-links { margin: 0.75rem 0 0; font-size: 0.9rem; }
.cz-work-link { margin-right: 1rem; white-space: nowrap; }
/* Kind affordances: a score is a download, not a destination — and a
   self-hosted file always is, whatever it holds. */
.cz-work-link[data-kind="score"]::before,
.cz-work-link[data-kind="download"]::before { content: "\2193\00a0"; }
.cz-work-more { margin: 0.6rem 0 0; font-size: 0.9rem; }
.cz-work-more-link { color: var(--cz-accent-deep); }

/* Cards variant — image-forward tiles, one across → two → three. */
.cz-works-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem 1.5rem;
  align-items: start;
  margin: 1.25rem 0;
}
@media (min-width: 40rem) { .cz-works-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .cz-works-grid { grid-template-columns: repeat(3, 1fr); } }

.cz-works--cards .cz-work {
  border: 1px solid var(--cz-line);
  border-radius: var(--cz-radius);
  padding: 1.25rem;
}
.cz-works--cards .cz-work-figure { margin: -1.25rem -1.25rem 1rem; border-radius: 0; }
.cz-works--cards .cz-work-img { aspect-ratio: 3 / 2; object-fit: cover; }
.cz-work-title {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1.3;
  margin: 0 0 0.2rem;
}
.cz-works--cards .cz-work-year { display: block; margin: 0 0 0.5rem; }


/* --- press-kit module (cz-*) -------------------------------------------- */
.cz-presskit { font-family: var(--cz-font-body); margin: 0 0 2.5rem; }
.cz-presskit-link { color: var(--cz-link); text-decoration: none; }
.cz-presskit-link:hover { color: var(--cz-link-hover); }
.cz-presskit-affix { font-weight: 600; }

/* Line — a bare row of links. Flex + gap rather than margins, so a row
   that wraps keeps even spacing on every line. */
.cz-presskit--line,
.cz-presskit-links {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 1.25rem;
  margin: 0;
  font-size: 0.9rem;
}
.cz-presskit--line .cz-presskit-link + .cz-presskit-link::before,
.cz-presskit-links .cz-presskit-link + .cz-presskit-link::before {
  content: var(--cz-presskit-sep, "");
  color: var(--cz-text-muted);
  margin-right: 1.25rem;
}

/* Panel — the same row under a heading, between rules. */
.cz-presskit--panel {
  border-top: 1px solid var(--cz-line);
  border-bottom: 1px solid var(--cz-line);
  padding: 1rem 0;
}
.cz-presskit-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin: 0 0 0.5rem;
}


/* --- bio module (cz-*) --------------------------------------------------- */
.cz-bio { font-family: var(--cz-font-body); color: var(--cz-text); }

.cz-bio-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.25rem;
  line-height: 1.3;
  margin: 2rem 0 1rem;
}
.cz-bio-section:first-child > .cz-bio-heading { margin-top: 0; }

/* A photograph introducing a section — the author breaking up their own long
   prose. No height here: the ladder (cz-cover--strip / standard / tall / full)
   is the design's to draw, and a design that draws none renders the picture at
   its own aspect, which is a photograph rather than a broken page. */
.cz-section-image {
  display: block;
  width: 100%;
  object-fit: cover;
  margin: 2rem 0;
  border-radius: var(--cz-radius);
}
.cz-section-image:first-child { margin-top: 0; }

/* The roster line. The separator is CSS so a design can change a middot to a
   slash without new markup, and the last player must not trail one. */
.cz-bio-roster {
  margin: 0 0 2rem;
  color: var(--cz-bio-roster-color, var(--cz-text-muted));
  font-size: var(--cz-bio-roster-size, 0.75rem);
  font-family: var(--cz-bio-roster-font, inherit);
  letter-spacing: var(--cz-bio-roster-tracking, 0.1em);
  text-transform: var(--cz-bio-roster-transform, uppercase);
  text-align: var(--cz-bio-roster-align, left);
}
.cz-bio-player + .cz-bio-player::before { content: var(--cz-bio-roster-sep, "\00a0\00b7\00a0"); }

.cz-bio-section p { margin: 0 0 1rem; line-height: 1.7; }
.cz-bio-section p:last-child { margin-bottom: 0; }
.cz-bio-section + .cz-bio-section { margin-top: 1rem; }

/* "lead" — the opening run, set larger. The size is a hook because the five
   designs that enlarge it disagree by a step and one of them (nocturne) also
   lightens the weight. */
.cz-bio-section--lead p {
  font-size: var(--cz-bio-lead-size, 1.125rem);
  font-weight: var(--cz-bio-lead-weight, inherit);
}

/* "panel" — a boxed run. Every part defaults to invisible in the
   --cz-chip-bg idiom, so a variant that panels on a design which sets none of
   them still reads as a plain run rather than an empty box. The border is
   split style/width/colour for contactModule's reason: three of the four
   designs that panel draw the border on one side only. */
.cz-bio-section--panel {
  margin: 1.5rem 0;
  background: var(--cz-bio-panel-bg, transparent);
  border-style: var(--cz-bio-panel-border-style, none);
  border-width: var(--cz-bio-panel-border-width, 0);
  border-color: var(--cz-bio-panel-border-color, var(--cz-accent));
  border-radius: var(--cz-radius);
  padding: var(--cz-bio-panel-pad, 0);
}
.cz-bio-section--panel > .cz-bio-heading { margin-top: 0; }

/* Roles. "cards" is a grid of boxes, "list" a plain run of entries, "steps"
   the same run with a numbered badge in a leading column. */
.cz-roles { display: grid; gap: var(--cz-role-gap, 1.5rem); margin-top: 1.5rem; }
@media (min-width: 768px) {
  .cz-roles--cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.cz-roles--cards .cz-role {
  background: var(--cz-role-card-bg, transparent);
  border-style: var(--cz-role-card-border-style, none);
  border-width: var(--cz-role-card-border-width, 0);
  border-color: var(--cz-role-card-border-color, var(--cz-accent));
  border-radius: var(--cz-radius);
  box-shadow: var(--cz-role-card-shadow, none);
  padding: var(--cz-role-card-pad, 0);
}

.cz-roles--steps .cz-role { display: flex; gap: 1rem; align-items: flex-start; }
/* The badge is the module's; its shape is the design's. A design that sets
   nothing gets a plain number rather than a broken circle. */
.cz-role-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--cz-role-step-size, 2rem);
  height: var(--cz-role-step-size, 2rem);
  border-radius: var(--cz-role-step-radius, 50%);
  background: var(--cz-role-step-bg, transparent);
  color: var(--cz-role-step-fg, var(--cz-text));
  font-weight: 700;
}
.cz-role-main { flex: 1; min-width: 0; }

.cz-role-org {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.05rem;
  margin: 0;
}
.cz-role-link { color: inherit; text-decoration: none; }
.cz-role-link:hover { color: var(--cz-link-hover); }
.cz-role-meta { margin: 0.25rem 0 0.5rem; font-size: 0.9rem; color: var(--cz-text-muted); }
.cz-role-body p { margin: 0 0 0.75rem; font-size: 0.95rem; line-height: 1.65; }
.cz-role-body p:last-child { margin-bottom: 0; }

.cz-bio-empty { color: var(--cz-text-muted); }

/* The short variant: a home-page teaser, so it is set a step larger than
   body copy and the link reads as the page's one call to action. Both are
   hooks with fallbacks, the button being the part designs disagree about
   most — bravura fills it with the accent, nocturne leaves it a plain link. */
.cz-bio-short-line {
  font-size: var(--cz-bio-short-size, 1.125rem);
  line-height: 1.7;
  margin: 0 0 1rem;
  max-width: var(--cz-bio-short-measure, 48rem);
}
.cz-bio-more {
  display: inline-block;
  margin-top: 0.5rem;
  padding: var(--cz-bio-more-pad, 0.75rem 1.5rem);
  background: var(--cz-bio-more-bg, var(--cz-accent));
  color: var(--cz-bio-more-fg, var(--cz-on-accent));
  border-radius: var(--cz-radius);
  font-size: 0.875rem;
  letter-spacing: var(--cz-bio-more-tracking, 0.1em);
  text-transform: var(--cz-bio-more-transform, uppercase);
  text-decoration: none;
}
.cz-bio-more:hover { background: var(--cz-bio-more-bg-hover, var(--cz-accent-hover)); }


/* --- members module (cz-*) ---------------------------------------------- */
.cz-members { font-family: var(--cz-font-body); color: var(--cz-text); }

.cz-members-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.35rem;
  line-height: 1.3;
  margin: 0 0 1.5rem;
}

.cz-members-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--cz-member-gap, 2.5rem);
}

/* Cards and portraits are grids across; stacked and prose are one column of
   full-width units, so they ignore the column count entirely. */
@media (min-width: 768px) {
  .cz-members--cards .cz-members-list[data-cz-columns="2"],
  .cz-members--portraits .cz-members-list[data-cz-columns="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cz-members--cards .cz-members-list[data-cz-columns="3"],
  .cz-members--portraits .cz-members-list[data-cz-columns="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cz-members--cards .cz-members-list[data-cz-columns="4"],
  .cz-members--portraits .cz-members-list[data-cz-columns="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .cz-members--cards .cz-members-list[data-cz-columns="3"],
  .cz-members--portraits .cz-members-list[data-cz-columns="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cz-members--cards .cz-members-list[data-cz-columns="4"],
  .cz-members--portraits .cz-members-list[data-cz-columns="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* The unit. The card treatment is hooks defaulting to invisible, in the
   --cz-chip-bg idiom: a design that wants a raised white card sets a ground,
   a padding and a shadow; a design that wants the block on the bare page
   sets none and gets exactly that. `--cz-member-rule` is the separate
   hairline-above treatment (fanfare rules each player off). */
.cz-member {
  background: var(--cz-member-card-bg, transparent);
  border-top: var(--cz-member-rule, none);
  border-radius: var(--cz-radius);
  box-shadow: var(--cz-member-card-shadow, none);
  padding: var(--cz-member-card-pad, 0);
}

/* Stacked — portrait in a side column beside the prose. The column width is
   a hook because the eight that do this disagree by design (10rem, 11rem,
   14rem, a third of the row) and it takes a fraction as readily as a length,
   which is what lets toccata's 3-column grid and sonata's fixed 11rem be the
   same rule. */
@media (min-width: 640px) {
  /* Only an imaged member becomes two columns: without the modifier a member
     with no photograph would lay its prose out in the *portrait* column. */
  .cz-members--stacked .cz-member--imaged {
    display: grid;
    grid-template-columns: var(--cz-member-portrait-width, 12rem) 1fr;
    gap: 1.5rem;
    align-items: start;
  }
}

.cz-member-portrait {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--cz-member-portrait-aspect, auto);
  object-fit: cover;
  border: var(--cz-member-portrait-border, none);
  border-radius: var(--cz-member-portrait-radius, 0);
  box-shadow: var(--cz-member-portrait-shadow, none);
}
.cz-member-portrait-link { display: block; }

.cz-members--cards .cz-member-portrait,
.cz-members--portraits .cz-member-portrait { margin-bottom: 1rem; }

/* A portrait fills its cell, so *fewer* columns means *bigger* pictures —
   the opposite of what the column clamp is for. Two players in a wall
   declared four across would otherwise get half-page posters. The cap is
   above any cell width the declared counts produce at the measures these
   designs use, so it only bites when the clamp has widened the cell. */
.cz-members--portraits .cz-member {
  text-align: center;
  max-width: var(--cz-member-portrait-max, 15rem);
  margin-inline: auto;
}

.cz-member-name {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.15rem;
  line-height: 1.35;
  margin: 0;
}
.cz-member-link { color: inherit; text-decoration: none; }
.cz-member-link:hover { color: var(--cz-link-hover); }
/* Madrigal's grid tells the reader the portrait goes somewhere. Decoration,
   so it is a hook rather than markup — the same call as --cz-presskit-sep. */
.cz-member-link::after { content: var(--cz-member-affix, ""); }

/* The instrument. Two designs put it on its own line under the name and six
   run it on, so the display is a hook; the separator that precedes it when
   inline is another, since three designs draw one and three don't. */
.cz-member-instrument {
  display: var(--cz-member-instrument-display, inline);
  font-family: var(--cz-font-body);
  font-size: 0.85rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--cz-member-instrument-color, var(--cz-text-muted));
}
/* No separator by default, because the default is no separator: the three
   designs that put the instrument on its own line want nothing before it,
   and a space there indents that line out of alignment with the name. The
   five that run it on set their own — with non-breaking spaces, ordinary
   trailing whitespace being dropped at the end of the pseudo-element box. */
.cz-member-instrument::before { content: var(--cz-member-sep, ""); }

.cz-member-body { margin-top: 0.85rem; line-height: 1.7; }
.cz-member-body p { margin: 0 0 0.85rem; }
.cz-member-body p:last-child { margin-bottom: 0; }
.cz-members-empty { color: var(--cz-text-muted); }


/* --- contact module (cz-*) ---------------------------------------------- */
.cz-contact { font-family: var(--cz-font-body); color: var(--cz-text); }

.cz-contact-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}
@media (min-width: 768px) {
  .cz-contact-cards[data-cz-columns="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cz-contact-cards[data-cz-columns="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .cz-contact-cards[data-cz-columns="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.cz-contact-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.05rem;
  line-height: 1.3;
  margin: 0 0 0.75rem;
}
.cz-contact-name { font-weight: 600; margin: 0 0 0.25rem; }
.cz-contact-address { margin: 0 0 0.75rem; font-size: 0.9rem; line-height: 1.6; }
.cz-contact-address p { margin: 0; }
.cz-contact-details { font-size: 0.9rem; line-height: 1.6; }
.cz-contact-row { margin: 0; }
.cz-contact-row--email { overflow-wrap: anywhere; }
.cz-contact-link { color: var(--cz-link); text-decoration: none; }
.cz-contact-link:hover { color: var(--cz-link-hover); }
.cz-contact-empty { color: var(--cz-text-muted); }

/* Cards — each entry in its own box. The surface is a hook rather than the
   ramp's own `surface`, because four designs box the card against the page
   ground and two of those want white on a tinted page. */
.cz-contact--cards .cz-contact-card {
  background: var(--cz-contact-card-bg, transparent);
  border: 1px solid var(--cz-line);
  border-radius: var(--cz-radius);
  padding: 1.5rem;
}

/* Ruled — a hairline above each entry instead of a box (madrigal's whole
   design language). The rule is a hook so the width, style and colour move
   together; a design that doesn't set it gets no rule and no padding. */
.cz-contact--ruled .cz-contact-card {
  border-top: var(--cz-contact-rule, 1px solid var(--cz-line));
  padding-top: 1rem;
}

/* Plain — no chrome. The card is a group of paragraphs and nothing else. */

/* The form block's chrome is hooks, all defaulting to invisible, in the
   --cz-chip-bg/--cz-chip-fg idiom: a design that wants the form to read as a
   panel sets a surface, a border and the padding that goes with them; a design
   that wants it to sit on the page sets none and gets exactly that.

   The border is split into style/width/colour rather than one shorthand
   because two of the eight draw it on **one side** — madrigal rules the form
   off above it, toccata puts a 4px accent bar down its left — and a border
   shorthand cannot say that. Padding is its own hook for a related reason:
   CSS cannot ask whether a border was set, so the padding a panel needs has
   to be stated. */
.cz-contact-form {
  margin-top: 2.5rem;
  max-width: 36rem;
  background: var(--cz-contact-form-bg, transparent);
  border-style: var(--cz-contact-form-border-style, none);
  border-width: var(--cz-contact-form-border-width, 0);
  border-color: var(--cz-contact-form-border-color, var(--cz-line));
  border-radius: var(--cz-radius);
  padding: var(--cz-contact-form-pad, 0);
}
.cz-contact-form-heading {
  font-family: var(--cz-font-heading);
  color: var(--cz-heading-color, var(--cz-text));
  font-size: 1.05rem;
  margin: 0 0 0.5rem;
}
.cz-contact-form-text { margin: 0 0 1rem; font-size: 0.9rem; }
.cz-contact-fields { max-width: 36rem; }
.cz-contact-field {
  display: block;
  width: 100%;
  box-sizing: border-box;
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--cz-text);
  background: var(--cz-contact-field-bg, transparent);
  border: 1px solid var(--cz-line);
  border-radius: var(--cz-radius);
  padding: 0.75rem 1rem;
  margin: 0 0 0.75rem;
}
.cz-contact-field:focus { outline: none; border-color: var(--cz-accent); }
.cz-contact-field::placeholder { color: var(--cz-text-muted); }
.cz-contact-button {
  display: inline-block;
  font-family: inherit;
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  color: var(--cz-on-accent);
  background: var(--cz-accent);
  border: 1px solid var(--cz-accent);
  border-radius: var(--cz-radius);
  padding: 0.75rem 2rem;
}
.cz-contact-button:hover { background: var(--cz-accent-hover); border-color: var(--cz-accent-hover); }

/* =========================================================================
   Ottavo — the poster outside the hall.

   Stage-dark ground with a warm plum bias, warm ivory type, one carmine
   accent. Bodoni Moda carries the display voice (a concert poster's didone),
   Archivo does the quiet work. The photography is the design: the hero is
   the viewport, the page covers are full-bleed, and everything else stays
   out of the pictures' way.

   Plain CSS, no build step. The :root block is the --cz-* contract — the
   eighteen tokens the shared modules read without a fallback, then the
   per-design hooks they read *with* one. Module stylesheets are concatenated
   ahead of this file, so anything here wins without specificity tricks.

   TWO PALETTES, both hand-drawn (docs/slot-templates.md, "Colour modes").
   The dark one is this design's native voice and is unchanged to the byte.
   The light one is the same poster in daylight rather than the dark one
   inverted — inverting is the deleted ramp deriver and the one thing that
   must not come back. So: warm bone rather than white (a cold white would be
   a different design beside that ivory type), a plum-black ink that keeps the
   dark ground's bias, and the carmine taken *down* the lightness scale until
   it holds on paper, where in the dark it sits above the ground.

   Three mechanics, each with a reason that is not obvious:

   1. `light-dark()` carries both values in one declaration, so a token has
      one home rather than two to drift apart. The mode is chosen entirely by
      `color-scheme`: `light dark` follows the reader, and the two
      [data-cz-mode] rules at the end of this block force one. That is the
      whole implementation of site.colorMode here.
   2. The accent families are declared as --o-* and *mapped* to --cz-*.
      accents.ts refuses a property declared more than once — it has no way to
      tell which declaration the customer meant — so redeclaring --cz-accent
      per mode would break /color and the intake colour step outright. The
      gauge substitutes --o-accent-*, declared once; --cz-accent is this
      design's own mapping and the gauge never sees it. The gauge drives the
      **dark** family because dark is what this design ships and what its
      contrast pairs are declared against; the light family is the designer's
      until the gauge learns to rotate both.
   3. Every hook is a var() onto a contract token, so hooks follow the mode
      for free. --cz-chip-fg was the exception — a literal, and therefore
      exactly the hook that would have kept its dark value in the light — so
      it is a gauge property with a light twin like everything else.
   ========================================================================= */

:root {
  /* Both, so the reader's own setting decides; the two rules at the end of
     this block are what a site that has forced a mode gets instead. */
  color-scheme: dark light;

  /* --- the accent family, dark: the gauge's properties ------------------ */
  /* Exactly one declaration each — accents.json substitutes these by name.
     Not used directly; the mapping is under "the contract". */
  --o-accent: #b13a44;
  --o-accent-hover: #c94f59;
  --o-accent-active: #d96570;
  --o-accent-bright: #e2717b;
  --o-accent-soft: #33222a;
  --o-accent-deep: #6f1d26;
  --o-on-accent: #ffffff;
  --o-on-accent-muted: #f0cdd0;
  --o-link: #d98a91;
  --o-link-hover: #e5a3a9;
  --o-chip-fg: #e8b9be;

  /* --- the accent family, light: hand-drawn ----------------------------- */
  /* The carmine walked *down* rather than up: on bone it has to be the
     darker thing, so "soft" is a pale wash and "deep" the near-oxblood, the
     mirror of what they are in the dark. --o-light-on-accent stays white
     because the accent it sits on is dark in this mode too. */
  --o-light-accent: #a32f3a;
  --o-light-accent-hover: #8c2831;
  --o-light-accent-active: #75212a;
  --o-light-accent-bright: #c4444f;
  --o-light-accent-soft: #f4e2e3;
  --o-light-accent-deep: #5e1820;
  --o-light-on-accent: #ffffff;
  --o-light-on-accent-muted: #f0cdd0;
  --o-light-link: #a32f3a;
  --o-light-link-hover: #8c2831;
  --o-light-chip-fg: #7a2029;

  /* --- the neutrals, both modes ----------------------------------------- */
  --o-ground: #131016;
  --o-surface: #1c1821;
  --o-text: #ede7dd;
  --o-text-muted: #a89d90;
  --o-line: #2e2833;
  --o-light-ground: #f7f3ee;
  --o-light-surface: #ece5dc;
  --o-light-text: #1c161a;
  --o-light-text-muted: #6a6058;
  --o-light-line: #ddd3c7;

  /* --- the contract ---------------------------------------------------- */
  --cz-ground: light-dark(var(--o-light-ground), var(--o-ground));
  --cz-surface: light-dark(var(--o-light-surface), var(--o-surface));
  --cz-text: light-dark(var(--o-light-text), var(--o-text));
  --cz-text-muted: light-dark(var(--o-light-text-muted), var(--o-text-muted));
  --cz-accent: light-dark(var(--o-light-accent), var(--o-accent));
  --cz-accent-hover: light-dark(var(--o-light-accent-hover), var(--o-accent-hover));
  --cz-accent-active: light-dark(var(--o-light-accent-active), var(--o-accent-active));
  --cz-accent-bright: light-dark(var(--o-light-accent-bright), var(--o-accent-bright));
  --cz-accent-soft: light-dark(var(--o-light-accent-soft), var(--o-accent-soft));
  --cz-accent-deep: light-dark(var(--o-light-accent-deep), var(--o-accent-deep));
  --cz-on-accent: light-dark(var(--o-light-on-accent), var(--o-on-accent));
  --cz-on-accent-muted: light-dark(var(--o-light-on-accent-muted), var(--o-on-accent-muted));
  --cz-link: light-dark(var(--o-light-link), var(--o-link));
  --cz-link-hover: light-dark(var(--o-light-link-hover), var(--o-link-hover));
  --cz-font-heading: "Bodoni Moda", "Bodoni MT", Didot, "Times New Roman", serif;
  --cz-font-body: "Archivo", ui-sans-serif, system-ui, sans-serif;
  /* The weight each face is set at when it heads something. A hook rather
     than two of the eighteen — everything below consumes them with a
     fallback — but written by /type beside the families above, which is why
     they live here and not down with the module hooks.

     They exist because `font-weight: 500` was a lie this design told itself:
     it is one step over body in Bodoni, two steps' worth of ink in Oswald,
     the tamest register Syne has, and *nothing at all* in the five catalogued
     faces with no weight axis, where 500 resolves to the one weight they
     ship. That single step was the whole hierarchy, so on those faces the
     hierarchy simply wasn't there — a section heading lighter than the
     sentence under it (found by eye five times over in the 2026-08-20 type
     audit, before anyone read a font query). Each face now states the weight
     it wants and /type writes that number here, so the value is always one
     the font actually has and never one the browser has to invent.

     Ottavo's own pair is Bodoni Moda's 500 and Archivo's 500, which is what
     the file said everywhere before it could vary. */
  --cz-font-heading-weight: 500;
  --cz-font-body-weight: 500;
  --cz-line: light-dark(var(--o-light-line), var(--o-line));
  /* The corner, and theme.json's one knob (2026-08-21). Sharp is what this
     design is: a poster is trimmed, not rounded, and the ladder stops at 4px
     because a didone at StartUI's 1rem is a different and worse design. It
     reaches the boxes the modules paint — photo frames, covers, chips, cards,
     contact fields — plus the four this file paints for itself (the bio
     portrait, a custom page's photographs, the nav panel, the menu toggle).
     What it deliberately never reaches is anything full-bleed: the hero, the
     page cover and the bands run to the viewport edge, and a corner there
     would be a rounded *page*. */
  --cz-radius: 0px;

  /* --- hooks: read by the modules with a fallback ----------------------- */
  --cz-heading-color: var(--cz-text);
  --cz-chip-bg: var(--cz-accent-soft);
  --cz-chip-fg: light-dark(var(--o-light-chip-fg), var(--o-chip-fg));
  /* day-month dates ("14 March") in the schedule's list column. */
  --cz-event-date-width: 7.5rem;
  --cz-contact-rule: 1px solid var(--cz-line);
  /* A player's portrait is a photograph in a box like any other, so it takes
     the same corner rather than a second opinion about one. */
  --cz-member-portrait-radius: var(--cz-radius);
  --cz-member-instrument-color: var(--cz-text-muted);
  /* On its own line under the name — inline, it glues to the heading. */
  --cz-member-instrument-display: block;
  --cz-bio-roster-font: var(--cz-font-body);
  --cz-bio-roster-tracking: 0.16em;
  --cz-bio-roster-transform: uppercase;
  --cz-bio-roster-size: 0.72rem;
  --cz-bio-roster-color: var(--cz-text-muted);

  /* --- this design's own ------------------------------------------------ */
  --o-measure: 74rem;
  --o-narrow: 46rem;
  --o-gutter: 1.5rem;
  /* The bottom two rungs of the scale — a module's section heading, and the
     title of one item in a list. Named here rather than left to the eleven
     modules because a scale is a single judgement and they each hold a
     fragment of it; see "the rank that cannot come from ink" below. */
  --o-heading-section: 1.5rem;
  --o-heading-item: 1.25rem;
  --o-rule: 1px solid var(--cz-line);
  /* One inline geometry for the hero and the shell, so type over the
     photograph lines up with type on the page. */
  --o-inset: max(var(--o-gutter), calc((100vw - var(--o-measure)) / 2));
  /* The page cover's fade into the page. It is the one scrim that has to
     follow the mode, and the distinction is worth writing down because the
     other two look identical in the source: a scrim over a *photograph*
     (the hero, the lightbox) stays dark in both modes — it is there so ivory
     type reads over a picture, and a photo viewer is dark in daylight too —
     while a scrim that blends a photograph into the *page ground* has to be
     the page ground, whatever the page ground currently is.

     Mixed off the token rather than written as two literals (2026-08-20):
     "whatever the page ground currently is" stopped being true the day the
     base role could turn it, and a pair of frozen hexes meant a Forest site's
     cover faded through a 25% plum on its way into a green page — a hue
     appearing nowhere else on the page, and invisible in review unless you
     picked a distant ground and opened a page that has a cover. One
     declaration now follows both the mode and the ground. */
  --o-cover-fade: color-mix(in srgb, var(--cz-ground) 25%, transparent);
  /* Type over a photograph. Deliberately *not* light-dark(), which is the
     whole point: the hero's scrim is dark in both modes, so the type on top
     of it is the dark palette's ivory in both modes too. A photograph is not
     the page ground and type on it must not follow the page ground — in the
     light mode that is plum-black ink over a scrimmed picture, which is
     invisible on any photograph darker than the page, and a customer's one
     uploaded photograph is dark about half the time. */
  --o-on-photo: var(--o-text);
  --o-on-photo-muted: var(--o-text-muted);
  --o-photo-shadow: 0 1px 10px rgba(10, 8, 12, 0.8);
}

/* The forced modes (site.colorMode). `auto` stamps nothing, so a site that
   has not chosen keeps the `dark light` above and follows its reader. No
   palette belongs in these rules: switching the used colour scheme is what
   re-resolves every light-dark() on the page. */
:root[data-cz-mode="light"] { color-scheme: light; }
:root[data-cz-mode="dark"] { color-scheme: dark; }

/* -------------------------------------------------------------------------
   Base
   ------------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background: var(--cz-ground);
  color: var(--cz-text);
  font-family: var(--cz-font-body);
  font-size: 1rem;
  line-height: 1.7;
}

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

a { color: var(--cz-link); }
a:hover { color: var(--cz-link-hover); }

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

::selection { background: var(--cz-accent-deep); color: var(--cz-on-accent); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--cz-accent);
  color: var(--cz-on-accent);
  padding: 0.6rem 1rem;
  border-radius: var(--cz-radius);
  z-index: 40;
}
.skip-link:focus { left: 0; }

.shell {
  width: 100%;
  max-width: var(--o-measure);
  margin-inline: auto;
  padding-inline: var(--o-gutter);
}
/* Cap the children, not the container — a capped container re-centres and
   the page title drifts off the text's left edge (quarto's lesson). */
.shell--narrow > * { max-width: var(--o-narrow); }

.cz-event-date, .cz-track-number, .cz-track-duration, .cz-work-year, .cz-date {
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------
   Masthead
   ------------------------------------------------------------------------- */

.bar {
  /* The bar's ink follows whatever is *behind* the bar, and that is the page
     ground everywhere except over the hero photograph, where it is the
     photograph. Held in variables rather than written as a chain of colour
     overrides because four things take the colour and there are three
     grounds to take it from, and `:has()` carries its argument's
     specificity — as overrides it is a specificity puzzle, as inherited
     variables it is a cascade. Anything inside the bar that paints its own
     ground (the stacked Menu, a group's panel) sets them back. */
  --o-bar-ink: var(--cz-text);
  --o-bar-ink-muted: var(--cz-text-muted);
  --o-bar-line: var(--cz-line);
  --o-bar-shadow: none;
  background: var(--cz-ground);
  border-bottom: var(--o-rule);
}
.bar-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  padding-block: 1.1rem;
  /* The anchor for the More panel: its `top: 100%` is the bottom of the bar
     row rather than the bottom of the menu's own box, which sits inside the
     row's padding and would leave the panel overlapping the bar. */
  position: relative;
}

.brand { display: flex; align-items: baseline; gap: 1rem; min-width: 0; }
.mark { text-decoration: none; color: inherit; display: inline-flex; align-items: center; }
.cz-wordmark {
  font-family: var(--cz-font-heading);
  font-weight: var(--cz-font-heading-weight, 500);
  font-size: 1.05rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--o-bar-ink, var(--cz-text));
  text-shadow: var(--o-bar-shadow, none);
  white-space: nowrap;
}
.cz-logo { display: block; }
/* Sized by HEIGHT, not by a max-height, and that is not a style preference:
   `.mark` is an inline-flex box, an SVG with a viewBox and no width/height
   attributes has a ratio but no intrinsic size, and flexbox resolves that
   pair to a 0×0 item — the mark simply does not paint, in a browser only,
   with nothing wrong in the markup. A stated height gives the ratio
   something to multiply. `max-width` then keeps a genuinely wide wordmark
   from crowding the menu on a narrow bar. */
.cz-logo--wide { height: 2.4rem; width: auto; max-width: 15rem; }
.cz-logo--square { height: 2.4rem; width: auto; }

/* The logo slot asks for `data-cz-ground="auto"` — the bar's ground is
   --cz-ground and this design has two. Under a forced mode the engine picks
   one file and nothing here applies; under `auto` it emits both grounds'
   artwork and these rules choose. The one place light-dark() cannot help,
   `display` not being a colour, so it is the two-block pattern. The wordmark
   is classed alongside: class only the picture and a site whose artwork suits
   one ground shows its fallback text in both modes. */
.cz-logo--on-light, .cz-wordmark--on-light { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-cz-mode="dark"]) .cz-logo--on-dark,
  :root:not([data-cz-mode="dark"]) .cz-wordmark--on-dark { display: none; }
  :root:not([data-cz-mode="dark"]) .cz-logo--on-light { display: block; }
  :root:not([data-cz-mode="dark"]) .cz-wordmark--on-light { display: inline; }
}
:root[data-cz-mode="light"] .cz-logo--on-dark,
:root[data-cz-mode="light"] .cz-wordmark--on-dark { display: none; }
:root[data-cz-mode="light"] .cz-logo--on-light { display: block; }
:root[data-cz-mode="light"] .cz-wordmark--on-light { display: inline; }
.bar-tagline {
  margin: 0;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--o-bar-ink-muted, var(--cz-text-muted));
  text-shadow: var(--o-bar-shadow, none);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bar-tagline:empty { display: none; }

/* Nav: a quiet row of tracked caps; the current page carries the accent. */
.cz-nav-list {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.35rem 1.15rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.cz-nav-link {
  display: inline-block;
  text-decoration: none;
  color: var(--o-bar-ink-muted, var(--cz-text-muted));
  text-shadow: var(--o-bar-shadow, none);
  font-size: 0.72rem;
  /* The body face's own label weight, not the heading face's: this row is set
     in --cz-font-body. Lato is the case that proves it needs asking — it
     ships 400 and 700 with nothing between, so the bar asked for 500 and got
     400, and the floating menu over the hero rendered a step light with
     nothing in the source to say why. */
  font-weight: var(--cz-font-body-weight, 500);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding-block: 0.25rem;
  border-bottom: 2px solid transparent;
}
.cz-nav-link:hover { color: var(--o-bar-ink, var(--cz-text)); }
.cz-nav-link--current {
  color: var(--o-bar-ink, var(--cz-text));
  border-bottom-color: var(--cz-accent);
}
.cz-nav-group { display: flex; flex-direction: column; gap: 0.2rem; }
.cz-nav-heading {
  display: block;
  font-size: 0.72rem;
  font-weight: var(--cz-font-body-weight, 500);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--o-bar-ink-muted, var(--cz-text-muted));
  text-shadow: var(--o-bar-shadow, none);
  padding-block: 0.25rem;
  border-bottom: 2px solid transparent;
}
.cz-nav-group > .cz-nav-list { gap: 0.15rem 0.9rem; }
/* Child-scoped, and that matters: a group whose label names a landing page
   renders as `<a class="cz-nav-heading cz-nav-link">`, so a descendant
   selector here restyles the *heading* into a member — one label in the row
   quietly lowercase and a size smaller than its neighbours. */
.cz-nav-group > .cz-nav-list .cz-nav-link {
  letter-spacing: 0.02em;
  text-transform: none;
  font-weight: 400;
  font-size: 0.78rem;
}

/* Groups as panels, above the collapse breakpoint only (2026-08-20).
   -----------------------------------------------------------------
   They used to sit open in the bar — heading in the row, members on a line
   beneath it — which is honest markup and reads fine with one group and
   four pages. It falls apart at the size a real site reaches: a full page
   set with two groups wrapped the bar to three ragged rows, and because
   every member is a sibling in the same wrapping flex row, a group's
   members came to rest beside a *different* group's heading. You could not
   tell what belonged to what, which is worse than a dropdown.
   So above the breakpoint a group's list becomes a panel under its own
   heading, and below it nothing changes: the whole nav is already the
   stacked Menu, where an open list is exactly right and no pointer is
   involved. That split is deliberate — a hover-only affordance on touch is
   the reason `nav-open` exists in site.js, not an argument for it here. */
@media (min-width: 64.01rem) {
  .cz-nav-group { position: relative; }
  /* The heading reads as openable: a caret, and a hit area that includes
     the gap the panel appears in. */
  .cz-nav-heading { cursor: default; }
  .cz-nav-group > .cz-nav-heading::after {
    content: "";
    display: inline-block;
    width: 0.32em;
    height: 0.32em;
    margin-left: 0.5em;
    vertical-align: 0.15em;
    border-right: 1px solid currentColor;
    border-bottom: 1px solid currentColor;
    transform: rotate(45deg);
    opacity: 0.7;
  }
  .cz-nav-group > .cz-nav-list {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 40;
    display: block;
    min-width: max-content;
    /* Flush under the heading, deliberately: a gap here is outside the
       group's own box, so the pointer crossing it loses :hover and the
       panel shuts on the way to it. */
    margin-top: 0;
    padding: 0.7rem 1rem;
    background: var(--cz-surface);
    border: var(--o-rule);
    border-radius: var(--cz-radius);
    box-shadow: 0 14px 34px light-dark(rgba(28, 22, 26, 0.14), rgba(0, 0, 0, 0.45));
    /* Not `display: none`: a hidden panel cannot take focus, so the
       keyboard could never reach a grouped page. `visibility` keeps it out
       of the tab order while shut and lets :focus-within open it. */
    visibility: hidden;
    opacity: 0;
    transition: opacity 120ms ease;
  }
  .cz-nav-group > .cz-nav-list > .cz-nav-item + .cz-nav-item { margin-top: 0.35rem; }
  .cz-nav-group:hover > .cz-nav-list,
  .cz-nav-group:focus-within > .cz-nav-list,
  .cz-nav-group.is-open > .cz-nav-list {
    visibility: visible;
    opacity: 1;
  }
  /* The last group sits at the right-hand end of a right-justified row, so
     its panel would hang off the page. */
  .bar-row .cz-nav-group:last-of-type > .cz-nav-list { left: auto; right: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .cz-nav-group > .cz-nav-list { transition: none; }
}

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--o-bar-line, var(--cz-line));
  border-radius: var(--cz-radius);
  /* The toggle sits on the bar's ground, not the menu's — it stays over the
     photograph while the menu it opens paints on the page ground under it. */
  color: var(--o-bar-ink, var(--cz-text));
  text-shadow: var(--o-bar-shadow, none);
  font: inherit;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.45rem 0.8rem;
  cursor: pointer;
}

.nav-toggle-more { display: none; }
/* The page you are on is one of the ones that moved (site.js). The row has
   nothing left to mark, so the button carries the accent instead. */
.nav-toggle--current {
  border-color: var(--cz-accent);
  color: var(--o-bar-ink, var(--cz-text));
}

@media (max-width: 64rem) {
  .nav-toggle { display: inline-block; }
  .nav { display: none; flex-basis: 100%; }
  .nav.is-open { display: block; background: var(--cz-ground); }
  .cz-nav-list { flex-direction: column; gap: 0.6rem; padding-block: 0.9rem; }
  /* Both lists are one stacked menu down here, so the split stops meaning
     anything — the second list must not open a second block of padding. */
  .nav > .cz-nav-list + .cz-nav-list { padding-top: 0; }
}

/* The overflow menu, above the breakpoint (2026-08-21).
   -----------------------------------------------------
   The row keeps five; site.js moves the rest into a second list inside the
   same <nav>, which the same toggle opens with the same `is-open` class.
   Painted as a panel here and as a plain continuation of the stacked menu
   below the breakpoint — one menu, two widths, which is what makes this a
   cap on the row rather than a navigation of its own. */
@media (min-width: 64.01rem) {
  /* Only when something actually overflowed: a "More" beside three pages is
     a control that opens an empty box. The class is site.js's, so a visitor
     with no JavaScript gets no button and no missing pages — the row simply
     wraps, exactly as it did before. */
  .bar-row.nav-has-more .nav-toggle {
    display: inline-block;
    /* Painted last, at the far right, after the menu whose tail it holds.
       DOM order keeps the button before the list it controls, which is the
       order a screen reader wants; this is the one place the design lets
       paint order and source order disagree. */
    order: 2;
  }
  .nav-toggle-label { display: none; }
  .nav-toggle-more { display: inline; }
  .nav-more-list {
    position: absolute;
    top: 100%;
    /* The gutter, not zero: `.bar-row` is also `.shell`, and an absolute box
       resolves against the **padding** box — so `right: 0` hung the panel a
       gutter's width past the button that opens it, which is visible and was
       found by measuring rather than by reading. */
    right: var(--o-gutter);
    z-index: 40;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    gap: 0.55rem;
    min-width: 12rem;
    max-width: min(20rem, calc(100vw - 2 * var(--o-gutter)));
    padding: 0.85rem 1.1rem;
    background: var(--cz-surface);
    border: var(--o-rule);
    border-radius: var(--cz-radius);
    box-shadow: 0 14px 34px light-dark(rgba(28, 22, 26, 0.14), rgba(0, 0, 0, 0.45));
    /* A group panel's reasoning, verbatim: not `display: none`, because a
       hidden panel cannot take focus and the keyboard could never reach a
       page that overflowed. */
    visibility: hidden;
    opacity: 0;
    transition: opacity 120ms ease;
  }
  .nav.is-open .nav-more-list { visibility: visible; opacity: 1; }
  /* A group in here is stacked open — the mobile treatment. A flyout off the
     side of a dropdown is a second hover target hanging over the page, and
     the panel is already a column. */
  .nav-more-list .cz-nav-group > .cz-nav-heading::after { display: none; }
  .nav-more-list .cz-nav-group > .cz-nav-list {
    position: static;
    display: flex;
    flex-direction: column;
    visibility: visible;
    opacity: 1;
    min-width: 0;
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .nav-more-list { transition: none; }
}

/* The two things inside the bar that paint a ground of their own: the
   stacked Menu below the breakpoint and a group's panel above it. Both sit
   on --cz-ground/--cz-surface, so they take the page's ink back — over a
   hero photograph the bar's ink is ivory, and ivory on bone is the same
   invisible wordmark that rule exists to prevent. */
.bar .cz-nav-group > .cz-nav-list,
.bar .nav-more-list {
  --o-bar-ink: var(--cz-text);
  --o-bar-ink-muted: var(--cz-text-muted);
  --o-bar-line: var(--cz-line);
  --o-bar-shadow: none;
}
/* The stacked menu takes it too, but **only** below the breakpoint, and that
   guard is the whole reason this is a second rule rather than a third
   selector above. Above the breakpoint `is-open` no longer paints a ground —
   it reveals the More panel and leaves the row where it was, over the hero
   photograph — so an unguarded reset would turn the row plum-black on a
   picture the moment the button was pressed. */
@media (max-width: 64rem) {
  .bar .nav.is-open {
    --o-bar-ink: var(--cz-text);
    --o-bar-ink-muted: var(--cz-text-muted);
    --o-bar-line: var(--cz-line);
    --o-bar-shadow: none;
  }
}

/* The nav is a flex item beside the brand; without a shrink allowance it
   takes its intrinsic single-line width and the list inside never wraps —
   it just runs off the page at laptop widths. */
.bar-row .nav { flex: 1 1 12rem; min-width: 0; }
.bar-row .cz-nav-list { justify-content: flex-end; }
@media (max-width: 64rem) {
  .bar-row .cz-nav-list { justify-content: flex-start; }
}

/* Home, when there is a photograph: the bar floats over the hero, so its
   ground is the photograph. Both inks go to full ivory — muted grey vanished
   over a lit patch — with a breath of shadow under them. */
body:has(.hero-frame .cz-hero-image) .bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
  background: transparent;
  border-bottom-color: transparent;
  --o-bar-ink: var(--o-on-photo);
  --o-bar-ink-muted: var(--o-on-photo);
  --o-bar-line: color-mix(in srgb, var(--o-on-photo) 55%, transparent);
  --o-bar-shadow: var(--o-photo-shadow);
}
/* The mark takes the same rule as the ink, one step further round: over the
   photograph it is the on-dark artwork whatever the mode, because a mark is
   a picture and cannot inherit a colour. `display` is not a colour either,
   so this is the logo's two-block pattern gaining a third block rather than
   a token. The one-pager is excluded because its bar keeps a ground of its
   own — see the sticky rule below. */
body:has(.hero-frame .cz-hero-image):not(:has(.o-section)) .bar .cz-logo--on-light,
body:has(.hero-frame .cz-hero-image):not(:has(.o-section)) .bar .cz-wordmark--on-light {
  display: none;
}
body:has(.hero-frame .cz-hero-image):not(:has(.o-section)) .bar .cz-logo--on-dark {
  display: block;
}
body:has(.hero-frame .cz-hero-image):not(:has(.o-section)) .bar .cz-wordmark--on-dark {
  display: inline;
}
@media (max-width: 64rem) {
  /* The stacked Menu paints on the ground, so it needs the gutter the
     transparent bar does not. (This read 56rem until the panels moved the
     collapse breakpoint, which left the open menu gutterless on a tablet.) */
  body:has(.hero-frame .cz-hero-image) .nav.is-open { padding-inline: 0.75rem; }
}

/* One-page assembly (`.o-section` exists on no other document): the bar
   follows the scroll — a menu of anchors is the one navigation the reader
   needs in reach — translucent over whatever passes under it. The second
   selector re-states the hero condition because the floating rule above
   carries its specificity inside `:has()`, and this must win with a
   photograph on the page too. Bands clear the stuck bar through the engine's
   `--cz-scroll-offset` hook below. */
body:has(.o-section) .bar,
body:has(.o-section):has(.hero-frame .cz-hero-image) .bar {
  position: sticky;
  top: 0;
  left: auto;
  right: auto;
  z-index: 40;
  background: color-mix(in srgb, var(--cz-ground) 88%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border-bottom: var(--o-rule);
  /* 88% of the ground is a ground, so the ink is the page's again even with
     a photograph passing under it. */
  --o-bar-ink: var(--cz-text);
  --o-bar-ink-muted: var(--cz-text-muted);
  --o-bar-line: var(--cz-line);
  --o-bar-shadow: none;
}
/* How tall the bar stands, declared once because two things need it and they
   are the same measurement: a one-pager's bands clear the stuck bar through
   the engine's `--cz-scroll-offset` hook, and a full-screen page cover ends
   where the screen does rather than one bar-height past it. One bar row since
   groups became panels (2026-08-20) — the wrapped three-row bar this used to
   clear was the same defect the panels fixed.

   It is a nominal height, not a measured one: a site with enough top-level
   pages to wrap the bar exceeds it, and CSS cannot ask. Deliberately generous
   for that reason — erring tall makes a full cover finish a little short of
   the fold, where nothing is hidden, instead of tucking a sliver underneath
   it. */
:root {
  --o-bar-height: 5.25rem;
}
/* Below the menu breakpoint the nav collapses behind the toggle, so the bar
   is the brand line and the toggle line. */
@media (max-width: 64rem) {
  :root {
    --o-bar-height: 7.75rem;
  }
}
body:has(.o-section) {
  --cz-scroll-offset: var(--o-bar-height);
}

/* -------------------------------------------------------------------------
   Hero — the viewport is the poster
   ------------------------------------------------------------------------- */

.cz-hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-start;
  gap: 0.5rem;
  min-height: 38vh;
  padding: 6.5rem var(--o-inset) 3.25rem;
}
.cz-hero:has(.cz-hero-image) { min-height: 92svh; }
.cz-hero-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}
/* The scrim: dark at the foot for the name, a breath at the top for the bar. */
.cz-hero:has(.cz-hero-image)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to top, rgba(19, 16, 22, 0.88) 0%, rgba(19, 16, 22, 0.35) 34%, rgba(19, 16, 22, 0) 60%),
    linear-gradient(to bottom, rgba(19, 16, 22, 0.72) 0%, rgba(19, 16, 22, 0) 24%);
}
.cz-hero-name {
  position: relative;
  z-index: 2;
  font-family: var(--cz-font-heading);
  font-weight: var(--cz-font-heading-weight, 500);
  font-size: clamp(2.6rem, 8vw, 6.25rem);
  line-height: 0.98;
  letter-spacing: 0.01em;
  text-wrap: balance;
  color: var(--cz-text);
  margin: 0;
}
.cz-hero-tagline {
  position: relative;
  z-index: 2;
  margin: 0.25rem 0 0;
  font-size: 0.82rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cz-text-muted);
}
.cz-hero:has(.cz-hero-image) .cz-hero-tagline { color: var(--cz-on-accent-muted); }
/* The name and the photo credit sit *on the photograph*, under the scrim
   above — so they are ivory in both modes, not the page's ink. The tagline
   above already was, by having been given a tinted white rather than a
   contract token; this is that by intent rather than by luck. */
.cz-hero:has(.cz-hero-image) .cz-hero-name { color: var(--o-on-photo); }
.cz-hero:has(.cz-hero-image) .cz-hero-credit { color: var(--o-on-photo-muted); }
.cz-hero-credit {
  position: absolute;
  right: var(--o-gutter);
  bottom: 0.9rem;
  z-index: 2;
  margin: 0;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cz-text-muted);
}

/* -------------------------------------------------------------------------
   Page covers, heads, bands
   ------------------------------------------------------------------------- */

/* The per-page art direction: a full-bleed cover under the bar, scrimmed
   into the ground so the page title reads as printed over its foot. */
/* The page cover is declared in each page file rather than once in
   layout.html, because home must not have one — its hero is already a
   photograph the size of the viewport. */
.page-cover { position: relative; }
.page-cover .cz-page-backdrop {
  display: block;
  width: 100%;
  object-fit: cover;
}
/* Four heights the author picks from (`cover.height` on the manifest), drawn
   here because only the design knows what a strip is on this page. `standard`
   is what a cover has always been on ottavo, so a site written before heights
   existed does not move. At `tall` and `full` the crop bites hard, which is
   what the focal point in /photos is for.

   One vocabulary, two scales, and the split is the point. `cz-cover--tall` is
   the author saying "tall" about three different boxes: a page cover, a band
   breaking up the page, and a photograph inside a section. The first two span
   the viewport and are measured against it; the third sits in a column of
   prose — inside the two-column about grid, no less — where a viewport height
   is a tall slot the width of a paragraph. So the *names* are shared and the
   *lengths* are per context, which is what having the design draw the ladder
   is for. */
.cz-page-backdrop.cz-cover--strip { height: 22vh; }
.cz-page-backdrop.cz-cover--standard { height: 44vh; }
.cz-page-backdrop.cz-cover--tall { height: 66vh; }
/* `svh`, not `vh`: on a phone `100vh` is the address bar's worth taller than
   the screen, so "full" would crop a face at the fold. The bar is in flow on
   every page that can carry a cover (the floating bar is the hero's, and home
   has no cover), so subtracting it makes the picture end where the screen
   does rather than one bar-height past it. */
.cz-page-backdrop.cz-cover--full {
  height: calc(100svh - var(--o-bar-height));
}
/* The section ladder: a photograph the author put between their own
   paragraphs. Measured in rem against the column rather than in vh against
   the screen — a "full" picture here is one that fills the reader's view of
   the column, not one that displaces the page. */
.cz-section-image.cz-cover--strip { height: 10rem; }
.cz-section-image.cz-cover--standard { height: 18rem; }
.cz-section-image.cz-cover--tall { height: 28rem; }
.cz-section-image.cz-cover--full { height: 40rem; }
.page-cover:has(.cz-page-backdrop)::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Blends the cover into the page ground, so it is the one scrim that has to
     follow the mode — see --o-cover-fade. `transparent` rather than a literal
     zero-alpha colour: in light mode a fade to `rgba(19,16,22,0)` passes
     through grey on the way out, which is the classic gradient smudge. */
  background: linear-gradient(to top, var(--cz-ground) 0%, var(--o-cover-fade) 45%, transparent 75%);
}
/* When a cover ran, whatever the author put under it sits tight beneath —
   the page's name, usually, and its own writing on a custom page.

   Adjacency, and deliberately: under composed pages a photograph can land
   anywhere, so "tight under a cover" has to be a fact about what is *next to*
   it rather than about which page this is. */
.page-cover:has(.cz-page-backdrop) + .page-head { padding-top: 1.5rem; }
.page-cover:has(.cz-page-backdrop) + .band { padding-block-start: 1.5rem; }

.page-head { padding-block: 4rem 0; }
.page-title, .cz-page-title {
  font-family: var(--cz-font-heading);
  font-weight: var(--cz-font-heading-weight, 500);
  font-size: clamp(2.2rem, 5.5vw, 3.6rem);
  line-height: 1.04;
  letter-spacing: 0.01em;
  text-wrap: balance;
  color: var(--cz-text);
  margin: 0 0 1.5rem;
}
.page-title::after, .cz-page-title::after {
  content: "";
  display: block;
  width: 3.5rem;
  height: 3px;
  margin-top: 1.4rem;
  background: var(--cz-accent);
}

.band { padding-block: 4.5rem; }
.band--intro { padding-block: 4.5rem 3.5rem; }
.band--siblings { padding-block: 2rem; border-top: var(--o-rule); }

/* The band under the page's name opens tighter — it is the top of the page's
   content, not another stop down it. */
.page-head + .band { padding-block: 2.25rem 5rem; }

/* Alternating grounds.
   ---------------------------------------------------------------------
   Painted by *position*, because with composed pages position is the author's
   and a band cannot know in its own markup whether it has landed on surface
   or on ground. `nth-of-type` counts sections, so a photograph between two
   bands does not flip the rhythm: a cover is a `<div>` and every region band
   is a `<section>`.

   This replaced a `--surface` class each page file set by hand, which read
   correctly on ten fixed pages and could not survive anybody moving one. */
.page-regions > section.band:nth-of-type(even) {
  background: var(--cz-surface);
  border-block: var(--o-rule);
}
/* Adjacent surface bands would double their rules. A photograph between them
   is not adjacency, so the general sibling combinator would be wrong here. */
.page-regions > section.band:nth-of-type(even) + section.band:nth-of-type(even) {
  border-top: 0;
}

/* A band the author placed whose region rendered nothing at all — an empty
   press kit, a page's own writing on a page that has none — would be 9rem of
   surface colour where the page should have ended. Gated on `[data-cz-item]`,
   the engine's own region hook that every module emits, so the rule does not
   have to be revisited each time a placement is added to the palette.

   The page's name is not a region and carries no hook, so it is spared by
   name; a module with an empty inventory *does* emit its hook and keeps its
   band, which is right — the author chose it, and "New dates are being
   confirmed." is the answer. */
.page-regions > .band:not(:has([data-cz-item])):not(:has(.cz-page-title)) {
  display: none;
}

/* The band label: tracked caps behind a short carmine dash. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  margin: 0 0 2.25rem;
  font-size: 0.72rem;
  /* The body face's weight, not the heading face's — the eyebrow sets no
     family, so it inherits --cz-font-body from `body`. It looked like a
     heading in the source and is a body-face label, which is exactly the
     confusion a literal 500 in both places hid. */
  font-weight: var(--cz-font-body-weight, 500);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--cz-text-muted);
}
.eyebrow::before {
  content: "";
  width: 2rem;
  height: 2px;
  background: var(--cz-accent);
}

/* Module section headings: the didone, one notch under the page title. */
.cz-heading {
  font-family: var(--cz-font-heading);
  letter-spacing: 0.01em;
  color: var(--cz-text);
}

/* The heading face, everywhere it heads something.
   -----------------------------------------------
   The eleven modules each reach for --cz-font-heading and then answer the
   weight question for themselves, three different ways: 500 in six of them,
   nothing at all in the schedule's event title (a <p>, so it inherits 400 and
   comes out *lighter* than the programme line under it), and the UA's bold in
   the four that render an <hN> — which on a face with one weight is not bold
   at all but a synthesised smear of its 400. All three are literals, and a
   literal is the thing this design is giving up: 500 is one step over body in
   Bodoni, the tamest register Syne has, already a slab in Oswald, and nothing
   whatever in DM Serif Display, Instrument Serif, Marcellus, Prata and
   Italiana, which ship one weight each. So ottavo answers it once, and asks
   the face rather than telling it.

   The figures go lining in the same breath, and it is a statement about this
   design rather than about any face: a concert poster sets its numbers as
   capitals. Cormorant Garamond defaults to old-style, which is right in a
   book and wrong here — curated *Programme* rendered "Vol. 1" as "Vol. ɪ",
   "Disc 1" as "Disc ɪ", and dropped the 2026 dividing the diary to x-height
   beside the lining figures in the date column an inch to its left. On a
   musician's site the numerals are opus numbers, catalogue numbers, volume
   numbers and years, so this is most of what the reader is scanning for. */
.cz-heading,
.cz-year-heading,
.cz-year-summary,
.cz-bio-heading,
.cz-members-heading,
.cz-member-name,
.cz-role-org,
.cz-works-heading,
.cz-work-summary,
.cz-work-title,
.cz-post-title,
.cz-article-heading,
.cz-recording-title,
.cz-track-heading,
.cz-video-title,
.cz-event-heading,
.cz-contact-heading,
.cz-contact-form-heading,
.cz-presskit-heading {
  font-weight: var(--cz-font-heading-weight, 500);
  font-variant-numeric: lining-nums;
}

/* The rank that cannot come from ink.
   ----------------------------------
   Every failure the type audit found was at item scale — a role heading, an
   event title, a recording title — and none at the hero or the page title,
   which came through all seventeen faces intact. That is the design's own
   doing: the scale is enormous at the top (a 6.25rem hero over a 3.6rem page
   title) and collapses at the bottom, where an item title arrived from its
   module at 1.05–1.15rem — five to fifteen per cent over the sentence beneath
   it — and left the weight step to carry the rest of the distance. On a face
   with no step there is nothing to carry it, and a narrow display serif at
   1.05rem reads smaller *and* lighter than the body text it is heading.

   Size is the one signal that means the same thing in every face: 1.25rem
   measures larger than 1rem whatever is drawing it, where a weight, a stroke
   contrast and an x-height all vary with the family. The other four
   candidates were looked at and set aside — case, because tracked capitals
   are already this design's word for "label" (the eyebrow, the nav, the
   roster, the wordmark) and a concert title is not a label; colour, because
   the one accent is spoken for and a diary of carmine event names would
   shout; tracking and space, because neither of them restores rank against
   the line directly underneath, which is the actual failure.

   Two tiers, because the modules ship eleven opinions about a scale they
   cannot see the rest of. `--lead` and the like keep their own steps: a
   module rule with two classes outranks these. */
.cz-heading,
.cz-year-heading,
.cz-year-summary,
.cz-bio-heading,
.cz-members-heading,
.cz-works-heading {
  font-size: var(--o-heading-section);
  line-height: 1.25;
}
.cz-member-name,
.cz-role-org,
.cz-work-summary,
.cz-work-title,
.cz-post-title,
.cz-article-heading,
.cz-recording-title,
.cz-video-title,
.cz-event-heading,
.cz-contact-heading,
.cz-contact-form-heading {
  font-size: var(--o-heading-item);
  line-height: 1.3;
}
/* A third rung, for the one heading that sits inside a block which shrank.
   The track listing is 0.9rem, so a work title heading a disc inherited that
   and came out the same size as "1. Allegro" beneath it — carried only by
   full-ink colour against the lines' muted grey, which a hairline display
   serif loses (Salon, Programme and Manuscript all showed it; Poster did
   not, which is why it survived the first pass). 1.05rem is a step over its
   own tracks and stays well under the recording title above it, which is
   what a sub-heading has to do in both directions at once. */
.cz-track-heading { font-size: 1.05rem; }

.cz-empty { color: var(--cz-text-muted); font-style: italic; }

.cz-prose { max-width: var(--o-narrow); margin: 0 0 1.1rem; }
.cz-inline-link {
  color: var(--cz-link);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}

/* -------------------------------------------------------------------------
   About
   ------------------------------------------------------------------------- */

.about-grid { display: grid; gap: 2.5rem; }
.about-portrait { margin: 0; }
.about-portrait:empty { display: none; }
.cz-bio-portrait { display: block; width: 100%; height: auto; border-radius: var(--cz-radius); }
@media (min-width: 56rem) {
  .about-grid:has(.about-portrait:not(:empty)) {
    grid-template-columns: minmax(16rem, 24rem) minmax(0, 1fr);
    gap: 3.5rem;
  }
  .about-portrait { position: sticky; top: 2rem; align-self: start; }
}

/* A roster on a site with no roster is a band of nothing: a soloist's site
   carries this placement by default and has no members to put in it. The
   module emits its region hook either way, so this is gated on the list
   itself rather than on the hook the general rule above uses. */
.band--roster:not(:has(.cz-members-list)) { display: none; }

/* -------------------------------------------------------------------------
   Press — the pull quotes are the showpiece
   ------------------------------------------------------------------------- */

.cz-press blockquote p,
.cz-press-quote p {
  font-family: var(--cz-font-heading);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.35rem, 2.6vw, 1.8rem);
  line-height: 1.35;
  letter-spacing: 0.005em;
  color: var(--cz-text);
}

/* -------------------------------------------------------------------------
   Custom pages
   ------------------------------------------------------------------------- */

.cz-page-images {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1.25rem;
  margin: 2.25rem 0;
}
.cz-page-image {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  display: block;
  border-radius: var(--cz-radius);
}
.cz-page-quote { margin: 2.25rem 0; padding-left: 1.5rem; border-left: 3px solid var(--cz-accent); }
.cz-page-quote p {
  font-family: var(--cz-font-heading);
  font-style: italic;
  font-size: 1.35rem;
  line-height: 1.4;
  margin: 0 0 0.5rem;
}
.cz-page-quote cite {
  font-style: normal;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cz-text-muted);
}
.cz-page-links, .cz-page-downloads {
  list-style: none;
  margin: 1.75rem 0 0;
  padding: 0;
  border-top: var(--o-rule);
}
.cz-page-links li, .cz-page-downloads li { border-bottom: var(--o-rule); }
.cz-page-link, .cz-download {
  display: block;
  padding: 0.8rem 0;
  text-decoration: none;
  color: var(--cz-text);
}
.cz-page-link:hover, .cz-download:hover { color: var(--cz-accent-bright); }
.cz-download::after { content: " ↓"; color: var(--cz-accent-bright); }

.cz-sibling-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: baseline;
}
.cz-sibling-link, .cz-sibling-current {
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.cz-sibling-link { color: var(--cz-text-muted); text-decoration: none; }
.cz-sibling-link:hover { color: var(--cz-text); }
.cz-sibling-current { color: var(--cz-text); border-bottom: 2px solid var(--cz-accent); }

/* -------------------------------------------------------------------------
   One-page assembly
   ------------------------------------------------------------------------- */

.o-section { border-top: var(--o-rule); padding-block: 4.5rem; }
.o-section[data-cz-index]:nth-of-type(even) { background: var(--cz-surface); }

/* -------------------------------------------------------------------------
   Footer
   ------------------------------------------------------------------------- */

.foot {
  border-top: var(--o-rule);
  padding-block: 3.5rem;
  margin-top: 5rem;
}
.foot-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.25rem 2rem;
}
.foot-name {
  font-family: var(--cz-font-heading);
  font-weight: var(--cz-font-heading-weight, 500);
  font-size: 1rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cz-text);
  margin: 0;
}
.cz-social {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.cz-social-link {
  color: var(--cz-text-muted);
  text-decoration: none;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border-bottom: 1px solid transparent;
}
.cz-social-link:hover { color: var(--cz-text); border-bottom-color: var(--cz-accent); }
.foot-credits {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.8rem;
  color: var(--cz-text-muted);
}
.cz-credit-link { color: var(--cz-text); }
.cz-credit-dot { display: inline-block; text-decoration: none; }
.cz-nowrap { white-space: nowrap; }

/* -------------------------------------------------------------------------
   Lightbox (site.js)
   ------------------------------------------------------------------------- */

/* display is scoped to [open] — a bare display:flex would override the UA's
   dialog:not([open]) rule and paint the closed dialog (quarto's lesson). */
.o-lightbox[open] {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5vmin;
  background: rgba(10, 8, 12, 0.94);
  border: 0;
}
.o-lightbox img {
  max-width: 100%;
  max-height: 90vh;
  object-fit: contain;
}
.o-lightbox-close {
  position: absolute;
  top: 1rem;
  right: 1.25rem;
  background: none;
  border: 0;
  color: #fff;
  font: inherit;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.5rem;
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
