/* ==========================================================================
   cj-article.css
   CJ Digital Publish Connector — layout and typography for pipeline articles
   v3 · 31 July 2026 (0.4.5: centre inside flex parents; lists/tables clear floats)

   Structure and rhythm only. Every site-specific NUMBER (widths, type size,
   image width, block padding) and every site-specific COLOUR is a custom
   property supplied per site by the connector, which writes them inline on
   .cj-article from the dispatch payload (options.layoutProfile.tokens).

   Fallback rule — non-negotiable. A token that a site has not set MUST fall
   back to currentColor / inherit / transparent, or to another token, and
   NEVER to a literal colour taken from an existing client. An unconfigured
   site renders plainly and inherits its own theme. Rendering as a confident
   copy of another client is a far worse failure than rendering plainly.

   The contract token set (the keys the hub sends) is fourteen, each with a
   fallback and at least one consuming rule below:

     Layout   --cj-content        wrapper / band width       (min(…, 1180px))
              --cj-measure        reading column width       (680px)
              --cj-body-size      body text size             (fluid ~16–18px)
              --cj-image-width    floated image width        (40%)
              --cj-space-block    wrapper block padding      (clamp 2–3rem)

     Colour   --cj-accent         links, markers, rules      currentColor
              --cj-accent-ink     text on the accent         inherit
              --cj-band-bg        tinted section background   transparent
              --cj-band-ink       text inside a band         inherit
              --cj-cta-bg         CTA button background       var(--cj-accent)
              --cj-cta-ink        CTA button text            var(--cj-accent-ink)
              --cj-rule           borders, dividers, tables   currentColor
              --cj-table-head-bg  table header background     transparent
              --cj-table-head-ink table header text          inherit
              --cj-callout-bg     callout background          transparent
              --cj-callout-ink    text inside a callout      inherit
              --cj-callout-rule   callout edge rule          var(--cj-accent)
              --cj-callout-rule-width  callout edge width    4px
              --cj-pullquote-size pull quote text size       1.35em
              --cj-pullquote-ink  pull quote text colour     inherit

   Note on --cj-measure: fixed px, never ch. ch resolves against each
   element's own font-size, so a 36px heading and a 19px paragraph get wildly
   different widths from the same value.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Structural tokens

   These are STRUCTURE, not site identity: a spacing scale, a type scale and
   image geometry. They carry neutral defaults and are not part of the
   per-site colour contract, so no client value is baked in here.
   -------------------------------------------------------------------------- */

.cj-article {
  /* Derived widths ------------------------------------------------------ */
  --cj-media: var(--cj-measure, 680px);                   /* images, tables: share the reading edge */
  --cj-wide: min(var(--cj-content, 1180px), 100%);        /* banded sections */
  --cj-gutter: clamp(1.25rem, 4vw, 3rem);

  /* Vertical rhythm ----------------------------------------------------- */
  --cj-space-xs: 0.5rem;
  --cj-space-sm: 1rem;
  --cj-space-md: 1.75rem;
  --cj-space-lg: 3rem;
  --cj-space-xl: clamp(3rem, 6vw, 5rem);

  /* Type scale (size of headings; body size is a contract token) -------- */
  --cj-body-leading: 1.7;
  --cj-h2-size: clamp(1.9rem, 1.45rem + 1.6vw, 2.6rem);
  --cj-h3-size: clamp(1.3rem, 1.15rem + 0.5vw, 1.55rem);
  --cj-heading-leading: 1.12;
  --cj-heading-tracking: 0.01em;

  /* Button geometry (colour comes from the CTA tokens) ------------------ */
  --cj-btn-radius: 0px;
  --cj-btn-pad-y: 1rem;
  --cj-btn-pad-x: 2.25rem;
  --cj-btn-weight: 600;
  --cj-btn-tracking: 0.09em;

  /* Media --------------------------------------------------------------- */
  --cj-image-ratio: 16 / 9;
  --cj-image-radius: 0px;

  /* Signature accent bar above each H2. Width 0 removes it. -------------- */
  --cj-heading-mark-width: 2.75rem;
  --cj-heading-mark-height: 5px;

  font-size: var(--cj-body-size, clamp(1rem, 0.95rem + 0.25vw, 1.125rem));
  line-height: var(--cj-body-leading);
}

/* --------------------------------------------------------------------------
   2. Measure, containment and the single left edge

   The article fills its container; individual elements are constrained, so
   banded sections can run wider than the text they hold. The measure rule is
   scoped to DESCENDANTS, not direct children, so text inside a band shares
   the same centreline — and therefore the same left edge — as text in open
   flow. A direct-child rule would miss band contents and stagger the edges.
   -------------------------------------------------------------------------- */

