/* ==========================================================================
   Connect 2026 - Reader shell                                           T-29
   --------------------------------------------------------------------------
   Shell A and its variants: Article (p3), News (p5), In-depth (p9), Magazine
   article (p12). Shells B-E reuse most of this.

   MEASURED on artboard p3:
     split hero      image 387x240 (ratio 1.616) + 22px gap + 361px title col
                     = the 771px READER column (not the 743px listing one).
                     Title 14.5pt SemiBold x2.4 = 35px.
     author avatar   65px circle
     author line     19px - name cyan, role italic muted, company semibold navy
                     (same three-weight treatment as the card author line)
     standfirst      18px Medium, TOPIC-COLOURED - the artboard draws it at 80%
                     opacity of the topic colour; we use the solid -ink variant
     key points      18px Medium, muted
     rule            1px, full column width, between key points and body

   DEPENDS ON: tokens.css, shell.css, components/topic.css
   ========================================================================== */

/* ========================================================== SPLIT HERO === */

/*
   Measured on p3 against the READER column (771px, not the listing's 743):
     image 387.4  |  gap 22.2  |  title 361.5   = 771.1

   Ratios rather than fixed px so the hero still fills the column when the
   rail stacks and the content column grows.
*/
/*
   VERTICAL RHYTHM - measured on p3, not a uniform scale.

   The design groups the top of the page tightly and opens up lower down:

     back link -> meta      18px
     meta -> hero           10px        hero -> author         13px    |  these three read as ONE unit
     author -> pills        32px   /
     pills -> standfirst    37px
     standfirst -> bullets  27px

   An earlier version used a flat 20px between everything, which made the page
   read evenly spaced and slightly mushy - the grouping disappeared. Spacing is
   set as margin-BOTTOM only, so the sequence cannot double up.
*/
.cn-reader__hero {
  display: grid;
  grid-template-columns: 387fr 361fr;
  column-gap: 22px;
  align-items: start;
  margin-top: 0;
  margin-bottom: var(--cn-space-3);   /* 12 - artboard 13 */
}

/* p3's 303 x 191 frame, filled (object-fit: fill) - see card.css. */
.cn-reader__hero-img {
  display: block;
  width: 100%;
  aspect-ratio: 1.616;
  object-fit: fill;
  background: var(--cn-band-grey);
}

.cn-reader__title {
  font-size: var(--cn-text-h1);
  font-weight: var(--cn-fw-semibold);
  line-height: var(--cn-lh-tight);
  color: var(--cn-ink);
}

/*
   Title-only hero, no image. NOT used by News - News has the split hero like
   every other Shell A page (measured on p5). Kept for the Filmed Interview and
   Webinar shells, where the artboards show a title above a video rather than
   beside a still.
*/
.cn-reader__hero--full { grid-template-columns: minmax(0, 1fr); }

/* ========================================================= AUTHOR ROW ==== */

.cn-reader__author {
  display: flex;
  align-items: center;
  /* 8, not 16. Measured on p18: the 65px avatar ends at x=63.2pt and the
     byline starts at x=66.5pt - a 3.3pt gap, 8px. Same on p3, where the
     byline sits at x=66.2 against an avatar at x=36. */
  gap: var(--cn-space-2);
  margin-top: 0;
  margin-bottom: var(--cn-space-8);   /* 32 - artboard 32 */
}

/*
   THE DISC, WITH INITIALS UNDER THE PHOTO. See authorRow.tag - the wrap is
   the 65px circle the boards draw, the initials fill it, and the image sits
   over them absolutely. When there is no image, or it removes itself on a
   404, the initials are what you see; when there is, they are covered.
*/
.cn-reader__avatar-wrap {
  position: relative;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 65px;
  height: 65px;
  border-radius: var(--cn-radius-pill);
  overflow: hidden;
  background: var(--cn-navy);
}

.cn-reader__avatar-initials {
  font-size: var(--cn-text-card);
  font-weight: var(--cn-fw-semibold);
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--cn-ink-invert);
  user-select: none;
}

.cn-reader__avatar {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: var(--cn-radius-pill);
  object-fit: cover;
  background: var(--cn-band-grey);
}

/* Reuses the .cn-author three-weight treatment from shell.css. */

/* ============================================================== BODY ===== */

/*
   THE AUDIO PLAYER (audio.jsp). The CRM's embed code arrives with its own
   width="..." and height="..." - SoundCloud's is 100%/166, Spotify's often a
   fixed pixel width - so on a MacBook's 1280 the iframe overran the 775
   column. Whatever the code says, the frame is the column's width; the
   height it asked for is kept. 24 above (under the byline) and 24 below,
   so the pill row no longer sits on the player's edge; empty (a piece the
   reader may not hear) it takes no room at all.
*/
.cn-audio-embed {
  margin-top: var(--cn-space-6);
  margin-bottom: var(--cn-space-6);
  max-width: 100%;
  overflow: hidden;
}
.cn-audio-embed:empty { display: none; }
.cn-audio-embed iframe,
.cn-audio-embed audio,
.cn-audio-embed embed,
.cn-audio-embed object {
  display: block;
  width: 100% !important;
  max-width: 100%;
}
.cn-audio-embed audio { height: 54px; }

/* Standfirst - topic-coloured, sits above the key points. */
.cn-reader__standfirst {
  font-size: var(--cn-text-body);
  font-weight: var(--cn-fw-medium);
  line-height: var(--cn-lh-body);
  margin-top: 0;
  margin-bottom: var(--cn-space-6);   /* 24 - artboard 27 */
}

.cn-reader__points {
  margin-top: 0;
  margin-bottom: var(--cn-space-5);
  padding-left: var(--cn-space-5);
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-muted);
}
.cn-reader__points li + li { margin-top: var(--cn-space-3); }

/*
   Body copy is MUTED GREY, not near-black. Every reader artboard agrees -
   #575756 for 543 of the 593 body words on p3, and the same on p5, p7, p9,
   p12 and p18. --cn-ink-body (#1d1d1b) is the back-link / UI text colour,
   despite what its name suggests; it appears nowhere in a reader's prose.
*/
/*
   NO max-width HERE. An earlier version capped the paragraphs at 775 to hold
   the artboard's line length while the column grew above 1440. It worked on
   the measurement and failed on the page: at 1920 the title, action pills,
   comments and gate all ended at x=1151 and the prose stopped at 888, so the
   article had a ragged right edge 263px short of everything around it.

   Line length is protected by capping the whole column instead - see
   .cn-grid-rail--reader in shell.css - so every element in the article shares
   one right edge, which is what the artboard draws.
*/
.cn-reader__body {
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-muted);
}
.cn-reader__body p + p { margin-top: var(--cn-space-para); }  /* 22, measured */

/*
   THE BODY IS WORD HTML, AND IT HAS TO BE OVERRULED.

   The CRM stores the article body as HTML pasted out of Microsoft Word.
   Fetched from a real Q&A, it looks like this:

     <ul><li><span style="font-family: Poppins;"></span>
     <span lang="EN-GB" style="background-color: unset; font-size: 1em;
     text-indent: -0.25in; line-height: 115%;">...<o:p></o:p>

   Nested spans several deep, mso- properties, Word's <o:p> tags, runs of
   &nbsp; used as indentation, and inline font rules on nearly every element.
   Left alone those inline styles beat every rule in this file, and the reader
   renders at Word's typography rather than the artboard's 18/24.

   Inline styles can only be beaten by !important, so that is what this does,
   scoped to the body and to the properties Word sets. It does NOT touch
   font-weight - a <b> lead-in inside a bullet is real emphasis the editor
   meant - and links are given the accent back on the line below, because
   colour is inherited away here.

   The alternative was sanitising server-side, which needs an HTML parser this
   project does not depend on. This is a CSS fix for a content problem, and it
   is honest about that.
*/
.cn-reader__standfirst *,
.cn-reader__points *,
.cn-reader__body * {
  font-family: inherit !important;
  font-size: inherit !important;
  line-height: inherit !important;
  letter-spacing: normal !important;
  color: inherit !important;
  background-color: transparent !important;
  text-indent: 0 !important;
}

.cn-reader__standfirst a,
.cn-reader__points a,
.cn-reader__body a { color: var(--cn-cyan) !important; }

/*
   THE Q&A PAIRS ARE RECOVERABLE FROM THE WORD HTML AFTER ALL.

   Fetched from a real Q&A, the body is shaped like this:

     <ul><li><b>lead-in</b> text</li> ...      the key points
     <p><b>What are the most effective ...?</b></p>   a question
     <p>answer text</p>                              its answer

   Eleven bold runs in that article: six are <li> lead-ins, five end in a
   question mark - and p7 draws exactly five Q&A pairs. So bold INSIDE A
   PARAGRAPH is a question, and bold inside a list item is not. That is the
   distinction, and it is why these selectors require `p`.

   Sampled off p7 to confirm the treatment:
     question  rgb(0,28,100)   = --cn-navy
     answer    rgb(87,87,86)   = --cn-ink-muted, which the body already is

   Overrides the colour:inherit above, so it has to be !important too.
*/
.cn-reader__body p b,
.cn-reader__body p strong {
  color: var(--cn-navy) !important;
  font-weight: var(--cn-fw-semibold);
}