.cj-article {
  max-width: none;
  /* Centre whether the parent is normal block flow or a flex column, without
     shrinking. A flex item with a max-width goes to flex-start under
     align-items:stretch, not centre (Breakdance section containers are
     display:flex; flex-direction:column), so auto inline margins are needed to
     centre it there. But auto cross-axis margins also suppress flex stretch, so
     width:100% must ride alongside them or the wrapper falls back to fit-content
     and renders narrower than the measure on short sections. Both are harmless
     in block flow. Constrained children below carry margin-inline:auto too. */
  width: 100%;
  margin-inline: auto;
  /* Wrapper block padding. Padding on the wrapper, not margin on the first
     child: a child's top margin collapses out of the wrapper and lets body
     text butt against the template's title bar. */
  padding-block: var(--cj-space-block, clamp(2rem, 5vw, 3rem));
}

.cj-article p,
.cj-article h2,
.cj-article h3,
.cj-article h4,
.cj-article h5,
.cj-article h6,
.cj-article ul,
.cj-article ol,
.cj-article dl,
.cj-article blockquote {
  max-width: var(--cj-measure, 680px);
  margin-inline: auto;
}

.cj-article .wp-block-media-text,
.cj-article .wp-block-table,
.cj-article .wp-block-image,
.cj-article .wp-block-embed,
.cj-article .wp-block-buttons,
.cj-article .wp-block-columns {
  max-width: var(--cj-media);
  margin-inline: auto;
}

.cj-article .cj-band {
  max-width: var(--cj-wide);
  margin-inline: auto;
}

/* A callout is an aside inside the column, not a full-width slab: it sits on
   the reading measure rather than the wide edge a band uses. */
.cj-article .cj-callout {
  max-width: var(--cj-measure, 680px);
  margin-inline: auto;
}

/* Inside a media-text, band or callout, content fills the column / box rather
   than re-centring at the article measure. The callout is listed in both group
   shapes: the composer emits paragraphs as direct children, while a site whose
   WordPress still writes the legacy wrapper nests them one level deeper. */
.cj-article .wp-block-media-text__content > *,
.cj-article .cj-callout > *,
.cj-article .cj-callout > .wp-block-group__inner-container > * {
  max-width: none;
  margin-inline: 0;
}

/* --------------------------------------------------------------------------
   3. Vertical rhythm
   -------------------------------------------------------------------------- */

.cj-article p,
.cj-article ul,
.cj-article ol,
.cj-article blockquote {
  margin-block: 0 var(--cj-space-md);
}

.cj-article h2 {
  margin-block: var(--cj-space-xl) var(--cj-space-md);
}

.cj-article h3 {
  margin-block: var(--cj-space-lg) var(--cj-space-sm);
}

.cj-article .wp-block-media-text,
.cj-article .wp-block-table,
.cj-article .wp-block-image,
.cj-article .wp-block-embed {
  margin-block: var(--cj-space-lg);
}

.cj-article .cj-band {
  margin-block: var(--cj-space-xl);
}

.cj-article .cj-callout {
  margin-block: var(--cj-space-lg);
}

/* First and last elements never push against the template, or against the
   inside of a callout's padding. */
.cj-article > *:first-child,
.cj-article > .wp-block-group__inner-container > *:first-child,
.cj-article .cj-band > .wp-block-group__inner-container > *:first-child,
.cj-article .cj-callout > *:first-child,
.cj-article .cj-callout > .wp-block-group__inner-container > *:first-child,
.cj-article .wp-block-media-text__content > *:first-child {
  margin-block-start: 0;
}

.cj-article > *:last-child,
.cj-article > .wp-block-group__inner-container > *:last-child,
.cj-article .cj-band > .wp-block-group__inner-container > *:last-child,
.cj-article .cj-callout > *:last-child,
.cj-article .cj-callout > .wp-block-group__inner-container > *:last-child,
.cj-article .wp-block-media-text__content > *:last-child {
  margin-block-end: 0;
}

/* --------------------------------------------------------------------------
   4. Headings

   Family and colour inherit from the site so each brand keeps its own voice.
   Only size, leading and the accent mark are set here.
   -------------------------------------------------------------------------- */

.cj-article h2,
.cj-article h3,
.cj-article h4 {
  line-height: var(--cj-heading-leading);
  letter-spacing: var(--cj-heading-tracking);
  text-wrap: balance;
}

.cj-article h2 {
  font-size: var(--cj-h2-size);
}

.cj-article h3 {
  font-size: var(--cj-h3-size);
}