/*
   Space BEFORE a question, not between every paragraph. Measured on p7: lines
   inside an answer step 24, question to its answer is 24, and answer to the
   NEXT question is 48. So a question opens a pair and needs the extra air.

   :has() is doing real work here. Without support the pairs simply sit at the
   22px paragraph rhythm - tighter than the artboard, never broken.
*/
.cn-reader__body p:has(> b),
.cn-reader__body p:has(> strong) {
  margin-top: var(--cn-space-12);
}

.cn-reader__body p:first-child { margin-top: 0; }

/*
   Word's leftover empty paragraphs are hidden by js/reader.js, not from here.
   A CSS :empty test cannot see them: they are <p><b><span>&nbsp;</span></b></p>,
   so every element in the chain is technically non-empty. Only the trimmed
   text content gives them away.
*/
/* Word emits these; they are not content. */
.cn-reader__body o\:p { display: none; }

.cn-reader__body ul {
  margin-block: var(--cn-space-6);
  padding-left: var(--cn-space-6);
  list-style: disc;
}

/*
   THE RULE CLOSES THE KEY POINTS.

   p7 draws it at y=1428.7 - after the last bullet (about 1417) and before the
   first question (1466). The key points are their own field (summary), so the
   rule belongs to that block rather than to the body below it, and it only
   appears when there are key points to close.
*/
/*
   THE RULE SITS ON THE BODY'S TOP EDGE, not under the key points.

   p7 draws it at y=1428.7, between the last key point and the first question.
   Hanging it off .cn-reader__points looked equivalent - but key points only
   render for a signed-in reader, so a signed-out one got the standfirst
   running straight into the first question with no separator at all.

   On the body it lands in the same place either way.
*/
.cn-reader__points {
  padding-bottom: var(--cn-space-8);
  margin-bottom: 0;
}

/*
   THE CYAN RULE BELONGS BETWEEN THE KEY POINTS AND THE BODY - AND NOWHERE
   ELSE.

   It was set on .cn-reader__body unconditionally, so it drew on every reader
   whether or not there was anything above it to separate. On the webinar
   reader the body IS the agenda, sitting under its own "Webinar agenda"
   heading with no key points at all, and the rule landed immediately under
   that heading. p16 draws no rule there. Reported by the test team as
   "divider not needed after webinar agenda and space after it" - the space
   being this element's 32px of padding-top, which went with it.

   Scoped to the adjacency it was always describing. Where a record has no key
   points the body now follows the standfirst with no rule, which is what p9
   draws for exactly that case.
*/
.cn-reader__points + .cn-reader__body {
  padding-top: var(--cn-space-8);
  /*
     CYAN, and 2px. Sampled off p7 at y=1428.7: rgb(28,183,252) across two
     solid rows with antialiasing either side. It is an accent rule, not the
     near-black hairline the listing rows use - those really are dark, sampled
     separately on p6 at rgb(67,67,67).
  */
  border-top: 2px solid var(--cn-cyan);
}

/*
   THE RULE UNDER THE ACTION PILLS, on the two readers that draw one.

   p16 and p18 both close the pill row with a full-width hairline before the
   section under it - Webinar Speakers on one, Related Interviews on the other.
   No other reader has it: p3 and p9 run the standfirst straight on from the
   pills. So it is markup on those two pages rather than a rule on .cn-actions,
   which every reader shares.

   The 40px below is what the test team asked for as "related Interviews
   divider needs space at top" - the rule needs to belong to the pills above
   it, not float between the two blocks.
*/
/* The reader's horizontal hairlines draw in --cn-border-mid, like the
   column partitions: #d8dade under the pill row "was not darkened" - it
   barely registered on white. Card and input borders keep --cn-border. */
.cn-reader__rule {
  height: 0;
  margin: var(--cn-space-8) 0 var(--cn-space-10);
  border: 0;
  border-top: 1px solid var(--cn-border-mid);
}

/*
   NO RULE BETWEEN THE SPEAKERS AND WHAT FOLLOWS. .cn-comments opens with a
   hairline of its own, which reads correctly after a body of prose and reads
   as a stray divider after the Webinar Speakers block - "not need divider
   after speaker section". The spacing stays; only the line goes.
*/
.cn-reader__section + .cn-comments { border-top: 0; }

/*
   AND NO RULE TWICE. On a webinar without speakers, and on every filmed
   interview, the pill row's hairline (.cn-reader__rule) was followed
   straight by .cn-comments and its own hairline - two lines 80px apart
   with nothing between them. When nothing but the sign-in note stands
   between the two, the pill row's rule steps aside and the thread's own
   rhythm (40 - rule - 24 - heading) applies, as on every other reader.
*/
.cn-reader__rule:has(+ .cn-comments),
.cn-reader__rule:has(+ .cn-actions__note + .cn-comments) { display: none; }

/*
   THE PREVIEW NOTICE IS NOT A CAPTION ON THE PILLS. "You are watching a
   preview. Sign in to watch the full webinar." sat with 12px above it and
   nothing below, so it read as though it belonged to the button row rather
   than to the player it describes. Reported by the test team.
*/
.cn-reader__preview-note { margin: var(--cn-space-4) 0 var(--cn-space-8); }

.cn-reader__body li + li { margin-top: var(--cn-space-3); }

/*
   Premium body replaced by the upgrade prompt. Sized to hold the fold open so
   the rail does not ride up beside the byline when the body is withheld.

   The body is not in the model at all when this renders - it is not hidden
   with CSS, so there is nothing here for a dev tool to reveal.
*/
/*
   THE TEASER, and the fade off the bottom of it.

   The gate used to sit where the body would have started, directly under the
   action pills - a wall rather than an article you are part-way into. Now the
   first half of the piece runs above it and dissolves into the page.

   THE TEXT REALLY STOPS THERE. Connect2026Controller.teaser cuts the body on
   a paragraph boundary before it reaches the model, so this gradient is over
   the end of what was sent, not a curtain across text that is sitting in
   view-source. The live reader does the opposite - full description, white
   glow on top - and the whole article is readable from the page source of a
   page asking you to register to read it.

   A GRADIENT, NOT A GLOW. Live uses box-shadow: 0 -20px 100px 50px #fff,
   which is a white cloud spreading out of the banner and over the text. It
   also spreads sideways over the rail. An overlay pinned to this element
   fades only this element.

   The last line is dimmed as well as covered: the gradient starts fully
   transparent at 45% so the top of the teaser is untouched, then reaches the
   page colour with enough distance to look like ink running out rather than
   a panel laid on top.
*/
.cn-reader__teaser {
  position: relative;
  /* Enough that the fade has somewhere to happen even on a short teaser. */
  min-height: 180px;
}

.cn-reader__teaser::after {
  content: "";
  position: absolute;
  inset: 45% 0 0;          /* top 45% untouched, fade over the rest */
  pointer-events: none;    /* the text underneath stays selectable */
  background: linear-gradient(
      to bottom,
      rgba(255, 255, 255, 0) 0%,
      rgba(255, 255, 255, .65) 55%,
      var(--cn-bg) 100%);
}

/*
   The last paragraph should not end flush against the gate - the fade needs
   room to finish before the sign-in panel starts, or the two read as one
   block with a gradient in the middle of it.
*/
/*
   UNDER THE TEASER THE GATE SITS CLOSE - 17 Sep 2026. The sign-in block
   carries its own 48px margins for the places it stands alone, and the
   gated wrapper its 48px padding; stacked here they put 64px of nothing
   between the teaser's fade and the navy block (asked about on screen).
   Inside the wrapper the block has no margin of its own, and the wrapper
   gives it 24 above and 32 below.
*/
.cn-reader__teaser + .cn-reader__gated,
.cn-reader__teaser + * > .cn-reader__gated {
  padding-top: var(--cn-space-10);   /* 40, the readers' one gap */
  padding-bottom: var(--cn-space-8);
  min-height: 0;
}
/* ...and the teaser's last paragraph keeps its margin out of it, so the 40
   is 40 on every reader (the article's teaser ended on a 16px paragraph
   margin, the audio's did not: 40 and 24 for the same gate). */
.cn-reader__teaser > :last-child { margin-bottom: 0; }
.cn-reader__gated > .cn-signin { margin: 0; }

.cn-reader__gated {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--cn-space-5);
  min-height: 320px;
  padding: var(--cn-space-12) 0;
}

.cn-reader__gated-lede {
  margin: 0;
  font-size: var(--cn-text-section);
  font-weight: var(--cn-fw-semibold);
  line-height: var(--cn-lh-tight);
  color: var(--cn-ink);
}

.cn-reader__gated .cn-body { margin: 0; max-width: 52ch; }

/* The Connect Plus prompt's two doors in one row - membership first, sign in
   beside it for a signed-out reader - stacking full-width on a phone the way
   .cn-signin__actions does. */
.cn-reader__gated-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cn-space-4);
}
@media (max-width: 479px) {
  .cn-reader__gated-actions { align-self: stretch; }
  .cn-reader__gated-actions .cn-btn { flex: 1 1 100%; text-align: center; }
}

/* Pull quotes appear inside several readers. */
.cn-reader__body blockquote {
  margin-block: var(--cn-space-6);
  padding-left: var(--cn-space-5);
  border-left: 3px solid var(--cn-cyan);
  font-size: var(--cn-text-card);
  line-height: var(--cn-lh-snug);
  color: var(--cn-ink);
}

/* Reader-scoped overrides of the shared components' default spacing. */
.cn-reader__hero + .cn-reader__author { margin-top: 0; }

/* ============================================================== RAIL ===== */

.cn-rail > * + * { margin-top: var(--cn-space-8); }

/*
   A heading belongs to the block it labels, so that pair keeps the heading's
   own 16px and does not also take the rail's 32px inter-block gap.

   Visible on p1: "Trending this week" and "Most Read" share a baseline, and
   the hero image and the Most Read panel BOTH start at y=340.6. With the
   rail gap applied the panel started 16px lower than the hero and the two
   columns stopped lining up at the top of the page.

   Same specificity as the rule above, so order decides - keep it after.
*/
.cn-rail__heading + * { margin-top: 0; }

/*
   "Related Webinars" / "Related Interviews" use this same cyan 24px label but
   in the CONTENT column (p16, p18), so it is not rail-only. Same rule, second
   name - do not fork it.
*/
.cn-section-heading,
.cn-rail__heading {
  margin-bottom: var(--cn-space-4);
  font-size: var(--cn-text-section);
  font-weight: var(--cn-fw-medium);
  line-height: var(--cn-lh-tight);
  color: var(--cn-cyan);
}

/*
   Related Research - light-blue panel holding a cover and its title.

   CENTRED, and that is measured not assumed. On p3 the panel spans x
   370.7-559.3 (midpoint 465). The "Related Research" heading spans 421.1-490
   (midpoint 455) and the report cover 408.7-521.8 (midpoint 465) - both centre
   on the panel rather than sitting against its left edge.

   Padding is ~40px, not the 20px used elsewhere: panel top 653.7pt to heading
   top 672.9pt is 19.2pt, roughly 41px once the glyph ascender is allowed for.
*/
.cn-panel {
  padding: var(--cn-space-5);
}
.cn-panel--blue {
  background: var(--cn-panel-blue);
  padding: var(--cn-space-10);
  text-align: center;
}
.cn-panel--grey { background: var(--cn-panel-grey); color: var(--cn-ink-invert); }

/*
   A panel holding a LIST, not a feature. Home's Most Read is the only one.

   The centred, 40px-padded treatment above is right for p3's Related
   Research, where a single cover floats in the middle of the panel. It is
   wrong for a ranked list: p1 starts every one of the six titles at exactly
   x=960.5 and every numeral at about x=906, against a panel edge of 887.9 -
   so the rows are left-aligned on a ~20px pad, not centred on a 40px one.

   Centring the list also makes each row's left edge move with the length of
   its title, which is what turns a ranked list into a ragged column.
*/
.cn-panel--list {
  padding: var(--cn-space-5);
  text-align: left;
}

/*
   Reserves the height the artboard gives an undesigned panel. p1's "Data pull
   outs and polls" is 452.5 x 409.1 with only a heading and a paragraph of
   lorem in it - the contents are a client decision (polls need three new
   tables, and the social feed is a separate integration). Without this the
   panel collapses to its heading, about 124px, and the rail runs short of the
   content column beside it.
*/
.cn-panel--reserved { min-height: 409px; }

/*
   One byline. p3 and p18 show a single author beside a 65px photo; p20 stacks
   THREE, each with its own, because a podcast episode is a conversation. The
   row is a column of these so one and three both come out right.
*/
.cn-reader__author-one {
  display: flex;
  align-items: center;
  gap: var(--cn-space-2);
}
.cn-reader__author-one + .cn-reader__author-one { margin-top: var(--cn-space-3); }