/* Signature mark above the heading, so it survives headings of any length or
   writing direction. Colour is the accent, falling back to the text colour. */
.cj-article h2::before {
  content: "";
  display: block;
  width: var(--cj-heading-mark-width);
  height: var(--cj-heading-mark-height);
  margin-block-end: var(--cj-space-sm);
  background: var(--cj-accent, currentColor);
}

/* --------------------------------------------------------------------------
   5. Body copy and links
   -------------------------------------------------------------------------- */

.cj-article p {
  text-wrap: pretty;
}

/* Links in running text must be visible. A site whose global link style is
   black with no underline is correct for navigation and wrong for prose.
   Scoped to prose only, so buttons and figures are untouched. */
.cj-article :is(p, li, td, blockquote) a {
  color: var(--cj-accent, currentColor);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: text-decoration-thickness 150ms ease, opacity 150ms ease;
}

.cj-article :is(p, li, td, blockquote) a:hover,
.cj-article :is(p, li, td, blockquote) a:focus-visible {
  text-decoration-thickness: 2px;
}

/* Focus must never hang on the accent: an accent close to the background would
   make the focus ring invisible, a keyboard-accessibility failure. Use the
   theme's own text colour (CanvasText), which contrasts with the theme
   background by definition, with a currentColor fallback for older engines. */
.cj-article a:focus-visible,
.cj-article .wp-block-button__link:focus-visible {
  outline: 2px solid currentColor;
  outline: 2px solid CanvasText;
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   6. Lists
   -------------------------------------------------------------------------- */

.cj-article ul,
.cj-article ol {
  padding-inline-start: 1.4em;
}

.cj-article li {
  margin-block-end: var(--cj-space-sm);
  padding-inline-start: 0.25em;
}

.cj-article li:last-child {
  margin-block-end: 0;
}

.cj-article ul li::marker {
  color: var(--cj-accent, currentColor);
}

.cj-article ol li::marker {
  color: var(--cj-accent, currentColor);
  font-weight: 700;
}

/* --------------------------------------------------------------------------
   7. Media, media-text and floated images

   Media-text WRAPS rather than gridding: the image floats and the text flows
   around it (B4c). The composer is moving to core/image with align left/right
   for the same effect (B5); float rules for that markup live here too. The
   media-text rules stay because a published Karl Knudsen article still uses
   media-text blocks — both sets ship until every article has been rewritten.
   -------------------------------------------------------------------------- */

.cj-article img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--cj-image-radius);
}

.cj-article figure {
  margin: 0;
}

.cj-article figcaption {
  margin-block-start: var(--cj-space-xs);
  max-width: var(--cj-measure, 680px);
  margin-inline: auto;
  font-size: 0.875em;
  line-height: 1.45;
  opacity: 0.75; /* de-emphasise without naming a colour */
}

/* Media-text: float, do not grid. Text flows around the image and continues
   below it (Word-style wrap). It is the next heading/band/table that clears the
   float, not the media-text box, so body copy keeps flowing under the image. */
.cj-article .wp-block-media-text {
  display: block;
}

.cj-article .wp-block-media-text__media {
  float: left;
  width: var(--cj-image-width, 40%);
  margin: 0.35rem var(--cj-space-md) var(--cj-space-sm) 0;
}

.cj-article .wp-block-media-text.has-media-on-the-right .wp-block-media-text__media {
  float: right;
  margin: 0.35rem 0 var(--cj-space-sm) var(--cj-space-md);
}

/* A consistent crop stops alternating sections jittering when a real photo
   replaces a placeholder. */
.cj-article .wp-block-media-text__media img {
  width: 100%;
  aspect-ratio: var(--cj-image-ratio);
  object-fit: cover;
}

.cj-article .wp-block-media-text__content {
  padding: 0;
}

/* core/image with align left / right: same float behaviour as media-text,
   ahead of the composer emitting this in place of core/media-text. */
.cj-article .wp-block-image.alignleft,
.cj-article .wp-block-image.alignright {
  max-width: var(--cj-image-width, 40%);
}

.cj-article .wp-block-image.alignleft {
  float: left;
  margin: 0.35rem var(--cj-space-md) var(--cj-space-sm) 0;
}

.cj-article .wp-block-image.alignright {
  float: right;
  margin: 0.35rem 0 var(--cj-space-sm) var(--cj-space-md);
}

/* Beside a floated image, only running PARAGRAPHS wrap (Word-style). Headings,
   lists, tables and bands clear below the float — a list or table wrapping a
   float indents its first rows beside the image and snaps back once they clear
   it, which reads as broken. h3 is included as well as h2 because documents
   carry "H3:" prefixes, so h3 appears in published bodies. Deliberately NOT a
   clearfix on the media-text box, so paragraphs keep flowing beneath the image. */