/*
   flex-start, NOT the align-items: center this selector picks up higher in
   the file.

   That center was written when .cn-reader__author WAS the byline row, where
   it meant "line the avatar up with the text". Turning the row into a column
   of .cn-reader__author-one rows changed what the same declaration does:
   align-items works on the cross axis, and the cross axis of a column is
   HORIZONTAL, so it started centring the byline in the column instead.

   It only shows when the byline is narrower than the column, which is why it
   looked like an Articles-only fault: at 1440 the Article byline is 319px and
   sits 229px in from the left, while a Q&A byline runs the full 775 and has
   no room to move. Both were wrong; only one could be seen.

   The row's own centring is unaffected - .cn-reader__author-one sets it
   itself, which is where it belonged all along.
*/
.cn-reader__author {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/*
   The author's LinkedIn, inline at the end of the byline. Not on p3 - an
   author with no link leaves the row exactly as measured.
*/
.cn-author__linkedin {
  color: var(--cn-cyan);
  font-size: var(--cn-text-label);
  text-decoration: underline;
  white-space: nowrap;
}
.cn-panel--blue .cn-rail__heading { color: var(--cn-cyan); }

/*
   The cover inside the panel centres with it, and does NOT fill the width.
   Measured identically on p3 and p9: 272 x 384px, ratio 0.7071, at
   x 408.7-521.8. The panel's inner width is 372px (452 less 2x40 padding), so
   the cover sits at about 73% of it, centred - not edge to edge.
*/
.cn-panel--blue .cn-cover { align-items: center; }
.cn-panel--blue .cn-cover__art {
  max-width: 272px;
  margin-inline: auto;
  aspect-ratio: 0.7071;
  object-position: center;
}

/*
   "Read this article in the September Edition" - the magazine article reader.

   Not on p12: the brief asks for the flipbook link from the article (p19, and
   Andreas's note) and the artboard has no room for a sixth pill without
   breaking a row that measures exactly to the column edge. So it sits between
   the byline and the pills, where it reads as part of the byline block rather
   than as another control.

   Pulled up tight to the byline (-8) and given the pill row's own top gap
   below it, so adding the line does not move the pills.
*/
.cn-reader__inmag {
  margin: calc(var(--cn-space-2) * -1) 0 var(--cn-space-5);
  font-size: var(--cn-text-meta);
  font-weight: var(--cn-fw-medium);
  line-height: var(--cn-lh-body);
}

/* ============================================== MAGAZINE LANDING (p10) == */
/*
   THE HERO BAND. 1255.9 x 460.2 at the full container width - it runs across
   the rail, which nothing else in the module does - filled #BBE9FE, the only
   pale blue panel in the design. The cover sits 43px in from the band's left
   edge at 276.7 x 391.4, and the text column starts at x=451.3.

   Grid rather than flex so the cover keeps its width while the description
   takes whatever is left, exactly as measured: 43 + 277 + 45 + text.
*/
.cn-issue-hero {
  display: grid;
  grid-template-columns: 277px minmax(0, 1fr);
  /* Measured: cover ends 406.0, text starts 451.3 -> 45.3 between them, and
     (460.2 - 391.4) / 2 = 34.4 above and below. space-12 lands 48 and
     space-10 lands 40 at desktop; space-9 and -11 do not exist in the scale
     and an undefined token takes the whole declaration down with it. */
  column-gap: var(--cn-space-12);
  align-items: center;
  padding: var(--cn-space-10) var(--cn-space-12);
  background: var(--cn-panel-blue);
  margin-bottom: var(--cn-space-12);
}

.cn-issue-hero__cover { position: relative; }   /* for the Plus mark */
/* The cover PICTURE only - the direct child. The Connect Plus mark inside
   the same link carries its own <img>, and "img" here stretched that logo to
   the cover's shape (68 x 96 in a 84 x 21 badge). */
.cn-issue-hero__cover > img {
  display: block;
  width: 100%;
  aspect-ratio: 277 / 391;
  object-fit: cover;
}

.cn-issue-hero__body > * + * { margin-top: var(--cn-space-5); }

/*
   24px navy, the same size the description is set at on the board - larger
   than body copy because it is doing the hero's talking.
*/
.cn-issue-hero__title {
  margin: 0;
  font-size: var(--cn-text-h1);
  font-weight: var(--cn-fw-semibold);
  line-height: var(--cn-lh-tight);
  color: var(--cn-ink);
}

.cn-issue-hero__text {
  /*
     RE-MEASURED off p10's text runs: 20px, 26.7 line pitch (1.33), twelve
     lines in two paragraphs - four then eight - with 34 between them. An
     earlier 24px/8-line reading was the section-heading size and overran the
     band. The issue name is the copy's first words; there is no title.
  */
  font-size: var(--cn-text-card);
  font-weight: var(--cn-fw-medium);
  line-height: 1.33;
  color: var(--cn-ink);
  /*
     The CRM's description runs to whatever length the editor wrote. Clamped
     at the board's twelve lines so a long one cannot push the band past its
     measured height; the issue page carries the full text.
  */
  display: -webkit-box;
  -webkit-line-clamp: 12;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Word leaves paragraphs in here; they must not double the gaps. */
.cn-issue-hero__text p { margin: 0 0 var(--cn-space-3); }
.cn-issue-hero__text p:last-child { margin-bottom: 0; }

/* Whatever inline sizing the CRM's editor left behind, the band sets it. */
.cn-issue-hero__text p,
.cn-issue-hero__text span { font-size: inherit !important; line-height: inherit; }

/* The name is in the outline only; it must not spend the rhythm's 20px. */
.cn-issue-hero__body > .cn-sr-only + * { margin-top: 0; }

/* p10: the pill sits 25 under the copy, not the 20 the body rhythm gives. */
.cn-issue-hero__body > .cn-btn { margin-top: var(--cn-space-6); }

/* =========================================== MAGAZINE LANDING BLOCKS == */
/*
   THE GAPS DOWN THE CONTENT COLUMN, each measured on p10 (real px, band foot
   to block, ink to ink where the block is type). The shell's .cn-section
   rhythm is 80 between sections; p10 runs 32-57. So none of these blocks is
   a .cn-section, and each carries its own number:

     band foot 793.8  -> skyscraper 831.1          37   (grid starts)
                      -> "Featured" ink 857.9      64   (heading 20 into the grid)
     heading ink foot -> first eyebrow             16
     row 4 pill foot  -> quote block 1545          43
     quote foot 1796  -> "Archive" ink 1835        39
     heading ink foot -> covers 1889.8             27
     pill foot ~2245  -> pager 2278                33
     pager            -> upgrade block 2328        ~32
     upgrade foot 2789-> "Contribute" ink 2838     49
     heading ink foot -> copy 2876                 13
     copy foot 2958   -> advert 2986               28
     advert foot 3238 -> newsletter 3275           37
*/
.cn-magazine .cn-issue-hero { margin-bottom: var(--cn-space-10); }
.cn-magazine__featured { margin-top: var(--cn-space-5); }

/*
   FEATURED ROWS: rules BETWEEN rows only. The listing grid rules every row on
   its top edge, which draws one under the section heading; p10 has none there
   and none after the fourth row.
*/
.cn-magazine__featured .cn-headline-row:first-of-type { border-top: 0; padding-top: 0; }

/*
   THE PULL-OUT QUOTE, 743.6 x 251 on p10.

   The artboard draws a solid grey block and nothing else - which is why
   --cn-panel-grey's own comment in tokens.css already says "solid grey
   blocks, pull-out quote". So the panel is built to that rather than
   invented: the issue's grey block, the quote set large in white, the
   attribution under it.

   It is only rendered when an issue HAS a quote (see magazine.jsp), so the
   captioned empty box the placeholder used to leave is gone either way.

   min-height, not height: 251 is what the board draws for the quote it
   drew, and a longer quote has to be allowed to make the panel taller
   rather than spill out of it.
*/
.cn-magazine__quote {
  min-height: 251px;
  padding: 30px;
  text-align: left;
  margin-top: var(--cn-space-8);
  margin-bottom: var(--cn-space-8);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--cn-space-4);
  background: var(--cn-panel-grey);
  border-radius: var(--cn-radius-card);
  color: #ffffff;
}

/*
   The mark is drawn, not typed: a " in the markup is read out by a screen
   reader and lands in the page's text, and the quote is already inside a
   <blockquote> with its attribution. Pure decoration belongs in CSS.
*/
.cn-magazine__quote::before {
  content: "“";
  font-size: 72px;
  line-height: 0.8;
  font-weight: var(--cn-fw-bold);
  color: var(--cn-cyan);
}

.cn-magazine__quote blockquote {
  margin: 0;
  font-size: var(--cn-text-h3);
  line-height: var(--cn-lh-snug);
  font-weight: var(--cn-fw-medium);
}

.cn-magazine__quote figcaption {
  font-size: var(--cn-text-meta);
  line-height: var(--cn-lh-snug);
  color: rgba(255, 255, 255, 0.85);
}

/* The page it came from is a reference, not part of the attribution. */
.cn-magazine__quote-page {
  display: inline;
  color: rgba(255, 255, 255, 0.65);
}

@media (max-width: 767px) {
  .cn-magazine__quote {
    min-height: 0;
    padding: var(--cn-space-6);
  }
  .cn-magazine__quote::before { font-size: 56px; }
  .cn-magazine__quote blockquote { font-size: var(--cn-text-card); }
}

.cn-magazine__archive .cn-section-heading { margin-bottom: var(--cn-space-6); }
/* p10: each "View >" is centred under its cover (cover 88-300, pill centred
   on 194), where the featured rows' pills sit on the left edge. */
.cn-magazine__archive .cn-cover { align-items: center; }
/* Centred pill, 24 under the cover as drawn (2197 -> 2221 ink). */
.cn-magazine__archive .cn-cover { gap: var(--cn-space-4); }
.cn-magazine__archive .cn-listing__pagination { margin-top: var(--cn-space-8); }
.cn-magazine__archive { margin-bottom: var(--cn-space-8); }

.cn-magazine__contribute { margin-top: var(--cn-space-10); }
.cn-magazine__contribute .cn-section-heading { margin-bottom: var(--cn-space-3); }

/* The contribute copy: p10 sets four lines of 17.6px at a 22.2 pitch. */
.cn-magazine__contribute-copy {
  max-width: 70ch;
  margin: 0;
}
.cn-magazine__contribute .cn-btn { margin-top: var(--cn-space-4); }
.cn-magazine__contribute-ad { margin-top: 28px; }
.cn-magazine .cn-grid-rail + .cn-newsletter { margin-top: var(--cn-space-10); }

/*
   THE RAIL keeps the card's own row (p10: Related Reports eyebrows step
   106.6, rules between rows), not the listing grid's 123 - the grid class is
   on this page for its content rows and was reaching the rail too. Skyscraper
   foot 2189 -> "Related Reports" ink 2213.8 is ~20; rows -> Forthcoming
   Events 2679.7 is ~40.
*/
.cn-magazine .cn-rail .cn-headline-row {
  padding-top: var(--cn-space-3);
  padding-bottom: var(--cn-space-2);
  border-top: 0;
  border-bottom: 1px solid var(--cn-ink-body);   /* the card's own rule - see card.css */
}
.cn-magazine .cn-rail .cn-headline-row:first-child { padding-top: 0; }
.cn-magazine .cn-rail .cn-headline-row:last-child { border-bottom: 0; }
.cn-magazine .cn-rail > * + * { margin-top: var(--cn-space-5); }
.cn-magazine .cn-rail > .cn-fce { margin-top: var(--cn-space-10); }

/*
   BUILT AS HOME'S CONNECT PLUS PANEL IS (home.css .cn-home__plus): the mid
   grey panel, cyan heading, a semibold navy lede, one line of body and the
   button - with the wide creative touching its foot. Same numbers, so the two
   pages' upgrade blocks read as one thing. 460 on p10 is the panel plus the
   creative, not the panel alone.
*/
.cn-upgrade {
  padding: 46px 25px;
  background: var(--cn-band-mid);
}
.cn-upgrade .cn-section-heading { margin-bottom: var(--cn-space-3); }
.cn-upgrade p {
  margin: 0;
  max-width: 70ch;
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink);
}
.cn-upgrade p + p { margin-top: var(--cn-space-2); }
.cn-upgrade__lede { font-weight: var(--cn-fw-semibold); color: var(--cn-navy); }
.cn-upgrade__cta { margin-top: var(--cn-space-4); }
.cn-upgrade__ad { margin-top: 0; }

/* =========================================================== RESPONSIVE == */
@media (max-width: 767px) {
  /* The band cannot hold 277 + text on a phone; the cover leads. */
  .cn-issue-hero {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--cn-space-6);
    padding: var(--cn-space-6);
  }
  .cn-issue-hero__cover > img { max-width: 220px; }
  .cn-issue-hero__text { font-size: var(--cn-text-author); }
  .cn-upgrade { padding: var(--cn-space-6) var(--cn-space-4); }
}

/* ================================================ MAGAZINE ISSUE (p11) == */
/*
   ONE ROW OF CONTROLS under the edition (or under the cover block that
   stands in for it): Read now, then the action pills. The pill bar is its own
   flex list with a 40px foot for the readers; here it sits beside the button,
   so the foot goes and the row wraps as one. p11 sets the pills at y=1194,
   36 tall, 40 under the edition.
*/
.cn-issue-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cn-space-3);
  /* p11: edition foot 1154.5 -> pills 1194.3, so 40 above as well as below. */
  margin-top: var(--cn-space-10);
  margin-bottom: var(--cn-space-10);
}
/* The cover block's own foot would double the 40 above the row - and so
   would the standfirst's 24 and the CRM's last paragraph margin inside it.
   The row's 40 is the only gap. */
.cn-issue .cn-issue-cover { margin-bottom: 0; }
.cn-issue .cn-issue-cover .cn-reader__standfirst { margin-bottom: 0; }
.cn-issue .cn-issue-cover .cn-reader__standfirst > :last-child { margin-bottom: 0; }
/* The pill list flows inline with the leading button - one row, wrapping
   pill by pill rather than the whole list dropping under the button. */
.cn-issue-actions .cn-actions { display: contents; }

/* One quiet line under the controls saying why Read now goes where it goes. */
.cn-issue-actions__note {
  /* 12 under the pills, the row's 40 under itself. */
  margin: calc(var(--cn-space-10) * -1 + var(--cn-space-3)) 0 var(--cn-space-10);
  font-size: var(--cn-text-meta);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-body);
}

/*
   p11 draws no title at all - the flipbook carries the branding - but the live
   issue page does, and a reader with no heading is a reader a screen reader
   cannot summarise. Kept, sized down from the 34.8 hero title because it is
   standing alone rather than balancing an image.
*/
.cn-reader__title--full {
  margin: 0 0 var(--cn-space-6);
  font-size: var(--cn-text-h1);
  line-height: var(--cn-lh-tight);
}

/*
   WHAT A READER SEES INSTEAD OF THE EDITION when they are not entitled to it:
   the cover beside the issue description and one prompt. Sized off p10's hero,
   which is the only place the design draws a cover at all - 276.7 x 391.4,
   about a third of the column.
*/
.cn-issue-cover {
  display: grid;
  grid-template-columns: 277px minmax(0, 1fr);
  column-gap: var(--cn-space-6);
  align-items: start;
  margin-bottom: var(--cn-space-8);
}

.cn-issue-cover__art {
  width: 100%;
  aspect-ratio: 277 / 391;
  object-fit: cover;
  border: 1px solid var(--cn-border);
}

.cn-issue-cover__body > * + * { margin-top: var(--cn-space-4); }

/*
   The article list, and the 2px cyan rule at y=1264.2 that opens it.

   Deliberately NOT .cn-reader__body, which carries the Word-HTML taming rules
   - its `*` selectors force font-size and colour to inherit, which would
   flatten every headline row in this list. Only the rule is wanted here.
*/
.cn-issue-articles {
  padding-top: var(--cn-space-8);
  border-top: 2px solid var(--cn-cyan);
}

/* p11 steps 163.6 between rows, against the 113 a listing uses: each row here
   carries a "View >" pill under its title, and that is the difference. */
.cn-issue-articles .cn-headline-row { padding-bottom: var(--cn-space-4); }

/* =========================================================== RESPONSIVE == */
@media (max-width: 767px) {
  /* Two columns of 277 + text do not fit a phone; the cover leads instead. */
  .cn-issue-cover {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--cn-space-5);
  }
  .cn-issue-cover__art { max-width: 220px; }
}

/*
   CONTRIBUTE BLOCK - the magazine readers' rails, and p10's content column.

   MEASURED 452.5 x 341.6 in the rail on both p11 (y=2255) and p12 (y=2329.6).
   The artboard draws a black box captioned "Contribute section", so the box is
   designed and its contents are not; the words come from the brief. See
   contributeBlock.tag.

   min-height rather than height: the copy is the client's and may grow, and a
   fixed 342 would clip it. It holds the measured space when the text is short,
   which is the point of the number.
*/
.cn-contribute {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--cn-space-4);
  padding: var(--cn-space-6);
  background: var(--cn-panel-grey);
  color: var(--cn-ink-invert);
  text-align: center;
}

.cn-contribute--rail { min-height: 342px; }

/* p10 runs it across the 743.6 content column, where it reads as a band. */
.cn-contribute--column { min-height: 251px; }

.cn-contribute__heading {
  margin: 0;
  font-size: var(--cn-text-section);
  font-weight: var(--cn-fw-medium);
  line-height: var(--cn-lh-tight);
  /*
     WHITE, NOT CYAN. Cyan on this grey panel measures 3.18:1 against a 4.5
     floor. Nothing in the design dictates the colour - the artboard draws an
     empty captioned box - so there is no reason to ship an unreadable heading.
     White is 7.23:1.
  */
  color: var(--cn-ink-invert);
}

.cn-contribute__body {
  margin: 0;
  max-width: 46ch;
  font-size: var(--cn-text-meta);
  font-weight: var(--cn-fw-medium);
  line-height: var(--cn-lh-body);
}

/* Pre-wiring state. Quieter than the body copy, and not a control. */
.cn-contribute__note {
  margin: 0;
  font-size: var(--cn-text-label);
  opacity: 0.7;
}

/*
   Unwired: the artboard's own state, which is a captioned box and nothing
   more. p11 and p12 both draw exactly this - a grey panel with the words
   "Contribute section" centred in it - so nothing here is invented; the
   panel's colour, padding and centring already come from .cn-contribute
   above and only the type needs setting.

   MUST NOT SHIP with the caption showing. Check with:
     curl -s <any magazine reader> | grep -c "cn-contribute--placeholder"
   It disappears the moment CONNECT_CONTRIBUTE_URL exists.
*/
.cn-contribute--placeholder {
  font-size: var(--cn-text-card);
  font-weight: var(--cn-fw-semibold);
}

/* =========================================================== RESPONSIVE == */
/* T-40 spec - see CONNECT2026.md section 4.7. */
@media (max-width: 1279px) {
  .cn-rail > * + * { margin-top: var(--cn-space-6); }
}

@media (max-width: 767px) {
  .cn-reader__hero { grid-template-columns: minmax(0, 1fr); row-gap: var(--cn-space-4); }
  .cn-reader__author { gap: var(--cn-space-3); }
  .cn-reader__avatar-wrap { width: 48px; height: 48px; }
  .cn-reader__avatar-initials { font-size: var(--cn-text-meta); }
}

/* ========================================================== SHELL B: Q&A === */
/*
   Event Q&A (p7). Structurally it is Shell A - same 771px column, same
   387/22/361 split hero, same five action pills, same standfirst and key
   points. Four things differ:

     1. the hero image is composed EVENT ARTWORK (CeFPro mark, event name,
        speech bubbles, speaker photo) rather than a stock photo. That is a
        content difference, not a structural one - no "brand block" to build.
     2. NO author avatar. The byline starts at x=35.3, hard against the left
        margin, where Article's starts at x=66.2 indented past its 65px avatar.
     3. the body is a Q&A sequence rather than prose.
     4. a speaker bio block closes the page.

   MEASURED on p7:
     question   7.5pt SemiBold #001c64   answer 7.5pt Medium #575756
     answer follows its question immediately; 25px between Q&A pairs
     bio block  769 x 256px, bordered, 134px circular avatar, cyan text
*/

.cn-qa__q {
  font-size: var(--cn-text-body);
  font-weight: var(--cn-fw-semibold);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink);
}

.cn-qa__a {
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-muted);
}

/* Answer sits directly under its question; the gap goes between PAIRS. */
.cn-qa__q + .cn-qa__a { margin-top: 0; }
.cn-qa__a + .cn-qa__q { margin-top: var(--cn-space-6); }

/* ------------------------------------------------------- speaker bio ---- */

/*
   768.8 x 256.1 on p7, effectively the full 775 content column, with a 2px
   border. The artboard renders that border pure black; used --cn-border-strong
   here because black is the image-placeholder colour in this design file and
   reads the same at 2px. The photo is 133.7 and sits 26px in from the edge.
*/
.cn-speaker-bio {
  display: flex;
  align-items: flex-start;
  gap: var(--cn-space-6);
  margin-top: var(--cn-space-12);
  padding: var(--cn-space-6);
  border: 2px solid var(--cn-border-strong);
  border-radius: var(--cn-radius-card);
}
.cn-speaker-bio + .cn-speaker-bio { margin-top: var(--cn-space-6); }

.cn-speaker-bio__body { min-width: 0; }

/*
   Name, role and company repeat the byline convention measured on p3 and p18 -
   cyan, italic, bold - so the panel reads as the same person as the byline at
   the top of the page.
*/
.cn-speaker-bio__name {
  margin: 0;
  font-size: var(--cn-text-section);
  font-weight: var(--cn-fw-semibold);
  line-height: var(--cn-lh-tight);
  color: var(--cn-cyan);
}

.cn-speaker-bio__role {
  margin: var(--cn-space-1) 0 0;
  font-size: var(--cn-text-author);
  line-height: var(--cn-lh-snug);
  color: var(--cn-ink-muted);
}
.cn-speaker-bio__role strong { font-weight: var(--cn-fw-semibold); color: var(--cn-ink); }

.cn-speaker-bio__link {
  display: inline-block;
  margin-top: var(--cn-space-3);
  font-size: var(--cn-text-meta);
  font-weight: var(--cn-fw-medium);
  color: var(--cn-fam-cyan-ink);
  text-decoration: underline;
}