.cj-article :is(h2, h3, ul, ol, .wp-block-table, .cj-band) {
  clear: both;
}

/* Exception: inside a media-text content column, nothing clears — that content
   is meant to sit beside the floated media, not drop below it. Mirrors the
   element list above so headings and lists in the content column stay put.
   REMOVAL TRIGGER: this rule (and the media-text float rules it protects)
   becomes dead CSS once the composer switches from core/media-text to
   core/image. Delete both then. */
.cj-article .wp-block-media-text__content :is(h2, h3, ul, ol, .wp-block-table) {
  clear: none;
}

/* --------------------------------------------------------------------------
   8. Banded sections

   A band is a full-width tinted section. Its background and text colour are
   tokens, defaulting to transparent / inherit so an unconfigured band is
   simply untinted rather than tinted in someone else's brand.
   -------------------------------------------------------------------------- */

.cj-article .cj-band {
  padding-block: clamp(2.25rem, 5vw, 3.5rem);
  padding-inline: var(--cj-gutter);
  color: var(--cj-band-ink, inherit);
}

/* Default tint only when the composer has not supplied one inline, and never
   on a CTA (a CTA is a button, not a tinted slab — see section 10). */
.cj-article .cj-band:not(.cj-cta):not(.has-background) {
  background-color: var(--cj-band-bg, transparent);
}

/* Band keeps its full-width background; its contents sit on the same edge as
   everything else — inner container constrained to the reading measure and
   centred (folded in from KK's Customiser). */
.cj-article .cj-band > .wp-block-group__inner-container {
  max-width: var(--cj-measure, 680px);
  margin-inline: auto;
}

/* A band's first heading needs less air above it than one in open flow. */
.cj-article .cj-band > .wp-block-group__inner-container > h2:first-child,
.cj-article .cj-band > h2:first-child {
  margin-block-start: 0;
}

/* --------------------------------------------------------------------------
   9. Tables
   -------------------------------------------------------------------------- */

.cj-article .wp-block-table {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.cj-article .wp-block-table table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375em;
  line-height: 1.5;
}

.cj-article .wp-block-table thead th {
  background: var(--cj-table-head-bg, transparent);
  color: var(--cj-table-head-ink, inherit);
  text-align: start;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.85rem 1rem;
  border: 0;
  border-block-end: 2px solid var(--cj-rule, currentColor);
}

.cj-article .wp-block-table tbody td {
  padding: 0.85rem 1rem;
  border: 0;
  border-block-end: 1px solid var(--cj-rule, currentColor);
  vertical-align: top;
}

.cj-article .wp-block-table tbody tr:last-child td {
  border-block-end: 0;
}

/* --------------------------------------------------------------------------
   10. Buttons and the CTA

   The site's own buttons are square, uppercase and tracked out. The CTA is a
   BUTTON with space around it, not a tinted section: cj-cta must not inherit
   the band tint. Every rule here is inert when no CTA block is present — the
   composer omits an unconfigured CTA entirely.
   -------------------------------------------------------------------------- */

.cj-article .wp-block-buttons {
  margin-block: var(--cj-space-md) 0;
}

/* Buttons carry the accent by default: accent background, accent-ink text. */
.cj-article .wp-block-button__link {
  display: inline-block;
  background: var(--cj-accent, currentColor);
  color: var(--cj-accent-ink, inherit);
  border: 0;
  border-radius: var(--cj-btn-radius);
  padding: var(--cj-btn-pad-y) var(--cj-btn-pad-x);
  font-size: 1rem;
  font-weight: var(--cj-btn-weight);
  line-height: 1;
  letter-spacing: var(--cj-btn-tracking);
  text-transform: uppercase;
  text-decoration: none;
  transition: filter 200ms ease, transform 200ms ease;
}

/* The CTA button may take its own colour, falling back to the accent so an
   unset CTA still matches the site's buttons rather than going plain. */
.cj-article .cj-cta .wp-block-button__link {
  background: var(--cj-cta-bg, var(--cj-accent, currentColor));
  color: var(--cj-cta-ink, var(--cj-accent-ink, inherit));
}

.cj-article .wp-block-button__link:hover,
.cj-article .wp-block-button__link:focus-visible {
  filter: brightness(0.92); /* darken any colour without naming one */
  transform: translateY(-1px);
}

/* A CTA is a button with space around it, not a band: no tint, no box padding,
   just separation from the content above (folded in from KK's Customiser).
   Alignment is left to the buttons block's own justification. */