/* The disc with initials under the photo - see speakerBio.tag. */
.cn-speaker-bio__avatar-wrap {
  position: relative;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 134px;
  height: 134px;
  overflow: hidden;
  border-radius: var(--cn-radius-pill);
  background: var(--cn-navy);
}
.cn-speaker-bio__initials {
  font-size: var(--cn-text-h2);
  font-weight: var(--cn-fw-semibold);
  line-height: 1;
  color: var(--cn-ink-invert);
  user-select: none;
}
.cn-speaker-bio__avatar {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: var(--cn-radius-pill);
  object-fit: cover;
  background: var(--cn-band-grey);
}

/*
   The biography itself is prose, so it takes the reader body treatment -
   18px muted, not the 24px cyan the artboard's placeholder caption used.
*/
.cn-speaker-bio__text {
  margin: var(--cn-space-3) 0 0;
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-muted);
}

@media (max-width: 767px) {
  .cn-speaker-bio { flex-direction: column; align-items: center; text-align: center; }
  .cn-speaker-bio__avatar { width: 96px; height: 96px; }
}

/* ======================================================== SHELL C ======== */
/*
   Webinar (p16) and Filmed Interview (p18).

   Shell C is NOT a variant of the split hero. There is no hero still at all:
   the title runs the full 771px column, and a 16:9 video sits where Shell A
   puts its body. Use .cn-reader__hero--full, or just an <h1> on its own.

   MEASURED DIFFERENCES BETWEEN THE TWO PAGES - they are not the same page
   with different copy:

                          Webinar (p16)          Interview (p18)
     author row           none                   65px avatar + byline
     above the video      "Webinar agenda"       two plain paragraphs
                          + 6 tight bullets      (no standfirst, no points)
     below the pills      "Webinar Speakers"     "Related Interviews"
                          3-up speaker grid      headlineRow list, in-column
     related strip        FOUR text columns,     none
                          divided by hairlines,
                          FULL width below the
                          grid - NOT cards
     rail                 half ad -> FE card -> newsletter        (identical)

   NOTE the rail here is a HALF ad (452x679), not the skyscraper the listings
   use. Both artboards agree.

   THE RELATED STRIP ROW WAS WRONG HERE and said "3-up cards". Rendering p16 at
   3x and looking shows four columns of text - a magenta "Webinars" eyebrow
   over a navy title, with a vertical hairline between each and no image
   anywhere. Three cards would have been a different component and a different
   height. Render the board before trusting a shape written from a vector
   scan; this file has now made that mistake twice.
*/

/* In-page section heading - "Webinar agenda", "Webinar Speakers". 13pt/32px,
   Medium, in the TOPIC colour (set data-topic on the element). Distinct from
   .cn-section-heading below, which is the cyan 10pt/24px "Related X" label. */
.cn-reader__heading {
  margin: var(--cn-space-3) 0 var(--cn-space-2);   /* artboard 10 / ~0 */
  font-size: var(--cn-text-h2);
  font-weight: var(--cn-fw-medium);
  line-height: var(--cn-lh-tight);
}

/* Separates the speaker / related block from the pills-and-rule above it.
   Artboard 69px; the rule's own 24px bottom margin collapses into this. */
.cn-reader__section { margin-top: var(--cn-space-15); }

/*
   Shell C has no hero still, so the meta line is not sitting tight against an
   image edge. The artboard opens it to 37px before the title, where Shell A
   keeps 10px.

   NOTE this does NOT add to .cn-meta-line's own 12px bottom margin - adjacent
   siblings collapse to the larger of the two, so the value here IS the gap.
*/
.cn-meta-line + .cn-reader__hero--full { margin-top: var(--cn-space-10); }

/*
   Pills straight into the closing rule, with no standfirst between them as
   Shell A has. Artboard 61px; .cn-actions' own 40px bottom margin collapses
   into this rather than adding to it.
*/
.cn-actions + .cn-rule { margin-top: var(--cn-space-15); }

/* Video frame. Artboard: 16px above (10 on p18), 24-28px below to the pills. */
.cn-video-wrap {
  margin-top: var(--cn-space-4);
  margin-bottom: var(--cn-space-6);
}

/*
   Webinar agenda list. Deliberately NOT .cn-reader__points - the agenda steps
   line-to-line with no gap between items (10.05pt throughout, measured), where
   the key-points list opens 12px between items. Marker sits flush in the
   margin at x=0 with the text indented 40px, which list-style cannot do on its
   own without pulling the text back in.
*/
.cn-reader__agenda {
  margin: 0;
  padding-left: 0;
  list-style: none;
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-muted);
}
.cn-reader__agenda li { position: relative; padding-left: 40px; }
.cn-reader__agenda li::before {
  content: "•";
  position: absolute;
  left: 0;
  top: 0;
}

/*
   Webinar speaker row. Three .cn-person--speaker cards, centred, 164px
   avatars. Intrinsic like every other grid here: it drops to 2-up then 1-up
   from its OWN width, so it still shows three across when the rail stacks
   away and the column gets wider - a viewport ladder would show fewer.
*/
.cn-speaker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit,
    minmax(max(180px, (100% - 2 * var(--cn-space-6)) / 3), 1fr));
  gap: var(--cn-space-8) var(--cn-space-6);
  margin-top: var(--cn-space-6);   /* artboard 27 */
}

@media (max-width: 767px) {
  /* Avatar size on small screens is owned by person.css, not here. */
  .cn-reader__agenda li { padding-left: 24px; }
}


/* ==================================================== SHELL D: AUDIO ===== */
/*
   Audio (p20). Shell C's layout with the video swapped for a third-party
   audio embed, plus one genuinely new piece:

     MULTI-AUTHOR STACK. p20 draws THREE author rows, each with its own 65px
     avatar at x=36.3, stepping 34.2pt = 82px. Every other reader has one
     author or none. A panel discussion has several, so this is the general
     case and the single byline is the special one.

   Everything else is reuse: title-only hero, action pills, and the same
   in-column "Related audio" headlineRow list p18 uses.
*/

/*
   Stacked bylines close up to 82px between avatar tops - 65px avatar plus
   16px - where a lone byline keeps its 32px run-out to the pills. Setting it
   on `+` means only the gaps BETWEEN authors tighten; the last row still
   carries the full 32px.
*/
.cn-author-stack .cn-reader__author + .cn-reader__author { margin-top: 0; }
.cn-author-stack .cn-reader__author { margin-bottom: var(--cn-space-4); }
.cn-author-stack .cn-reader__author:last-child { margin-bottom: var(--cn-space-8); }

/* The audio embed sits where the video does, with the same run-in and
   run-out. Artboard: pills at y=387.9, embed bottom 377.5 - 25px. */
.cn-embed + .cn-actions { margin-top: var(--cn-space-6); }
.cn-author-stack + .cn-embed { margin-top: var(--cn-space-2); }

/* =================================================== SHELL E: REPORT ===== */
/*
   Report (p14) and Magazine issue (p11). Premium readers - both carry the
   Connect Plus lockup in the meta line and open with "Download PDF" rather
   than "Share".

   REPORT HERO - measured on p14, and it is NOT the Shell A split hero:

     cover 235  |  gap 16  |  text 520      = 771

   The cover is a portrait report cover at 235x326 (ratio 0.721), not the
   1.616 landscape still Shell A uses. The text column holds the title, a 2px
   cyan rule at its full 520px, and a "Read now" primary button - measured
   129x36 at x=140.7, which is the standard pill height.

   MAGAZINE ISSUE has no hero and no title at all: the meta line runs straight
   into a 769x685 embedded-document block. Do not add a title to it "for
   consistency" - the artboard genuinely does not have one.
*/
/* Lead-in line that introduces a list - "Discover:" on p14. Body copy in
   every respect; it exists only to carry the spacing to the list below. */
.cn-reader__lead {
  margin: 0 0 var(--cn-space-4);
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-muted);
}

.cn-report-hero {
  display: grid;
  grid-template-columns: 235fr 520fr;
  column-gap: 16px;
  align-items: start;
  margin-bottom: var(--cn-space-10);
}

.cn-report-hero__cover {
  width: 100%;
  aspect-ratio: 235 / 326;
  object-fit: cover;
  background: var(--cn-ph-img);
}

/* The title starts 58px below the cover's top edge, not level with it. */
.cn-report-hero__body { padding-top: var(--cn-space-15); }

/* 2px, --cn-cyan, full width of the text column. Sampled straight off the
   render at (28,183,252) - the vector's stroking colour reads #1a3e70, which
   is not what the artboard actually draws.
   p14: title ink foot 698.4 -> rule 704.2 (6), rule -> Read now pill 737
   (33). */
.cn-report-hero__rule {
  height: 2px;
  margin: var(--cn-space-2) 0 var(--cn-space-8);
  border: 0;
  background: var(--cn-cyan);
}

/*
   THE RULE UNDER THE PROSE, p14 y=1418.2: 1px cyan across the reader column,
   52 below the last line, the pills 65 under it, the embed 33 under the
   pills. The prose block's own foot (the Word HTML's last paragraph margin)
   is taken out so the 52 is one number.
*/
.cn-report__rule {
  height: 1px;
  margin: var(--cn-space-12) 0 var(--cn-space-15);
  border: 0;
  background: var(--cn-cyan);
}
.cn-report .cn-reader__body > :last-child { margin-bottom: 0; }
/* p14: meta ink foot 465.4 -> cover 490.3, 25. */
.cn-report .cn-report-hero { margin-top: var(--cn-space-6); }
.cn-report .cn-actions { margin-bottom: var(--cn-space-8); }

/* ============================================ RESEARCH HERO VARIANT ==== */
/*
   p13 Latest Report uses the SAME hero as p10 Latest Magazine - full
   container, 1258 x 460, cover 277 x 391 on the left, text on the right. Only
   the fill and three extra text parts differ, so this is a modifier rather
   than the separate component an earlier pass built.

   THE FILL IS A GRADIENT, left to right:
       rgb(238,118,51) -> rgb(249,207,175)
   sampled off the render at y=300 across x=57..895. Reading the page shape by
   shape finds no flat fill and reports no band at all, which is exactly what
   happened - hence the note in research.jsp about rendering before trusting a
   scan.
*/
.cn-issue-hero--report {
  background: linear-gradient(90deg, #ee7633 0%, #f9cfaf 100%);
}

/*
   THREE LINES HERE, EIGHT ON THE MAGAZINE.

   Both bands are 460 tall, but they spend that height differently: p10 gives
   its description eight lines and carries no eyebrow, while p13 spends the
   space on an eyebrow, a title and two pills and leaves the description three
   lines (y=236, 248, 260). Inheriting the magazine clamp rendered this band
   552 tall - 92px past the board - because the CRM description is far longer
   than either count.
*/
.cn-issue-hero--report .cn-issue-hero__text { -webkit-line-clamp: 3; }

/*
   p13's copy is 24px, not p10's 20: three lines of Poppins Medium at a 26.4
   pitch (568.9, 595.3, 621.7), navy. And it sits 5 under the title's ink
   (563.9 -> 568.9) where the body rhythm would put 20.
*/
.cn-issue-hero--report .cn-issue-hero__text {
  font-size: var(--cn-text-section);
  line-height: 1.1;
}
.cn-issue-hero--report .cn-issue-hero__title + .cn-issue-hero__text { margin-top: var(--cn-space-2); }
/* Eyebrow ink foot 474.4 -> title ink 490.7: 16 of ink, ~8 of box. */
.cn-issue-hero--report .cn-issue-hero__eyebrow + .cn-issue-hero__title { margin-top: var(--cn-space-2); }
/* Copy foot 645.7 -> pills 656: 10. */
.cn-issue-hero--report .cn-issue-hero__text + .cn-issue-hero__actions { margin-top: var(--cn-space-3); }

/*
   THE GAPS DOWN THE CONTENT COLUMN, each measured on p13 (real px):
     band foot 797     -> rail panel 831 / "Latest Research" ink 846   37 / 49
     heading           -> survey block 871.5                            ~1
     survey block foot -> "Report Library" ink 1326   (the lorem copy
                          between them is not built)                   40
     heading ink foot  -> covers 1358.8                                  9
     cover foot 1670   -> "View report >" pill 1699                    29
     pill foot 1739    -> first row rule 1759                           20
     last row 2652     -> pager 2669                                    17
     skyscraper foot   -> newsletter 2710                               36
*/
.cn-research .cn-issue-hero { margin-bottom: var(--cn-space-10); }
.cn-research__latest { margin-top: 0; }
.cn-research__latest .cn-section-heading { margin-bottom: var(--cn-space-2); }
/* p13: the copy sits 24 under the banner, body size, the column's width. */
.cn-research__survey-copy {
  margin: var(--cn-space-6) 0 0;
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink);
}
.cn-research__library { margin-top: var(--cn-space-10); }
.cn-research__library .cn-section-heading { margin-bottom: var(--cn-space-3); }
.cn-research__library .cn-cover { align-items: center; gap: 28px; }
.cn-research__rows { margin-top: var(--cn-space-5); }
.cn-research .cn-listing__pagination { margin-top: var(--cn-space-2); }
.cn-research .cn-grid-rail + .cn-newsletter { margin-top: var(--cn-space-10); }

/* p13 sets a "Report" eyebrow above the title; p10 has none. */
.cn-issue-hero__eyebrow {
  display: block;
  font-size: var(--cn-text-meta);
  font-weight: var(--cn-fw-medium);
  color: var(--cn-ink);
}

.cn-issue-hero__title {
  margin: 0;
  font-size: var(--cn-text-h1);
  font-weight: var(--cn-fw-semibold);
  line-height: var(--cn-lh-tight);
  color: var(--cn-ink);
}

/* Two pills side by side, wrapping rather than overflowing the text column. */
.cn-issue-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cn-space-3);
  margin: 0;
}

/*
   "Download report >" is drawn wider than "View report >" - 82.2 x 14.2pt
   against 65.5 x 14.2, so 197px against the standard 157. The longer label,
   not a different component.
*/
.cn-headline-row__cta--wide { min-width: 197px; }

/*
   p13's rail panel. 453 x 448 with a cyan heading and a paragraph inside -
   a panel, not one of the black captioned placeholders. The height is
   reserved because what fills it is undesigned.
*/
.cn-panel--polls { min-height: 448px; }

/* =============================================== SHELL D/E RESPONSIVE ==== */
@media (max-width: 767px) {
  /* The cover beside a 4-line title is unreadable under ~600px. Stack it and
     cap the cover so it does not become a full-width poster. */
  .cn-report-hero {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--cn-space-5);
  }
  .cn-report-hero__cover { max-width: 235px; }
  .cn-report-hero__body { padding-top: 0; }
}

/* ============================================================= COMMENTS == */
/*
   NO ARTBOARD DRAWS THIS. The design puts a Comment pill in every reader's
   action bar and then never shows what pressing it does, so this is built on
   the token scale rather than measured - deliberately plain, so it reads as
   part of the page rather than competing with the Q&A above it.

   Closed until the pill is pressed. See js/comments.js.
*/
.cn-comments {
  /*
     THE THREAD'S OWN RHYTHM, measured against the block above it (the pills
     or the body) and the rule below (events / newsletter), so it reads as one
     more section of the reader and not a form dropped under it:

       block above  40  rule (hairline, --cn-border-mid like the pill row's
                        rule and the partitions - the ink-black line read as
                        a stray underline, and #d8dade did not register: on
                        a webinar or interview this IS the only line between
                        the pills and the thread, see .cn-reader__rule:has)
       rule         24  "N Comments" heading
       heading       6  house-rules line
       rules        20  textarea
       textarea     12  Post comment
       button       24  "No comments yet" / the thread
       thread       24  Load more
       last thing   40  the rule below (padding, so the last child's own
                        margin cannot add to it whichever child that is)
  */
  margin-top: var(--cn-space-10);
  margin-bottom: 0;
  padding-top: var(--cn-space-6);
  /* No bottom padding of its own: the 40 to the rule below belongs to the
     GRID (next rule), so it holds whichever column is the taller one. */
  padding-bottom: 0;
  border-top: 1px solid var(--cn-border-mid);
}
.cn-comments > :last-child { margin-bottom: 0; }

/*
   Comments closing the article column: the events rule follows at the block's
   own 40, not 40 + 80. Scoped to exactly that adjacency - a reader whose
   column ends some other way keeps the section rhythm it has.
*/
.cn-grid-rail--reader:has(article > .cn-comments:last-child) + .cn-section--ruled {
  margin-top: 0;   /* kept for older engines without :has on the rule above */
}
/*
   ...and the 40 is carried by the grid, not by the comments block. With it
   on the block, the rule was 40 under the comments only while the article
   column was the taller one; below 1280 the rail stacks underneath and on
   any width where the rail runs longer, the events panel ended flush
   against the cyan rule. Reported on the phone view of the article reader.
*/
/*
   ONE GAP UNDER EVERY READER COLUMN - 17 Sep 2026. Whatever ends the column
   (comments, the sign-in gate, the body), the newsletter band follows at 40:
   the grid carries the 40 as padding, the things that can end the column
   carry nothing of their own, and the band's stand-alone 32 is switched off
   right after a reader grid. Measured before: 72 under comments, 64 under
   the gate, signed in and out reading differently.
*/
.cn-grid-rail--reader { padding-bottom: var(--cn-space-10); }
.cn-grid-rail--reader article > .cn-reader__gated:last-child { padding-bottom: 0; }
.cn-grid-rail--reader + .cn-newsletter--full,
.cn-grid-rail--reader + .cn-section--ruled { margin-top: 0; }

/* The signed-out invitation, which has no form to space out. */
.cn-comments .cn-rail__heading { margin-bottom: var(--cn-space-2); }
/* The sign-in block inside the thread (a signed-out reader on an open
   piece) sits on the thread's own rhythm - its stand-alone 48px margins
   put 73px between the rule and the navy block. */
.cn-comments > .cn-signin { margin: var(--cn-space-4) 0 0; }

.cn-comments__rules {
  margin: 0 0 var(--cn-space-5);
  font-size: var(--cn-text-meta);
  color: var(--cn-ink-muted);
}