.cj-article .cj-cta {
  background: none;
  padding-block: 0;
  margin-block: 1.5rem 0;
}

.cj-article .cj-cta > .wp-block-group__inner-container {
  padding-block: 0;
}

/* --------------------------------------------------------------------------
   11. Quotes and rules
   -------------------------------------------------------------------------- */

.cj-article blockquote {
  padding-inline-start: var(--cj-space-md);
  border-inline-start: 4px solid var(--cj-accent, currentColor);
  font-size: 1.125em;
  line-height: 1.5;
}

.cj-article blockquote cite {
  display: block;
  margin-block-start: var(--cj-space-xs);
  font-size: 0.8125em;
  font-style: normal;
  opacity: 0.75;
}

/* A pull quote is a core/quote, not a core/pullquote: the payload validator
   does not accept core/pullquote, so an article carrying one is refused before
   it reaches the site. The class is what separates it from an ordinary quote —
   it drops the rule the block quote above carries and sets the line larger. */
.cj-article blockquote.cj-pullquote {
  padding-inline-start: 0;
  border-inline-start: none;
  margin-block: var(--cj-space-lg);
  font-size: var(--cj-pullquote-size, 1.35em);
  line-height: 1.35;
  font-style: italic;
  color: var(--cj-pullquote-ink, inherit);
}

/* --------------------------------------------------------------------------
   11a. Callouts

   An aside the writer marked in the source document. It arrives as a group
   block, so the composer can put its own background on it inline; the token
   below only applies when it has not, which keeps an unconfigured callout
   untinted rather than tinted in someone else's brand — the same rule a band
   follows.

   Padding is on the callout itself. The composer emits the paragraphs as
   direct children and there is no .wp-block-group__inner-container to select,
   so a rule written against one silently does nothing; the inner-container
   selectors here are the fallback for the legacy markup, not the main path.
   -------------------------------------------------------------------------- */

.cj-article .cj-callout {
  padding: var(--cj-space-sm) var(--cj-space-md);
  border-inline-start: var(--cj-callout-rule-width, 4px) solid
    var(--cj-callout-rule, var(--cj-accent, currentColor));
  color: var(--cj-callout-ink, inherit);
}

.cj-article .cj-callout:not(.has-background) {
  background-color: var(--cj-callout-bg, transparent);
}

.cj-article .cj-callout > .wp-block-group__inner-container {
  padding: 0;
}

.cj-article hr,
.cj-article .wp-block-separator {
  max-width: var(--cj-measure, 680px);
  margin-block: var(--cj-space-lg);
  margin-inline: auto;
  border: 0;
  border-block-start: 1px solid var(--cj-rule, currentColor);
}

/* --------------------------------------------------------------------------
   12. Narrow screens

   Below 781px a floated image column has become an unreadable sliver, so
   images unfloat and run full width, and content stacks beneath them.
   -------------------------------------------------------------------------- */

/* Floated images unstack at 700px (KK's Customiser breakpoint): a floated
   column has become an unreadable sliver by here, so images run full width. */
@media (max-width: 700px) {
  .cj-article .wp-block-media-text__media,
  .cj-article .wp-block-media-text.has-media-on-the-right .wp-block-media-text__media {
    float: none;
    width: 100%;
    margin: 0 0 var(--cj-space-sm) 0;
  }

  .cj-article .wp-block-image.alignleft,
  .cj-article .wp-block-image.alignright {
    float: none;
    max-width: 100%;
    margin-inline: auto;
  }
}

/* Band gutters and full-width buttons ease in slightly earlier. */
@media (max-width: 781px) {
  .cj-article .cj-band {
    padding-inline: var(--cj-space-md);
  }

  .cj-article .wp-block-button__link {
    width: 100%;
    text-align: center;
  }
}

/* --------------------------------------------------------------------------
   13. Accessibility and print
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .cj-article *,
  .cj-article *::before,
  .cj-article *::after {
    transition-duration: 0.01ms !important;
  }

  .cj-article .wp-block-button__link:hover {
    transform: none;
  }
}

@media print {
  .cj-article .cj-band {
    background: none !important;
    padding-inline: 0;
  }

  /* A callout keeps its rule in print — that is what marks it as an aside once
     the tint is gone — but loses the tint, which wastes ink. */
  .cj-article .cj-callout {
    background: none !important;
  }

  .cj-article .cj-cta {
    display: none;
  }

  .cj-article :is(p, li) a::after {
    content: " (" attr(href) ")";
    font-size: 0.8em;
    word-break: break-all;
  }
}