/* "No comments yet. Be the first." and the error line - a paragraph of its
   own under the form, not a caption glued to the button. */
.cn-comments__note {
  margin: var(--cn-space-6) 0 0;
  font-size: var(--cn-text-body);
  color: var(--cn-ink-muted);
}

.cn-comments__note.is-error { color: var(--cn-fam-orange); }
.cn-comments__note:empty { display: none; }

.cn-comments__list {
  display: flex;
  flex-direction: column;
  gap: var(--cn-space-6);
  margin: var(--cn-space-6) 0 0;
}
.cn-comments__list:empty { display: none; }

/*
   A COMMENT IS A PICTURE AND A COLUMN, as the live thread draws it: the
   writer's headshot on the left, everything they wrote and can do on the
   right. 40px disc, 12 gap - the byline's own avatar scale.
*/
.cn-comment {
  display: flex;
  gap: var(--cn-space-3);
  align-items: flex-start;
  padding-bottom: var(--cn-space-5);
  border-bottom: 1px solid var(--cn-border);
}

.cn-comment__main { flex: 1 1 auto; min-width: 0; }

.cn-comment__avatar {
  position: relative;
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--cn-navy);
  color: #fff;
  font-size: var(--cn-text-label);
  font-weight: var(--cn-fw-semibold);
  letter-spacing: .02em;
}

/* The photo sits over the initials; if it never loads the initials stay. */
.cn-comment__headshot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cn-comment__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cn-space-1) var(--cn-space-3);
  align-items: baseline;
  margin: 0 0 var(--cn-space-2);
}

.cn-comment__author {
  font-size: var(--cn-text-meta);
  font-weight: var(--cn-fw-semibold);
  color: var(--cn-ink);
}

.cn-comment__time {
  font-size: var(--cn-text-meta);
  color: var(--cn-ink-faint);
}

.cn-comment__body {
  margin: 0;
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink-muted);
  overflow-wrap: anywhere;   /* a pasted URL cannot push the column wide */
}

.cn-comments__form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--cn-space-3);
}

.cn-comments__field {
  width: 100%;
  padding: var(--cn-space-4);
  border: 1px solid var(--cn-border);
  border-radius: var(--cn-radius-card);
  font: inherit;
  font-size: var(--cn-text-body);
  line-height: var(--cn-lh-body);
  color: var(--cn-ink);
  resize: vertical;
}

.cn-comments__field:focus-visible {
  outline: 2px solid var(--cn-cyan);
  outline-offset: 2px;
}

/* ----------------------------------------------- hero event overlay ----- */
/*
   The event an Event Q&A was recorded at, set over the hero image. p7 places
   it inside the image at the top left, in the same navy the artboard fills the
   image box with; p6 repeats it on the listing hero. A Q&A without a named
   event renders the image alone.
*/
.cn-reader__hero-media {
  position: relative;
  display: block;
}

.cn-reader__hero-event {
  position: absolute;
  top: var(--cn-space-4);
  left: var(--cn-space-4);
  max-width: calc(100% - var(--cn-space-8));
  padding: var(--cn-space-2) var(--cn-space-3);
  background: var(--cn-navy);
  color: var(--cn-ink-invert);
  font-size: var(--cn-text-meta);
  font-weight: var(--cn-fw-semibold);
  line-height: 1.25;
}


/* ------------------------------------------------ audio: author row ---- */
/*
   THE AUDIO READER TOP-ALIGNS ITS BYLINE, where the other readers centre it.

   Its authors carry a two-line credit - role on one line, company on the next
   - and three of them stacked, so a portrait centred against that pair sits
   low and the column of faces reads as ragged. Top-aligned, the portraits
   line up with the first line of each name.

   MOVED OUT OF THE PAGE. This was an inline <style> block at the top of
   audio.jsp, which CONNECT2026.md rules out, and it carried !important to win
   against a rule it could just as well have been written after. Scoped by
   body[data-page] so it still reaches only the audio reader - the other
   readers keep the centred byline they were measured with.
*/
body[data-page="audio"] .cn-reader__author { align-items: start; }


/* --------------------------------------------- audio: related block ---- */
/*
   Related audio moved out of the rail and into the content column, where the
   design draws it. It is the same headline rows the rail used, so it needs no
   type of its own - only the rule that separates it from the body above.
*/
.cn-related-audio {
  margin-top: var(--cn-space-12);
  padding-top: var(--cn-space-8);
  border-top: 1px solid var(--cn-border-mid);
}


/* ------------------------------------------------- comments: actions --- */
/*
   THE ROW UNDER A COMMENT - vote, reply, delete - and the replies beneath it.

   These arrived with the comment module reaching parity with the live reader,
   which has voting, replying, deleting and paging where this had only a list
   and a box. The classes above (.cn-comment__head, __author, __time, __body)
   are reused rather than renamed, so this is an addition and not a rewrite of
   what was already deployed.

   BUTTONS THAT READ AS TEXT. They sit under someone's words, four of them per
   comment, and four pills there would out-shout the comment itself. Quiet
   until hovered, which is what the live thread does with its icon row.
*/
.cn-comment__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cn-space-4);
  margin-top: var(--cn-space-2);
}

.cn-comment__action {
  padding: 0;
  font: inherit;
  font-size: var(--cn-text-label);
  color: var(--cn-ink-faint);
  background: none;
  border: 0;
  cursor: pointer;
}

.cn-comment__action:hover,
.cn-comment__action:focus-visible { color: var(--cn-cyan); }

.cn-comment__action:disabled { opacity: .5; cursor: default; }

/* Deleting is the one irreversible thing in the row, so it is the one thing
   that does not look like the others when you reach for it. */
.cn-comment__action--danger:hover,
.cn-comment__action--danger:focus-visible { color: var(--cn-fam-orange); }

/* Tabular figures so a count going 9 -> 10 does not shift the row beside it. */
.cn-comment__vote { font-variant-numeric: tabular-nums; }

/* The thumb you pressed - live fills its icon; here the figure turns cyan
   and holds it, so a reader can see their own vote on a return visit. */
.cn-comment__vote[aria-pressed="true"] {
  color: var(--cn-cyan);
  font-weight: var(--cn-fw-semibold);
}

/* Sits on its own line under the actions, because it is about the thread
   below rather than about the comment above. */
.cn-comment__action--replies {
  display: block;
  margin-top: var(--cn-space-2);
  font-weight: var(--cn-fw-semibold);
  color: var(--cn-cyan);
}


/* ------------------------------------------------- comments: replies --- */
/*
   ONE LEVEL DEEP, like the live thread: a reply carries no reply button, so
   the indent below can never compound into a staircase.
*/
.cn-comment__replies {
  margin: var(--cn-space-4) 0 0;
  padding: 0;
}

.cn-comment__replies:empty { display: none; }

.cn-comment--reply {
  margin-top: var(--cn-space-4);
  padding-bottom: 0;
  padding-left: var(--cn-space-4);
  /* A rule rather than a border box - it marks the relationship without
     drawing a second card inside the first. */
  border-bottom: 0;
  border-left: 2px solid var(--cn-border);
}

/* A reply's picture is a size down, so the thread reads as one level in. */
.cn-comment--reply .cn-comment__avatar { width: 32px; height: 32px; font-size: 11px; }

.cn-comments__form--reply {
  margin-top: var(--cn-space-4);
  gap: var(--cn-space-3);
}

/* Reply and Cancel share a row under the reply box. */
.cn-comments__form--reply {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
}
.cn-comments__form--reply .cn-comments__field { flex: 1 0 100%; }


/* ---------------------------------------------------- comments: more --- */
/* Hidden by the tag until the first response says there IS another page, so
   it never offers one that is not there. */
.cn-comments__more {
  margin-top: var(--cn-space-6);
  text-align: center;
}
.cn-comments__more:has(> [hidden]) { display: none; }

/* ------------------------------------------------ comments: phones ----- */
@media (max-width: 767px) {
  .cn-comments { margin-top: var(--cn-space-8); }
  .cn-comment { gap: var(--cn-space-2); }
  .cn-comment__avatar { width: 32px; height: 32px; font-size: 11px; }
  .cn-comment--reply .cn-comment__avatar { width: 28px; height: 28px; font-size: 10px; }
  .cn-comment--reply { padding-left: var(--cn-space-3); }
  /* Post comment and the reply row's buttons fill the column so a thumb
     can find them; the note keeps its gap. */
  .cn-comments__form .cn-btn { width: 100%; }
  .cn-comments__more .cn-btn { width: 100%; }
  .cn-comment__actions { gap: var(--cn-space-3) var(--cn-space-4); }
}

/* A comment of yours the moderators have not passed yet - live's "Approval
   Pending". Everyone else cannot see it, so the writer is told why. */
.cn-comment__pending {
  margin-left: var(--cn-space-2);
  padding: 1px var(--cn-space-2);
  border-radius: var(--cn-radius-pill, 999px);
  font-size: var(--cn-text-label);
  font-weight: var(--cn-fw-semibold);
  color: var(--cn-ink);
  background: var(--cn-band-grey);
}
